Skip to content

Commit 20ccfa8

Browse files
committed
Ensure note popups don't clobber each other.
Fail fast if someone sets up multiple note popups with conflicting settings. This will allow us to obey the principle of least surprise.
1 parent 3c5e2d6 commit 20ccfa8

File tree

9 files changed

+25
-11
lines changed

9 files changed

+25
-11
lines changed

korman/exporter/convert.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def _check_sanity(self):
229229
for mod in bl_obj.plasma_modifiers.modifiers:
230230
fn = getattr(mod, "sanity_check", None)
231231
if fn is not None:
232-
fn()
232+
fn(self)
233233
inc_progress()
234234
self.report.msg("... Age is grinning and holding a spatula. Must be OK, then.")
235235

@@ -502,7 +502,9 @@ def _(temporary, parent):
502502
# Wow, recursively generated objects. Aren't you special?
503503
with indent():
504504
for mod in temporary.plasma_modifiers.modifiers:
505-
mod.sanity_check()
505+
fn = getattr(mod, "sanity_check", None)
506+
if fn is not None:
507+
fn(self)
506508
do_pre_export(temporary)
507509
return temporary
508510

korman/exporter/gui.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ class GuiConverter:
4949

5050
if TYPE_CHECKING:
5151
_parent: weakref.ref[Exporter] = ...
52+
_pages: Dict[str, Any] = ...
5253
_mods_exported: Set[str] = ...
5354

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

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

211+
def check_pre_export(self, name: str, **kwargs):
212+
previous = self._pages.setdefault(name, kwargs)
213+
if previous != kwargs:
214+
diff = set(previous.items()) - set(kwargs.items())
215+
raise ExportError(f"GUI Page '{name}' has target modifiers with conflicting settings:\n{diff}")
216+
209217
def create_note_gui(self, gui_page: str, gui_camera: bpy.types.Object):
210218
if not gui_page in self._mods_exported:
211219
guidialog_object = utils.create_empty_object(f"{gui_page}_NoteDialog")

korman/properties/modifiers/anim.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def blender_action(self):
4242
return None
4343
raise ExportError("'{}': Object has an animation modifier but is not animated".format(bo.name))
4444

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

korman/properties/modifiers/avatar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def requires_actor(self):
189189
# This should be an empty, really...
190190
return True
191191

192-
def sanity_check(self):
192+
def sanity_check(self, exporter):
193193
# The user absolutely MUST specify a clickable or this won't export worth crap.
194194
if self.clickable_object is None:
195195
raise ExportError("'{}': Sitting Behavior's clickable object is invalid".format(self.key_name))

korman/properties/modifiers/game_gui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def is_game_gui_control(cls) -> bool:
7070
def requires_dyntext(self) -> bool:
7171
return False
7272

73-
def sanity_check(self):
73+
def sanity_check(self, exporter):
7474
age: PlasmaAge = bpy.context.scene.world.plasma_age
7575

7676
# Game GUI modifiers must be attached to objects in a GUI page, ONLY

korman/properties/modifiers/gui.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ def _create_moul_nodes(self, clickable_object, nodes, linkingnode, age_name):
660660
share.link_input(share_anim_stage, "stage", "stage_refs")
661661
share.link_output(linkingnode, "hosts", "shareBookSeek")
662662

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

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

732+
# It's OK if multiple note popups point to the same GUI page,
733+
# they just need to have the same camera.
734+
exporter.gui.check_pre_export(self.gui_page, pl_id="note_popup", camera=self.gui_camera)
735+
732736
def pre_export(self, exporter: Exporter, bo: bpy.types.Object):
733737
# The GUI converter will debounce duplicate GUI dialogs.
734738
yield from exporter.gui.create_note_gui(self.gui_page, self.gui_camera)

korman/properties/modifiers/logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz):
154154
type=bpy.types.Object,
155155
poll=idprops.poll_camera_objects)
156156

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

korman/properties/modifiers/render.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def iter_dependencies(self):
120120
for i in (j.blend_onto for j in self.dependencies if j.blend_onto is not None and j.enabled):
121121
yield i
122122

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

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

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

korman/properties/modifiers/sound.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
533533
stereize_left = PointerProperty(type=bpy.types.Object, options={"HIDDEN", "SKIP_SAVE"})
534534
stereize_right = PointerProperty(type=bpy.types.Object, options={"HIDDEN", "SKIP_SAVE"})
535535

536-
def sanity_check(self):
536+
def sanity_check(self, exporter):
537537
modifiers = self.id_data.plasma_modifiers
538538

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

0 commit comments

Comments
 (0)