diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index ccb8080..6eb4f17 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -19,12 +19,102 @@ from PyHSPlasma import * from .node_core import * from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids +from ..exporter import ExportError -class PlasmaMessageSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): +class PlasmaMessageSocketBase(PlasmaNodeSocketBase): bl_color = (0.004, 0.282, 0.349, 1.0) +class PlasmaMessageSocket(PlasmaMessageSocketBase, bpy.types.NodeSocket): + pass -class PlasmaFootstepSoundMsgNode(PlasmaNodeBase, bpy.types.Node): +class PlasmaMessageNode(PlasmaNodeBase): + @property + def has_callbacks(self): + """This message has callbacks that can be waited on by a Responder""" + return False + + +class PlasmaOneShotMsgNode(PlasmaMessageNode, bpy.types.Node): + bl_category = "MSG" + bl_idname = "PlasmaOneShotMsgNode" + bl_label = "One Shot" + bl_width_default = 210 + + pos = StringProperty(name="Position", + description="Object defining the OneShot position") + seek = EnumProperty(name="Seek", + description="How the avatar should approach the OneShot position", + items=[("SMART", "Smart Seek", "Let the engine figure out the best path"), + ("DUMB", "Seek", "Shuffle to the OneShot position"), + ("NONE", "Warp", "Warp the avatar to the OneShot position")], + default="SMART") + + animation = StringProperty(name="Animation", + description="Name of the animation the avatar should execute") + drivable = BoolProperty(name="Drivable", + description="Player retains control of the avatar during the OneShot", + default=False) + reversable = BoolProperty(name="Reversable", + description="Player can reverse the OneShot", + default=False) + + def init(self, context): + self.inputs.new("PlasmaOneShotMsgSocket", "Sender", "sender") + + def convert_message(self, exporter, tree, so, respKey, wait): + msg = plOneShotMsg() + msg.addReceiver(self.get_key(exporter, tree, so)) + cb = self.find_input_socket("sender") + if cb.marker: + msg.addCallback(cb.marker, respKey, wait) + return msg + + def draw_buttons(self, context, layout): + layout.prop(self, "animation", text="Anim") + row = layout.row() + row.prop(self, "drivable") + row.prop(self, "reversable") + layout.prop_search(self, "pos", bpy.data, "objects", icon="EMPTY_DATA") + layout.prop(self, "seek") + + def export(self, exporter, tree, bo, so): + oneshotmod = self.get_key(exporter, tree, so).object + oneshotmod.animName = self.animation + oneshotmod.drivable = self.drivable + oneshotmod.reversable = self.reversable + oneshotmod.smartSeek = self.seek == "SMART" + oneshotmod.noSeek = self.seek == "NONE" + oneshotmod.seekDuration = 1.0 + + def get_key(self, exporter, tree, so): + name = self.create_key_name(tree) + if self.pos: + bo = bpy.data.objects.get(self.pos, None) + if bo is None: + raise ExportError("Node '{}' in '{}' specifies an invalid Position Empty".format(self.name, tree.name)) + pos_so = exporter.mgr.find_create_object(plSceneObject, bl=bo) + return exporter.mgr.find_create_key(plOneShotMod, name=name, so=pos_so) + else: + return exporter.mgr.find_create_key(plOneShotMod, name=name, so=so) + + @property + def has_callbacks(self): + cb = self.find_input_socket("sender") + return bool(cb.marker) + + +class PlasmaOneShotMsgSocket(PlasmaMessageSocketBase, bpy.types.NodeSocket): + marker = StringProperty(name="Marker", + description="Name of the marker specifying the time to send a callback message") + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.prop(self, "marker") + else: + layout.label(text) + + +class PlasmaFootstepSoundMsgNode(PlasmaMessageNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaFootstepSoundMsgNode" bl_label = "Footstep Sound" @@ -40,7 +130,7 @@ class PlasmaFootstepSoundMsgNode(PlasmaNodeBase, bpy.types.Node): def draw_buttons(self, context, layout): layout.prop(self, "surface") - def convert_message(self, exporter): + def convert_message(self, exporter, tree, so, respKey, wait): msg = plArmatureEffectStateMsg() msg.surface = footstep_surface_ids[self.surface] return msg diff --git a/korman/nodes/node_responder.py b/korman/nodes/node_responder.py index 9f6e24b..7e6bb68 100644 --- a/korman/nodes/node_responder.py +++ b/korman/nodes/node_responder.py @@ -85,7 +85,7 @@ class PlasmaResponderNode(PlasmaNodeVariableInput, bpy.types.Node): # Convert the Responder states stateMgr = ResponderStateMgr(self, responder) for stateNode in self.find_outputs("states", "PlasmaResponderStateNode"): - stateNode.convert_state(exporter, stateMgr) + stateNode.convert_state(exporter, tree, so, stateMgr) stateMgr.save() @@ -111,7 +111,7 @@ class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node): # Now draw a prop layout.prop(self, "default_state") - def convert_state(self, exporter, stateMgr): + def convert_state(self, exporter, tree, so, stateMgr): idx, state = stateMgr.get_state(self) # No sanity checking here. Hopefully nothing crazy has happened in the UI. @@ -131,22 +131,15 @@ class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node): self.commands = [] self.waits = {} - def add_command(self, node): - cmd = type("ResponderCommand", (), {"msg": None, "waitOn": -1}) + def add_command(self, node, waitOn): + cmd = type("ResponderCommand", (), {"msg": None, "waitOn": waitOn}) self.commands.append((node, cmd)) return (len(self.commands) - 1, cmd) - def add_wait(self, parentCmd): - try: - idx = self.commands.index(parentCmd) - except ValueError: - # The parent command didn't export for some reason... Probably no message. - # So, wait on nothing! - return -1 - else: - wait = len(self.waits) - self.waits[wait] = idx - return idx + def add_wait(self, parentIdx): + wait = len(self.waits) + self.waits[wait] = parentIdx + return wait def save(self, state): for node, cmd in self.commands: @@ -161,7 +154,7 @@ class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node): for i in self.find_outputs("cmds", "PlasmaResponderCommandNode"): # slight optimization--commands attached to states can't wait on other commands # namely because it's impossible to wait on a command that doesn't exist... - i.convert_command(exporter, stateMgr.responder, commands, True) + i.convert_command(exporter, tree, so, stateMgr.responder, commands) commands.save(state) @@ -179,36 +172,31 @@ class PlasmaResponderCommandNode(PlasmaNodeBase, bpy.types.Node): self.outputs.new("PlasmaMessageSocket", "Message", "msg") self.outputs.new("PlasmaRespCommandSocket", "Trigger", "trigger") - def convert_command(self, exporter, responder, commandMgr, forceNoWait=False): + def convert_command(self, exporter, tree, so, responder, commandMgr, waitOn=-1): # If this command has no message, there is no need to export it... msgNode = self.find_output("msg") if msgNode is not None: - idx, command = commandMgr.add_command(self) + idx, command = commandMgr.add_command(self, waitOn) - # If the thingthatdoneit is another command, we need to register a wait. - # We could hack and assume the parent is idx-1, but that won't work if the parent has many - # child commands. Le whoops! - if not forceNoWait: - parentCmd = self.find_input("whodoneit", "PlasmaResponderCommandNode") - if parentCmd is not None: - command.waitOn = commandMgr.add_wait(parentCmd) + # If we have child commands, we need to make sure that we support chaining this message as a callback + # If not, we'll export our children and tell them to not actually wait on us. + haveChildren = self.find_output("trigger", "PlasmaResponderCommandNode") is not None + if haveChildren and msgNode.has_callbacks: + childWaitOn = commandMgr.add_wait(idx) + else: + childWaitOn = -1 # Finally, convert our message... - msg = msgNode.convert_message(exporter) + msg = msgNode.convert_message(exporter, tree, so, responder.key, childWaitOn) self._finalize_message(exporter, responder, msg) - # If we have child commands, we need to make sure that we support chaining this message as a callback - # If not, we'll export our children and tell them to not actually wait on us. - haveChildren = self.find_output("trigger", "PlasmaResponderCommandNode") is not None - if haveChildren: - nowait = not self._add_msg_callback(exporter, responder, msg) command.msg = msg else: - nowait = True + childWaitOn = -1 # Export any child commands for i in self.find_outputs("trigger", "PlasmaResponderCommandNode"): - i.convert_command(exporter, responder, commandMgr, nowait) + i.convert_command(exporter, tree, so, responder, commandMgr, childWaitOn) _bcast_flags = { plArmatureEffectStateMsg: (plMessage.kPropagateToModifiers | plMessage.kNetPropagate), @@ -223,12 +211,6 @@ class PlasmaResponderCommandNode(PlasmaNodeBase, bpy.types.Node): msg.BCastFlags = self._bcast_flags[_cls] msg.BCastFlags |= plMessage.kLocalPropagate - def _add_msg_callback(self, exporter, responder, msg): - """Prepares a given message to be a callback to the responder""" - # We do not support callback messages ATM - return False - class PlasmaRespCommandSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): bl_color = (0.451, 0.0, 0.263, 1.0) -