diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index df8d366..c12627b 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -598,6 +598,10 @@ class Exporter: 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 ffa3b17..fa90436 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 98a6182..0d63a8e 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 80a266b..7ce8595 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 @@ if TYPE_CHECKING: 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 @@ class PlasmaMaintainersMarker(PlasmaModifierProperties): 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 a99b169..f401ed5 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")