Skip to content

Commit

Permalink
Ensure note popups don't clobber each other.
Browse files Browse the repository at this point in the history
Fail fast if someone sets up multiple note popups with conflicting
settings. This will allow us to obey the principle of least surprise.
  • Loading branch information
Hoikas committed Feb 18, 2024
1 parent 3c5e2d6 commit 20ccfa8
Show file tree
Hide file tree
Showing 9 changed files with 25 additions and 11 deletions.
6 changes: 4 additions & 2 deletions korman/exporter/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def _check_sanity(self):
for mod in bl_obj.plasma_modifiers.modifiers:
fn = getattr(mod, "sanity_check", None)
if fn is not None:
fn()
fn(self)
inc_progress()
self.report.msg("... Age is grinning and holding a spatula. Must be OK, then.")

Expand Down Expand Up @@ -502,7 +502,9 @@ def _(temporary, parent):
# Wow, recursively generated objects. Aren't you special?
with indent():
for mod in temporary.plasma_modifiers.modifiers:
mod.sanity_check()
fn = getattr(mod, "sanity_check", None)
if fn is not None:
fn(self)
do_pre_export(temporary)
return temporary

Expand Down
8 changes: 8 additions & 0 deletions korman/exporter/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ class GuiConverter:

if TYPE_CHECKING:
_parent: weakref.ref[Exporter] = ...
_pages: Dict[str, Any] = ...
_mods_exported: Set[str] = ...

def __init__(self, parent: Optional[Exporter] = None):
self._parent = weakref.ref(parent) if parent is not None else None
self._pages = {}
self._mods_exported = set()

# Go ahead and prepare the GUI transparent material for future use.
Expand Down Expand Up @@ -206,6 +208,12 @@ def convert_post_effect_matrices(self, camera_matrix: mathutils.Matrix) -> PostE
w2c[2, i] *= -1.0
return PostEffectModMatrices(c2w, w2c)

def check_pre_export(self, name: str, **kwargs):
previous = self._pages.setdefault(name, kwargs)
if previous != kwargs:
diff = set(previous.items()) - set(kwargs.items())
raise ExportError(f"GUI Page '{name}' has target modifiers with conflicting settings:\n{diff}")

def create_note_gui(self, gui_page: str, gui_camera: bpy.types.Object):
if not gui_page in self._mods_exported:
guidialog_object = utils.create_empty_object(f"{gui_page}_NoteDialog")
Expand Down
2 changes: 1 addition & 1 deletion korman/properties/modifiers/anim.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def blender_action(self):
return None
raise ExportError("'{}': Object has an animation modifier but is not animated".format(bo.name))

def sanity_check(self) -> None:
def sanity_check(self, exporter) -> None:
if not self.id_data.plasma_object.has_animation_data:
raise ExportError("'{}': Has an animation modifier but no animation data.", self.id_data.name)

Expand Down
2 changes: 1 addition & 1 deletion korman/properties/modifiers/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def requires_actor(self):
# This should be an empty, really...
return True

def sanity_check(self):
def sanity_check(self, exporter):
# The user absolutely MUST specify a clickable or this won't export worth crap.
if self.clickable_object is None:
raise ExportError("'{}': Sitting Behavior's clickable object is invalid".format(self.key_name))
2 changes: 1 addition & 1 deletion korman/properties/modifiers/game_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def is_game_gui_control(cls) -> bool:
def requires_dyntext(self) -> bool:
return False

def sanity_check(self):
def sanity_check(self, exporter):
age: PlasmaAge = bpy.context.scene.world.plasma_age

# Game GUI modifiers must be attached to objects in a GUI page, ONLY
Expand Down
8 changes: 6 additions & 2 deletions korman/properties/modifiers/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ def _create_moul_nodes(self, clickable_object, nodes, linkingnode, age_name):
share.link_input(share_anim_stage, "stage", "stage_refs")
share.link_output(linkingnode, "hosts", "shareBookSeek")

def sanity_check(self):
def sanity_check(self, exporter):
if self.clickable is None:
raise ExportError("{}: Linking Book modifier requires a clickable!", self.id_data.name)
if self.seek_point is None:
Expand Down Expand Up @@ -724,11 +724,15 @@ def clickable_object(self) -> Optional[bpy.types.Object]:
if self.id_data.type == "MESH":
return self.id_data

def sanity_check(self):
def sanity_check(self, exporter: Exporter):
page_type = helpers.get_page_type(self.id_data.plasma_object.page)
if page_type != "room":
raise ExportError(f"Note Popup modifiers should be in a 'room' page, not a '{page_type}' page!")

# It's OK if multiple note popups point to the same GUI page,
# they just need to have the same camera.
exporter.gui.check_pre_export(self.gui_page, pl_id="note_popup", camera=self.gui_camera)

def pre_export(self, exporter: Exporter, bo: bpy.types.Object):
# The GUI converter will debounce duplicate GUI dialogs.
yield from exporter.gui.create_note_gui(self.gui_page, self.gui_camera)
Expand Down
2 changes: 1 addition & 1 deletion korman/properties/modifiers/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz):
type=bpy.types.Object,
poll=idprops.poll_camera_objects)

def sanity_check(self):
def sanity_check(self, exporter):
if self.camera_object is None:
raise ExportError(f"'{self.id_data.name}': Telescopes must specify a camera!")

Expand Down
4 changes: 2 additions & 2 deletions korman/properties/modifiers/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def iter_dependencies(self):
for i in (j.blend_onto for j in self.dependencies if j.blend_onto is not None and j.enabled):
yield i

def sanity_check(self):
def sanity_check(self, exporter):
if self.has_circular_dependency:
raise ExportError("'{}': Circular Render Dependency detected!".format(self.id_data.name))

Expand Down Expand Up @@ -770,7 +770,7 @@ def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_col
def localization_set(self):
return "DynaTexts"

def sanity_check(self):
def sanity_check(self, exporter):
if self.texture is None:
raise ExportError("'{}': Localized Text modifier requires a texture", self.id_data.name)

Expand Down
2 changes: 1 addition & 1 deletion korman/properties/modifiers/sound.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
stereize_left = PointerProperty(type=bpy.types.Object, options={"HIDDEN", "SKIP_SAVE"})
stereize_right = PointerProperty(type=bpy.types.Object, options={"HIDDEN", "SKIP_SAVE"})

def sanity_check(self):
def sanity_check(self, exporter):
modifiers = self.id_data.plasma_modifiers

# Sound emitters can potentially export sounds to more than one emitter SceneObject. Currently,
Expand Down

0 comments on commit 20ccfa8

Please sign in to comment.