From 7bde98e37c28158ec402c7b0cc083105204a7894 Mon Sep 17 00:00:00 2001 From: Joseph Davies Date: Sun, 14 Feb 2016 06:52:28 -0800 Subject: [PATCH] Add MultiStageBehMod Node. --- korman/nodes/node_avatar.py | 382 +++++++++++++++++++++++++++++++++++- korman/nodes/node_python.py | 1 + 2 files changed, 382 insertions(+), 1 deletion(-) diff --git a/korman/nodes/node_avatar.py b/korman/nodes/node_avatar.py index d9a6ec0..dfdd4a6 100644 --- a/korman/nodes/node_avatar.py +++ b/korman/nodes/node_avatar.py @@ -20,12 +20,13 @@ from PyHSPlasma import * from .node_core import PlasmaNodeBase, PlasmaNodeSocketBase from ..properties.modifiers.avatar import sitting_approach_flags +from ..exporter.explosions import ExportError class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node): bl_category = "AVATAR" bl_idname = "PlasmaSittingBehaviorNode" bl_label = "Sitting Behavior" - bl_default_width = 100 + bl_width_default = 120 approach = EnumProperty(name="Approach", description="Directions an avatar can approach the seat from", @@ -71,3 +72,382 @@ class PlasmaSittingBehaviorNode(PlasmaNodeBase, bpy.types.Node): @property def requires_actor(self): return True + + +class PlasmaAnimStageAdvanceSocketIn(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.412, 0.2, 0.055, 1.0) + + auto_advance = BoolProperty(name="Advance to Next Stage", + description="Automatically advance to the next stage when the animation completes instead of halting", + default=True) + + def draw_content(self, context, layout, node, text): + if not self.is_linked: + layout.prop(self, "auto_advance") + else: + if self.links[0].from_node.stage_id is not None: + layout.label("{} {}".format(text, self.links[0].from_node.stage_id)) + else: + layout.label(text) + + +class PlasmaAnimStageRegressSocketIn(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.412, 0.2, 0.055, 1.0) + + auto_regress = BoolProperty(name="Regress to Previous Stage", + description="Automatically regress to the previous stage when the animation completes instead of halting", + default=True) + + def draw_content(self, context, layout, node, text): + if not self.is_linked: + layout.prop(self, "auto_regress") + else: + if self.links[0].from_node.stage_id is not None: + layout.label("{} {}".format(text, self.links[0].from_node.stage_id)) + else: + layout.label(text) + + +class PlasmaAnimStageOrderSocketOut(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.412, 0.2, 0.055, 1.0) + + +anim_play_flags = [("kPlayNone", "None", ""), + ("kPlayKey", "Keyboard", ""), + ("kPlayAuto", "Automatic", "")] +anim_stage_flags = [("kAdvanceNone", "None", ""), + ("kAdvanceOnMove", "Movement", ""), + ("kAdvanceAuto", "Automatic", ""), + ("kAdvanceOnAnyKey", "Any Keypress", "")] + + +class PlasmaAnimStageSettingsSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.412, 0.0, 0.055, 1.0) + + +class PlasmaAnimStageSettingsNode(PlasmaNodeBase, bpy.types.Node): + bl_category = "AVATAR" + bl_idname = "PlasmaAnimStageSettingsNode" + bl_label = "Animation Stage Settings" + bl_width_default = 325 + + forward = EnumProperty(name="Forward", + description="", + items=anim_play_flags, + default="kPlayNone") + backward = EnumProperty(name="Backward", + description="", + items=anim_play_flags, + default="kPlayNone") + stage_advance = EnumProperty(name="Stage Advance", + description="", + items=anim_stage_flags, + default="kAdvanceNone") + stage_regress = EnumProperty(name="Stage Regress", + description="", + items=anim_stage_flags, + default="kAdvanceNone") + + notify_on = EnumProperty(name="Notify", + description="Which events should send notifications", + items=[ + ("kNotifyEnter", "Enter", + "Send notification when animation first begins to play"), + ("kNotifyLoop", "Loop", + "Send notification when animation starts a loop"), + ("kNotifyAdvance", "Advance", + "Send notification when animation is advanced"), + ("kNotifyRegress", "Regress", + "Send notification when animation is regressed") + ], + default={"kNotifyEnter"}, + options={"ENUM_FLAG"}) + + input_sockets = OrderedDict([ + ("advance_to", { + "text": "Advance to Stage", + "type": "PlasmaAnimStageAdvanceSocketIn", + "valid_link_nodes": "PlasmaAnimStageNode", + "valid_link_sockets": "PlasmaAnimStageOrderSocketOut", + "link_limit": 1, + }), + ("regress_to", { + "text": "Regress to Stage", + "type": "PlasmaAnimStageRegressSocketIn", + "valid_link_nodes": "PlasmaAnimStageNode", + "valid_link_sockets": "PlasmaAnimStageOrderSocketOut", + "link_limit": 1, + }), + ]) + + output_sockets = OrderedDict([ + ("stage", { + "text": "Stage", + "type": "PlasmaAnimStageSettingsSocket", + "valid_link_nodes": "PlasmaAnimStageNode", + "valid_link_sockets": "PlasmaAnimStageSettingsSocket", + }), + ]) + + def draw_buttons(self, context, layout): + layout.prop(self, "forward") + layout.prop(self, "backward") + layout.prop(self, "stage_advance") + layout.prop(self, "stage_regress") + layout.separator() + + layout.label("Notify On:") + row = layout.row() + row.prop(self, "notify_on") + layout.separator() + + +class PlasmaAnimStageNode(PlasmaNodeBase, bpy.types.Node): + bl_category = "AVATAR" + bl_idname = "PlasmaAnimStageNode" + bl_label = "Animation Stage" + bl_width_default = 325 + + pl_attrib = ("ptAttribAnimation") + + anim_name = StringProperty(name="Animation Name", + description="Name of animation to play") + + loop_option = EnumProperty(name="Looping", + description="Loop options for animation playback", + items=[("kDontLoop", "Don't Loop", "Don't loop the animation"), + ("kLoop", "Loop", "Loop the animation a finite number of times"), + ("kLoopForever", "Loop Forever", "Continue playing animation indefinitely")], + default="kDontLoop") + num_loops = IntProperty(name="Num Loops", + description="Number of times to loop animation", + default=0) + + input_sockets = OrderedDict([ + ("stage_settings", { + "text": "Stage Settings", + "type": "PlasmaAnimStageSettingsSocket", + "valid_link_nodes": "PlasmaAnimStageSettingsNode", + "valid_link_sockets": "PlasmaAnimStageSettingsSocket", + "link_limit": 1, + }), + ]) + + output_sockets = OrderedDict([ + ("stage", { + "text": "Behavior", + "type": "PlasmaAnimStageRefSocket", + "valid_link_nodes": "PlasmaMultiStageBehaviorNode", + "valid_link_sockets": "PlasmaAnimStageRefSocket", + }), + ("stage_reference", { + "text": "Stage Progression", + "type": "PlasmaAnimStageOrderSocketOut", + "valid_link_nodes": "PlasmaAnimStageSettingsNode", + "valid_link_sockets": {"PlasmaAnimStageAdvanceSocketIn", "PlasmaAnimStageRegressSocketIn"} , + }), + ]) + + def draw_buttons(self, context, layout): + layout.prop(self, "anim_name") + + row = layout.row() + row.prop(self, "loop_option") + if self.loop_option == "kLoop": + row = layout.row() + row.prop(self, "num_loops") + + @property + def stage_id(self): + idx = None + stage_socket = self.find_output_socket("stage") + if stage_socket.is_linked: + msbmod = stage_socket.links[0].to_node + idx = next((idx for idx, socket in enumerate(msbmod.find_input_sockets("stage_refs")) if socket.is_linked and socket.links[0].from_node == self)) + return idx + + +class PlasmaBehaviorSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.348, 0.186, 0.349, 1.0) + + +class PlasmaMultiStageBehaviorNode(PlasmaNodeBase, bpy.types.Node): + bl_category = "AVATAR" + bl_idname = "PlasmaMultiStageBehaviorNode" + bl_label = "Multistage Behavior" + bl_width_default = 200 + + pl_attrib = ("ptAttribBehavior") + + freeze_phys = BoolProperty(name="Freeze Physical", + description="Freeze physical at end", + default=False) + reverse_control = BoolProperty(name="Reverse Controls", + description="Reverse forward/back controls at end", + default=False) + + input_sockets = OrderedDict([ + ("seek_target", { + "text": "Seek Target", + "type": "PlasmaSeekTargetSocketIn", + "valid_link_sockets": "PlasmaSeekTargetSocketOut", + }), + ("stage_refs", { + "text": "Stage", + "type": "PlasmaAnimStageRefSocket", + "valid_link_nodes": "PlasmaAnimStageNode", + "valid_link_sockets": "PlasmaAnimStageRefSocket", + "link_limit": 1, + "spawn_empty": True, + }), + ]) + + output_sockets = OrderedDict([ + ("hosts", { + "text": "Host Script", + "type": "PlasmaBehaviorSocket", + "valid_link_nodes": {"PlasmaPythonFileNode"}, + "spawn_empty": True, + }) + ]) + + def draw_buttons(self, context, layout): + layout.prop(self, "freeze_phys") + layout.prop(self, "reverse_control") + + def get_key(self, exporter, so): + seek_socket = self.find_input_socket("seek_target") + + if seek_socket.is_linked: + seek_target = seek_socket.links[0].from_node.target + if seek_target is not None: + seek_object = exporter.mgr.find_create_object(plSceneObject, bl=seek_target) + else: + raise ExportError("'{}': MultiStage Behavior's seek point object is invalid".format(self._find_create_key(plMultistageBehMod, exporter, so=so).name)) + else: + seek_object = so + + return self._find_create_key(plMultistageBehMod, exporter, so=seek_object) + + def export(self, exporter, bo, so): + seek_socket = self.find_input_socket("seek_target") + msbmod = self.get_key(exporter, so).object + + msbmod.smartSeek = True if seek_socket.is_linked or seek_socket.auto_target else False + msbmod.freezePhys = self.freeze_phys + msbmod.reverseFBControlsOnRelease = self.reverse_control + + for stage in self.find_inputs("stage_refs"): + animstage = plAnimStage() + animstage.animName = stage.anim_name + if stage.loop_option == "kLoopForever": + animstage.loops = -1 + elif stage.loop_option == "kLoop": + animstage.loops = stage.num_loops + + # Harvest additional AnimStage Settings, if available + settings = stage.find_input("stage_settings") + if settings: + animstage.forwardType = getattr(plAnimStage, settings.forward) + animstage.backType =getattr(plAnimStage, settings.backward) + animstage.advanceType = getattr(plAnimStage, settings.stage_advance) + animstage.regressType = getattr(plAnimStage, settings.stage_regress) + for flag in settings.notify_on: + animstage.notify |= getattr(plAnimStage, flag) + + advance_to = settings.find_input_socket("advance_to") + if advance_to.is_linked: + # Auto-Advance to specific stage + animstage.advanceTo = advance_to.links[0].from_node.stage_id + elif advance_to.auto_advance: + # Auto-Advance + animstage.advanceTo = None + else: + # Don't Auto-Advance, just stop! + animstage.advanceTo = -1 + + regress_to = settings.find_input_socket("regress_to") + if regress_to.is_linked: + # Auto-Regress to specific stage + animstage.regressTo = regress_to.links[0].from_node.stage_id + elif regress_to.auto_regress: + # Auto-Regress + animstage.regressTo = None + else: + # Don't Auto-Regress, just stop! + animstage.regressTo = -1 + + msbmod.addStage(animstage) + + @property + def requires_actor(self): + return not self.find_input_socket("seek_target").is_linked + + @property + def export_once(self): + return self.find_input_socket("seek_target").is_linked + + def harvest_actors(self): + seek_socket = self.find_input_socket("seek_target") + if seek_socket.is_linked and seek_socket.links[0].from_node.target is not None: + yield seek_socket.links[0].from_node.target.name + + +class PlasmaAnimStageRefSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.188, 0.186, 0.349, 1.0) + + def draw_content(self, context, layout, node, text): + if isinstance(node, PlasmaMultiStageBehaviorNode): + try: + idx = next((idx for idx, socket in enumerate(node.find_input_sockets("stage_refs")) if socket == self)) + except StopIteration: + layout.label(text) + else: + layout.label("Stage (ID: {})".format(idx)) + else: + layout.label(text) + + +class PlasmaSeekTargetSocketIn(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.180, 0.350, 0.180, 1.0) + auto_target = BoolProperty(name="Auto Smart Seek", + description="Smart Seek causes the avatar to seek to the provided position before starting the behavior ('auto' will use the current object as the seek point)", + default=False) + + def draw_content(self, context, layout, node, text): + if not self.is_linked: + layout.prop(self, "auto_target") + else: + target = self.links[0].from_node.target + if target: + layout.label("Smart Seek Target: {}".format(target.name)) + else: + layout.label("Smart Seek Target") + + +class PlasmaSeekTargetSocketOut(PlasmaNodeSocketBase, bpy.types.NodeSocket): + bl_color = (0.180, 0.350, 0.180, 1.0) + + +class PlasmaSeekTargetNode(PlasmaNodeBase, bpy.types.Node): + bl_category = "AVATAR" + bl_idname = "PlasmaSeekTargetNode" + bl_label = "Seek Target" + bl_width_default = 200 + + target = PointerProperty(name="Position", + description="Object defining the Seek Point's position", + type=bpy.types.Object) + + output_sockets = OrderedDict([ + ("seekers", { + "text": "Seekers", + "type": "PlasmaSeekTargetSocketOut", + "valid_link_nodes": {"PlasmaMultiStageBehaviorNode", "PlasmaOneShotMsgNode"}, + "valid_link_sockets": {"PlasmaSeekTargetSocketIn"}, + }) + ]) + + def draw_buttons(self, context, layout): + col = layout.column() + col.prop(self, "target", icon="EMPTY_DATA") diff --git a/korman/nodes/node_python.py b/korman/nodes/node_python.py index f9a841b..67a93c6 100644 --- a/korman/nodes/node_python.py +++ b/korman/nodes/node_python.py @@ -869,6 +869,7 @@ class PlasmaAttribTextureNode(idprops.IDPropMixin, PlasmaAttribNodeBase, bpy.typ _attrib_colors = { "ptAttribActivator": (0.188, 0.086, 0.349, 1.0), "ptAttribActivatorList": (0.188, 0.086, 0.349, 1.0), + "ptAttribBehavior": (0.348, 0.186, 0.349, 1.0), "ptAttribBoolean": (0.71, 0.706, 0.655, 1.0), "ptAttribExcludeRegion": (0.031, 0.110, 0.290, 1.0), "ptAttribDropDownList": (0.475, 0.459, 0.494, 1.0),