From 0f0d6e1086e53e8ecfd54c7a0f1d4c8e6b190058 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 23 Jun 2024 18:38:07 -0400 Subject: [PATCH] Add the SDL Show/Hide Modifier. This adds a modifier for the very common PythonFileMods xAgeSDLBoolShowHude and xAgeSDLIntShowHide. Right now, Korman doesn't try to parse SDL files, so the variable name and type have to be specified manually. The benefit to this, however, is the ability to easily wire up global SDL visibility type features. --- korman/exporter/convert.py | 4 ++ korman/operators/op_ui.py | 8 ++- korman/plasma_api.py | 16 ++++++ korman/properties/modifiers/logic.py | 86 +++++++++++++++++++++++++++- korman/ui/modifiers/logic.py | 37 ++++++++++++ 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index df8d3665..c12627bb 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -598,6 +598,10 @@ def age_name(self): else: return bpy.context.scene.world.plasma_age.age_name + @property + def age_sdl(self) -> bool: + return bpy.context.scene.world.plasma_age.age_sdl + @property def dat_only(self): return self._op.dat_only diff --git a/korman/operators/op_ui.py b/korman/operators/op_ui.py index ffa3b171..fa904361 100644 --- a/korman/operators/op_ui.py +++ b/korman/operators/op_ui.py @@ -77,11 +77,17 @@ class CollectionRemoveOperator(UIOperator, bpy.types.Operator): index_prop = StringProperty(name="Index Property", description="Name of the active element index property", options=set()) + manual_index = IntProperty(name="Manual Index", + description="Manual integer index to remove", + options=set()) def execute(self, context): props = getattr(context, self.context).path_resolve(self.group_path) collection = getattr(props, self.collection_prop) - index = getattr(props, self.index_prop) + if self.index_prop: + index = getattr(props, self.index_prop) + else: + index = self.manual_index if len(collection) > index: collection.remove(index) setattr(props, self.index_prop, index - 1) diff --git a/korman/plasma_api.py b/korman/plasma_api.py index 98a6182b..0d63a8ef 100644 --- a/korman/plasma_api.py +++ b/korman/plasma_api.py @@ -14,6 +14,22 @@ # along with Korman. If not, see . python_files = { + "xAgeSDLBoolShowHide.py": ( + { "id": 1, "type": "ptAttribString", "name": "sdlName" }, + { "id": 2, "type": "ptAttribBoolean", "name": "showOnTrue" }, + # --- CWE Only Below --- + { "id": 3, "type": "ptAttribBoolean", "name": "defaultValue" }, + { "id": 4, "type": "ptAttribBoolean", "name": "evalOnFirstUpdate "}, + ), + + "xAgeSDLIntShowHide.py": ( + { "id": 1, "type": "ptAttribString", "name": "stringVarName" }, + { "id": 2, "type": "ptAttribString", "name": "stringShowStates" }, + # --- CWE Only Below --- + { "id": 3, "type": "ptAttribInt", "name": "intDefault" }, + { "id": 4, "type": "ptAttribBoolean", "name": "boolFirstUpdate "}, + ), + # Provided by all variants of Uru and Myst V "xDialogToggle.py": ( { "id": 1, "type": "ptAttribActivator", "name": "Activate" }, diff --git a/korman/properties/modifiers/logic.py b/korman/properties/modifiers/logic.py index 80a266be..7ce85950 100644 --- a/korman/properties/modifiers/logic.py +++ b/korman/properties/modifiers/logic.py @@ -15,10 +15,8 @@ from __future__ import annotations -import bmesh import bpy from bpy.props import * -import mathutils from PyHSPlasma import * from typing import * @@ -28,6 +26,11 @@ from ...nodes.node_messages import * from ...nodes.node_responder import * +from typing import * + +if TYPE_CHECKING: + from ...exporter import Exporter + from ...addon_prefs import game_versions from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz from ...exporter import ExportError, utils @@ -191,6 +194,85 @@ def requires_actor(self): return True +class PlasmaSDLIntState(bpy.types.PropertyGroup): + value: int = IntProperty( + name="State Value", + description="The object is shown when the SDL variable is set to this value", + min=0, + soft_max=255, + options=set() + ) + + +class PlasmaSDLShowHide(PlasmaModifierProperties, PlasmaModifierLogicWiz): + pl_id = "sdl_showhide" + + bl_category = "Logic" + bl_label = "SDL Show/Hide" + bl_description = "Show/Hide an object based on an SDL Variable" + bl_object_types = {"MESH", "FONT"} + bl_icon = "VISIBLE_IPO_OFF" + + sdl_variable: str = StringProperty( + name="SDL Variable", + description="Name of the SDL variable that controls visibility", + options=set() + ) + variable_type: str = EnumProperty( + name="Type", + description="Data type of the SDL variable", + items=[ + ("bool", "Boolean", "A boolean, used to represent simple on/off for a single state"), + ("int", "Integer", "An integer, used to represent multiple state combinations"), + ], + options=set() + ) + + int_states = CollectionProperty(type=PlasmaSDLIntState) + bool_state: bool = BoolProperty( + name="Show When True", + description="If checked, show this object when the SDL Variable is TRUE. If not, hide it when TRUE.", + default=True, + options=set() + ) + + def created(self): + # Ensure at least one SDL int state is precreated for ease of use. + # REMEMBER: Blender's "sequences" don't do truthiness correctly... + if len(self.int_states) == 0: + self.int_states.add() + + def sanity_check(self, exporter: Exporter): + if not exporter.age_sdl: + raise ExportError(f"'{self.id_data.name}': Age Global SDL is required for the SDL Show/Hide modifier!") + if not self.sdl_variable.strip(): + raise ExportError(f"'{self.id_data.name}': A valid SDL variable is required for the SDL Show/Hide modifier!") + + def logicwiz(self, bo, tree): + if self.variable_type == "bool": + pfm_node = self._create_standard_python_file_node(tree, "xAgeSDLBoolShowHide.py") + self._create_python_attribute(pfm_node, "sdlName", value=self.sdl_variable) + self._create_python_attribute(pfm_node, "showOnTrue", value=self.bool_state) + elif self.variable_type == "int": + pfm_node = self._create_standard_python_file_node(tree, "xAgeSDLIntShowHide.py") + self._create_python_attribute(pfm_node, "stringVarName", value=self.sdl_variable) + self._create_python_attribute(pfm_node, "stringShowStates", value=",".join(self._states)) + else: + raise RuntimeError() + + @property + def key_name(self): + if self.variable_type == "bool": + return f"cPythBoolShowHide_{self.sdl_variable}_{self.bool_state:d}" + elif self.variable_type == "int": + return f"cPythIntShowHide_{self.sdl_variable}_{'-'.join(self._states)}" + + @property + def _states(self) -> Iterable[str]: + """Returns a sorted, deduplicated iterable of the integer (converted to strings) states we should be visible in.""" + return (str(i) for i in sorted(frozenset((i.value for i in self.int_states)))) + + class PlasmaTelescope(PlasmaModifierProperties, PlasmaModifierLogicWiz): pl_id="telescope" diff --git a/korman/ui/modifiers/logic.py b/korman/ui/modifiers/logic.py index a99b1696..f401ed58 100644 --- a/korman/ui/modifiers/logic.py +++ b/korman/ui/modifiers/logic.py @@ -60,6 +60,43 @@ def maintainersmarker(modifier, layout, context): layout.label(text="Positive Y is North, positive Z is up.") layout.prop(modifier, "calibration") +def sdl_showhide(modifier: PlasmaSDLShowHide, layout, context): + if not context.scene.world.plasma_age.age_sdl: + layout.label("This modifier requires Age Global SDL!", icon="ERROR") + return + + valid_variable = modifier.sdl_variable.strip() + layout.alert = not valid_variable + layout.prop(modifier, "sdl_variable") + if not valid_variable: + layout.label("A valid SDL variable is required!", icon="ERROR") + layout.alert = False + layout.prop(modifier, "variable_type") + layout.separator() + + def setup_collection_operator(op): + op.context = "object" + op.group_path = modifier.path_from_id() + op.collection_prop = "int_states" + op.index_prop = "" + + if modifier.variable_type == "bool": + layout.prop(modifier, "bool_state") + elif modifier.variable_type == "int": + layout.label("Show when SDL variable is:") + sub = layout.column_flow() + for i, state in enumerate(modifier.int_states): + row = sub.row(align=True) + row.prop(state, "value", text="Value") + op = row.operator("ui.plasma_collection_remove", icon="ZOOMOUT", text="") + setup_collection_operator(op) + op.manual_index = i + + op = layout.operator("ui.plasma_collection_add", icon="ZOOMIN", text="Add State Value") + setup_collection_operator(op) + else: + raise RuntimeError() + def telescope(modifier, layout, context): layout.prop(modifier, "clickable_region") layout.prop(modifier, "seek_target_object", icon="EMPTY_DATA")