|
|
|
@ -15,6 +15,7 @@
|
|
|
|
|
|
|
|
|
|
import bpy |
|
|
|
|
from bpy.props import * |
|
|
|
|
from PyHSPlasma import * |
|
|
|
|
import uuid |
|
|
|
|
|
|
|
|
|
from .node_core import * |
|
|
|
@ -28,6 +29,40 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
self.inputs.new("PlasmaRespTriggerSocket", "Trigger", "whodoneit") |
|
|
|
|
self.outputs.new("PlasmaRespStateSocket", "States", "states") |
|
|
|
|
|
|
|
|
|
def get_key(self, exporter, tree, so): |
|
|
|
|
return exporter.mgr.find_create_key(plResponderModifier, name=self.create_key_name(tree), so=so) |
|
|
|
|
|
|
|
|
|
def export(self, exporter, tree, bo, so): |
|
|
|
|
responder = self.get_key(exporter, tree, so).object |
|
|
|
|
if not bo.plasma_net.manual_sdl: |
|
|
|
|
responder.setExclude("Responder") |
|
|
|
|
|
|
|
|
|
class ResponderStateMgr: |
|
|
|
|
def __init__(self, respNode, respMod): |
|
|
|
|
self.states = [] |
|
|
|
|
self.parent = respNode |
|
|
|
|
self.responder = respMod |
|
|
|
|
|
|
|
|
|
def get_state(self, node): |
|
|
|
|
for idx, (theNode, theState) in enumerate(self.states): |
|
|
|
|
if theNode == node: |
|
|
|
|
return (idx, theState) |
|
|
|
|
state = plResponderModifier_State() |
|
|
|
|
self.states.append((node, state)) |
|
|
|
|
return (len(self.states) - 1, state) |
|
|
|
|
|
|
|
|
|
def save(self): |
|
|
|
|
resp = self.responder |
|
|
|
|
resp.clearStates() |
|
|
|
|
for node, state in self.states: |
|
|
|
|
resp.addState(state) |
|
|
|
|
|
|
|
|
|
# Convert the Responder states |
|
|
|
|
stateMgr = ResponderStateMgr(self, responder) |
|
|
|
|
for stateNode in self.find_outputs("states", "PlasmaResponderStateNode"): |
|
|
|
|
stateNode.convert_state(exporter, stateMgr) |
|
|
|
|
stateMgr.save() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node): |
|
|
|
|
bl_category = "LOGIC" |
|
|
|
@ -50,6 +85,59 @@ class PlasmaResponderStateNode(PlasmaNodeVariableInput, bpy.types.Node):
|
|
|
|
|
# Now draw a prop |
|
|
|
|
layout.prop(self, "default_state") |
|
|
|
|
|
|
|
|
|
def convert_state(self, exporter, stateMgr): |
|
|
|
|
idx, state = stateMgr.get_state(self) |
|
|
|
|
|
|
|
|
|
# No sanity checking here. Hopefully nothing crazy has happened in the UI. |
|
|
|
|
if self.default_state: |
|
|
|
|
stateMgr.responder.curState = idx |
|
|
|
|
|
|
|
|
|
# Where do we go from heah? |
|
|
|
|
toStateNode = self.find_output("gotostate", "PlasmaResponderStateNode") |
|
|
|
|
if toStateNode is None: |
|
|
|
|
state.switchToState = idx |
|
|
|
|
else: |
|
|
|
|
toIdx, toState = stateMgr.get_state(toStateNode) |
|
|
|
|
state.switchToState = toIdx |
|
|
|
|
|
|
|
|
|
class CommandMgr: |
|
|
|
|
def __init__(self): |
|
|
|
|
self.commands = [] |
|
|
|
|
self.waits = {} |
|
|
|
|
|
|
|
|
|
def add_command(self, node): |
|
|
|
|
cmd = type("ResponderCommand", (), {"msg": None, "waitOn": -1}) |
|
|
|
|
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 save(self, state): |
|
|
|
|
for node, cmd in self.commands: |
|
|
|
|
# Amusing, PyHSPlasma doesn't actually want a plResponderModifier_Cmd |
|
|
|
|
# Meh, I'll let this one slide. |
|
|
|
|
state.addCommand(cmd.msg, cmd.waitOn) |
|
|
|
|
state.numCallbacks = len(self.waits) |
|
|
|
|
state.waitToCmd = self.waits |
|
|
|
|
|
|
|
|
|
# Convert the commands |
|
|
|
|
commands = CommandMgr() |
|
|
|
|
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) |
|
|
|
|
commands.save(state) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlasmaRespStateSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): |
|
|
|
|
bl_color = (0.388, 0.78, 0.388, 1.0) |
|
|
|
@ -65,6 +153,55 @@ 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): |
|
|
|
|
# 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) |
|
|
|
|
|
|
|
|
|
# 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) |
|
|
|
|
|
|
|
|
|
# Finally, convert our message... |
|
|
|
|
msg = msgNode.convert_message(exporter) |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
# Export any child commands |
|
|
|
|
for i in self.find_outputs("trigger", "PlasmaResponderCommandNode"): |
|
|
|
|
i.convert_command(exporter, responder, commandMgr, nowait) |
|
|
|
|
|
|
|
|
|
_bcast_flags = { |
|
|
|
|
plArmatureEffectStateMsg: (plMessage.kPropagateToModifiers | plMessage.kNetPropagate), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
def _finalize_message(self, exporter, responder, msg): |
|
|
|
|
msg.sender = responder.key |
|
|
|
|
|
|
|
|
|
# BCast Flags are pretty common... |
|
|
|
|
_cls = msg.__class__ |
|
|
|
|
if _cls in self._bcast_flags: |
|
|
|
|
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) |
|
|
|
|