Browse Source

Implement OneShot logic nodes

This is somewhat simplified in that we classify OneShots as a message. It
just seems easier that way.
pull/10/head
Adam Johnson 9 years ago
parent
commit
f0748b690d
  1. 96
      korman/nodes/node_messages.py
  2. 60
      korman/nodes/node_responder.py

96
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

60
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)

Loading…
Cancel
Save