diff --git a/korman/helpers.py b/korman/helpers.py index 1bc0151..6ae2bf2 100644 --- a/korman/helpers.py +++ b/korman/helpers.py @@ -51,9 +51,9 @@ class TemporaryObject: def ensure_power_of_two(value): return pow(2, math.floor(math.log(value, 2))) -def find_modifier(boname, modid): - """Given a Blender Object name, finds a given modifier and returns it or None""" - bo = bpy.data.objects.get(boname, None) + +def find_modifier(bo, modid): + """Given a Blender Object, finds a given modifier and returns it or None""" if bo is not None: # if they give us the wrong modid, it is a bug and an AttributeError return getattr(bo.plasma_modifiers, modid) diff --git a/korman/nodes/node_conditions.py b/korman/nodes/node_conditions.py index d4fec71..d44363d 100644 --- a/korman/nodes/node_conditions.py +++ b/korman/nodes/node_conditions.py @@ -21,8 +21,9 @@ from PyHSPlasma import * from .node_core import * from ..properties.modifiers.physics import bounds_types +from .. import idprops -class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): +class PlasmaClickableNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.Node): bl_category = "CONDITIONS" bl_idname = "PlasmaClickableNode" bl_label = "Clickable" @@ -31,8 +32,10 @@ class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): # These are the Python attributes we can fill in pl_attrib = {"ptAttribActivator", "ptAttribActivatorList", "ptAttribNamedActivator"} - clickable = StringProperty(name="Clickable", - description="Mesh that is clickable") + clickable_object = PointerProperty(name="Clickable", + description="Mesh object that is clickable", + type=bpy.types.Object, + poll=idprops.poll_mesh_objects) bounds = EnumProperty(name="Bounds", description="Clickable's bounds (NOTE: only used if your clickable is not a collider)", items=bounds_types, @@ -63,7 +66,7 @@ class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): ]) def draw_buttons(self, context, layout): - layout.prop_search(self, "clickable", bpy.data, "objects", icon="MESH_DATA") + layout.prop(self, "clickable_object", icon="MESH_DATA") layout.prop(self, "bounds") def export(self, exporter, parent_bo, parent_so): @@ -127,27 +130,31 @@ class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): # First: look up the clickable mesh. if it is not specified, then it's this BO. # We do this because we might be exporting from a BO that is not actually the clickable object. # Case: sitting modifier (exports from sit position empty) - if self.clickable: - clickable_bo = bpy.data.objects.get(self.clickable, None) - if clickable_bo is None: - self.raise_error("invalid Clickable object: '{}'".format(self.clickable)) - clickable_so = exporter.mgr.find_create_object(plSceneObject, bl=clickable_bo) - return (clickable_bo, clickable_so) + if self.clickable_object: + clickable_so = exporter.mgr.find_create_object(plSceneObject, bl=self.clickable_object) + return (self.clickable_object, clickable_so) else: return (None, parent_so) def harvest_actors(self): - return (self.clickable,) + if self.clickable_object: + return (self.clickable_object.name,) + @classmethod + def _idprop_mapping(cls): + return {"clickable_object": "clickable"} -class PlasmaClickableRegionNode(PlasmaNodeBase, bpy.types.Node): + +class PlasmaClickableRegionNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.Node): bl_category = "CONDITIONS" bl_idname = "PlasmaClickableRegionNode" bl_label = "Clickable Region Settings" bl_width_default = 200 - region = StringProperty(name="Region", - description="Object that defines the region mesh") + region_object = PointerProperty(name="Region", + description="Object that defines the region mesh", + type=bpy.types.Object, + poll=idprops.poll_mesh_objects) bounds = EnumProperty(name="Bounds", description="Physical object's bounds (NOTE: only used if your clickable is not a collider)", items=bounds_types, @@ -161,14 +168,14 @@ class PlasmaClickableRegionNode(PlasmaNodeBase, bpy.types.Node): ]) def draw_buttons(self, context, layout): - layout.prop_search(self, "region", bpy.data, "objects", icon="MESH_DATA") + layout.prop(self, "region_object", icon="MESH_DATA") layout.prop(self, "bounds") def convert_subcondition(self, exporter, parent_bo, parent_so, logicmod): # REMEMBER: parent_so doesn't have to be the actual region scene object... - region_bo = bpy.data.objects.get(self.region, None) + region_bo = self.region_object if region_bo is None: - self.raise_error("invalid Region object: '{}'".format(self.region)) + self.raise_error("invalid Region") region_so = exporter.mgr.find_create_key(plSceneObject, bl=region_bo).object # Try to figure out the appropriate bounds type for the region.... @@ -198,6 +205,10 @@ class PlasmaClickableRegionNode(PlasmaNodeBase, bpy.types.Node): objinbox_key.object.satisfied = True logicmod.addCondition(objinbox_key) + @classmethod + def _idprop_mapping(cls): + return {"region_object": "region"} + class PlasmaClickableRegionSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): bl_color = (0.412, 0.0, 0.055, 1.0) @@ -310,7 +321,7 @@ class PlasmaVolumeReportNode(PlasmaNodeBase, bpy.types.Node): row.prop(self, "threshold", text="") -class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): +class PlasmaVolumeSensorNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bpy.types.Node): bl_category = "CONDITIONS" bl_idname = "PlasmaVolumeSensorNode" bl_label = "Region Sensor" @@ -320,8 +331,10 @@ class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): pl_attrib = {"ptAttribActivator", "ptAttribActivatorList", "ptAttribNamedActivator"} # Region Mesh - region = StringProperty(name="Region", - description="Object that defines the region mesh") + region_object = PointerProperty(name="Region", + description="Object that defines the region mesh", + type=bpy.types.Object, + poll=idprops.poll_mesh_objects) bounds = EnumProperty(name="Bounds", description="Physical object's bounds", items=bounds_types) @@ -364,11 +377,13 @@ class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): layout.prop(self, "report_on") # Okay, if they changed the name of the ObData, that's THEIR problem... - layout.prop_search(self, "region", bpy.data, "objects", icon="MESH_DATA") + layout.prop(self, "region_object", icon="MESH_DATA") layout.prop(self, "bounds") def get_key(self, exporter, parent_so): bo = self.region_object + if bo is None: + self.raise_error("Region cannot be empty") so = exporter.mgr.find_create_object(plSceneObject, bl=bo) rgn_enter, rgn_exit = None, None @@ -393,6 +408,8 @@ class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): def export(self, exporter, bo, parent_so): # We need to ensure we export to the correct SO region_bo = self.region_object + if region_bo is None: + self.raise_error("Region cannot be empty") region_so = exporter.mgr.find_create_object(plSceneObject, bl=region_bo) interface = exporter.mgr.find_create_object(plInterfaceInfoModifier, name=self.key_name, so=region_so) @@ -457,12 +474,9 @@ class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): logicmod.addCondition(volKey) return logicKey - @property - def region_object(self): - phys_bo = bpy.data.objects.get(self.region, None) - if phys_bo is None: - self.raise_error("invalid Region object: '{}'".format(self.region)) - return phys_bo + @classmethod + def _idprop_mapping(cls): + return {"region_object": "region"} @property def report_enters(self): diff --git a/korman/properties/modifiers/avatar.py b/korman/properties/modifiers/avatar.py index 76527e3..81c0457 100644 --- a/korman/properties/modifiers/avatar.py +++ b/korman/properties/modifiers/avatar.py @@ -20,13 +20,14 @@ from PyHSPlasma import * from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz from ...exporter.explosions import ExportError from ...helpers import find_modifier +from ... import idprops sitting_approach_flags = [("kApproachFront", "Front", "Approach from the font"), ("kApproachLeft", "Left", "Approach from the left"), ("kApproachRight", "Right", "Approach from the right"), ("kApproachRear", "Rear", "Approach from the rear guard")] -class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): +class PlasmaSittingBehavior(idprops.IDPropObjectMixin, PlasmaModifierProperties, PlasmaModifierLogicWiz): pl_id = "sittingmod" bl_category = "Avatar" @@ -39,10 +40,14 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): default={"kApproachFront", "kApproachLeft", "kApproachRight"}, options={"ENUM_FLAG"}) - clickable_obj = StringProperty(name="Clickable", - description="Object that defines the clickable area") - region_obj = StringProperty(name="Region", - description="Object that defines the region mesh") + clickable_object = PointerProperty(name="Clickable", + description="Object that defines the clickable area", + type=bpy.types.Object, + poll=idprops.poll_mesh_objects) + region_object = PointerProperty(name="Region", + description="Object that defines the region mesh", + type=bpy.types.Object, + poll=idprops.poll_mesh_objects) facing_enabled = BoolProperty(name="Avatar Facing", description="The avatar must be facing the clickable's Y-axis", @@ -53,8 +58,7 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): def export(self, exporter, bo, so): # The user absolutely MUST specify a clickable or this won't export worth crap. - clickable_obj = bpy.data.objects.get(self.clickable_obj, None) - if clickable_obj is None: + if self.clickable_object is None: raise ExportError("'{}': Sitting Behavior's clickable object is invalid".format(self.key_name)) # Generate the logic nodes now @@ -65,7 +69,7 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): def harvest_actors(self): if self.facing_enabled: - return (self.clickable_obj,) + return (self.clickable_object.name,) return () def logicwiz(self, bo): @@ -81,16 +85,16 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): # Clickable clickable = nodes.new("PlasmaClickableNode") clickable.link_output(sittingmod, "satisfies", "condition") - clickable.clickable = self.clickable_obj - clickable.bounds = find_modifier(self.clickable_obj, "collision").bounds + clickable.clickable_object = self.clickable_object + clickable.bounds = find_modifier(self.clickable_object, "collision").bounds # Avatar Region (optional) - region_phys = find_modifier(self.region_obj, "collision") + region_phys = find_modifier(self.region_object, "collision") if region_phys is not None: region = nodes.new("PlasmaClickableRegionNode") region.link_output(clickable, "satisfies", "region") region.name = "ClickableAvRegion" - region.region = self.region_obj + region.region_object = self.region_object region.bounds = region_phys.bounds # Facing Target (optional) @@ -105,6 +109,11 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): # facing target conditional for us. isn't that nice? clickable.find_input_socket("facing").allow_simple = False + @classmethod + def _idprop_mapping(cls): + return {"clickable_object": "clickable_obj", + "region_object": "region_obj"} + @property def key_name(self): return "{}_SitBeh".format(self.id_data.name) diff --git a/korman/properties/modifiers/region.py b/korman/properties/modifiers/region.py index 8101cd9..fbc5e42 100644 --- a/korman/properties/modifiers/region.py +++ b/korman/properties/modifiers/region.py @@ -88,7 +88,7 @@ class PlasmaFootstepRegion(PlasmaModifierProperties, PlasmaModifierLogicWiz): # Region Sensor volsens = nodes.new("PlasmaVolumeSensorNode") volsens.name = "RegionSensor" - volsens.region = bo.name + volsens.region_object = bo volsens.bounds = self.bounds volsens.find_input_socket("enter").allow = True volsens.find_input_socket("exit").allow = True diff --git a/korman/ui/modifiers/avatar.py b/korman/ui/modifiers/avatar.py index 5e8d010..31cc508 100644 --- a/korman/ui/modifiers/avatar.py +++ b/korman/ui/modifiers/avatar.py @@ -21,14 +21,14 @@ def sittingmod(modifier, layout, context): layout.row().prop(modifier, "approach") col = layout.column() - col.prop_search(modifier, "clickable_obj", bpy.data, "objects", icon="MESH_DATA") - clickable = find_modifier(modifier.clickable_obj, "collision") + col.prop(modifier, "clickable_object", icon="MESH_DATA") + clickable = find_modifier(modifier.clickable_object, "collision") if clickable is not None: col.prop(clickable, "bounds") col = layout.column() - col.prop_search(modifier, "region_obj", bpy.data, "objects", icon="MESH_DATA") - region = find_modifier(modifier.region_obj, "collision") + col.prop(modifier, "region_object", icon="MESH_DATA") + region = find_modifier(modifier.region_object, "collision") if region is not None: col.prop(region, "bounds")