diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index af8b19a..2762c9c 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -34,17 +34,32 @@ class PlasmaMessageNode(PlasmaNodeBase): ("sender", { "text": "Sender", "type": "PlasmaMessageSocket", + "valid_link_sockets": "PlasmaMessageSocket", "spawn_empty": True, }), ]) @property def has_callbacks(self): - """This message has callbacks that can be waited on by a Responder""" + """This message does not have callbacks that can be waited on by a Responder""" return False -class PlasmaAnimCmdMsgNode(idprops.IDPropMixin, PlasmaMessageNode, bpy.types.Node): +class PlasmaMessageWithCallbacksNode(PlasmaMessageNode): + output_sockets = OrderedDict([ + ("msgs", { + "text": "Send On Completion", + "type": "PlasmaMessageSocket", + "valid_link_sockets": "PlasmaMessageSocket", + }), + ]) + + @property + def has_callbacks(self): + """This message has callbacks that can be waited on by a Responder""" + return True + +class PlasmaAnimCmdMsgNode(idprops.IDPropMixin, PlasmaMessageWithCallbacksNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaAnimCmdMsgNode" bl_label = "Animation Command" @@ -407,7 +422,7 @@ class PlasmaLinkToAgeMsg(PlasmaMessageNode, bpy.types.Node): layout.prop(self, "spawn_point") -class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.types.Node): +class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacksNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaOneShotMsgNode" bl_label = "One Shot" @@ -426,7 +441,7 @@ class PlasmaOneShotMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.typ animation = StringProperty(name="Animation", description="Name of the animation the avatar should execute") marker = StringProperty(name="Marker", - description="Name of the marker specifying when to notify the Responder") + description="Name of the marker specifying when to notify the Responder") drivable = BoolProperty(name="Drivable", description="Player retains control of the avatar during the OneShot", default=False) @@ -523,7 +538,7 @@ class PlasmaSceneObjectMsgRcvrNode(idprops.IDPropObjectMixin, PlasmaNodeBase, bp return {"target_object": "object_name"} -class PlasmaSoundMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.types.Node): +class PlasmaSoundMsgNode(idprops.IDPropObjectMixin, PlasmaMessageWithCallbacksNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaSoundMsgNode" bl_label = "Sound" @@ -646,16 +661,12 @@ class PlasmaSoundMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.types layout.prop(self, "looping") layout.prop(self, "volume") - @property - def has_callbacks(self): - return True - @classmethod def _idprop_mapping(cls): return {"emitter_object": "object_name"} -class PlasmaTimerCallbackMsgNode(PlasmaMessageNode, bpy.types.Node): +class PlasmaTimerCallbackMsgNode(PlasmaMessageWithCallbacksNode, bpy.types.Node): bl_category = "MSG" bl_idname = "PlasmaTimerCallbackMsgNode" bl_label = "Timed Callback" @@ -677,10 +688,6 @@ class PlasmaTimerCallbackMsgNode(PlasmaMessageNode, bpy.types.Node): msg.time = self.delay return msg - @property - def has_callbacks(self): - return True - class PlasmaFootstepSoundMsgNode(PlasmaMessageNode, bpy.types.Node): bl_category = "MSG" diff --git a/korman/nodes/node_responder.py b/korman/nodes/node_responder.py index 166e242..646a6c8 100644 --- a/korman/nodes/node_responder.py +++ b/korman/nodes/node_responder.py @@ -126,9 +126,10 @@ class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node): ]) output_sockets = OrderedDict([ - ("cmds", { - "text": "Commands", - "type": "PlasmaRespCommandSocket", + ("msgs", { + "text": "Send Message", + "type": "PlasmaMessageSocket", + "valid_link_sockets": "PlasmaMessageSocket", }), ("gotostate", { "link_limit": 1, @@ -182,12 +183,44 @@ class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node): # Convert the commands commands = CommandMgr() - for i in self.find_outputs("cmds", "PlasmaResponderCommandNode"): + for i in self.find_outputs("msgs"): # 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, so, stateMgr.responder, commands) + self._generate_command(exporter, so, stateMgr.responder, commands, i) commands.save(state) + def _generate_command(self, exporter, so, responder, commandMgr, msgNode, waitOn=-1): + def prepare_message(exporter, so, responder, commandMgr, waitOn, msg): + idx, command = commandMgr.add_command(self, waitOn) + if msg.sender is None: + msg.sender = responder.key + msg.BCastFlags |= plMessage.kLocalPropagate + command.msg = msg + return (idx, command) + + # HACK: Some message nodes may need to sneakily send multiple messages. So, convert_message + # is therefore now a generator. We will ASSume that the first message generated is the + # primary msg that we should use for callbacks, if applicable + if inspect.isgeneratorfunction(msgNode.convert_message): + messages = tuple(msgNode.convert_message(exporter, so)) + msg = messages[0] + for i in messages[1:]: + prepare_message(exporter, so, responder, commandMgr, waitOn, i) + else: + msg = msgNode.convert_message(exporter, so) + idx, command = prepare_message(exporter, so, responder, commandMgr, waitOn, msg) + + # If the callback message node is not properly set up for event callbacks, we don't want to + if msgNode.has_callbacks and msgNode.find_output("msgs"): + childWaitOn = commandMgr.add_wait(idx) + msgNode.convert_callback_message(exporter, so, msg, responder.key, childWaitOn) + else: + childWaitOn = waitOn + + # Export any linked callback messages + for i in msgNode.find_outputs("msgs"): + self._generate_command(exporter, so, responder, commandMgr, i, childWaitOn) + def update(self): super().update() @@ -231,6 +264,7 @@ class PlasmaResponderCommandNode(PlasmaNodeBase, bpy.types.Node): ]) def convert_command(self, exporter, so, responder, commandMgr, waitOn=-1): + raise RuntimeError() def prepare_message(exporter, so, responder, commandMgr, waitOn, msg): idx, command = commandMgr.add_command(self, waitOn) if msg.sender is None: