mirror of https://github.com/H-uru/korman.git
Adam Johnson
10 years ago
15 changed files with 842 additions and 12 deletions
@ -0,0 +1,67 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
import inspect |
||||
from nodeitems_utils import NodeCategory, NodeItem |
||||
import nodeitems_utils |
||||
|
||||
# Put all Korman node modules here... |
||||
from .node_conditions import * |
||||
from .node_core import * |
||||
from .node_messages import * |
||||
from .node_responder import * |
||||
|
||||
class PlasmaNodeCategory(NodeCategory): |
||||
"""Plasma Node Category""" |
||||
|
||||
@classmethod |
||||
def poll(cls, context): |
||||
return (context.space_data.tree_type == "PlasmaNodeTree") |
||||
|
||||
# Here's what you need to know about this... |
||||
# If you add a new category, put the pretty name here! |
||||
# If you're making a new Node, ensure that your bl_idname attribute is present AND matches |
||||
# the class name. Otherwise, absolutely fascinating things will happen. Don't expect for me |
||||
# to come and rescue you from it, either. |
||||
_kategory_names = { |
||||
"CONDITIONS": "Conditions", |
||||
"LOGIC": "Logic", |
||||
"MSG": "Message", |
||||
} |
||||
|
||||
# Now, generate the categories as best we can... |
||||
_kategories = {} |
||||
for cls in dict(globals()).values(): |
||||
if inspect.isclass(cls): |
||||
if not issubclass(cls, PlasmaNodeBase) or not issubclass(cls, bpy.types.Node): |
||||
continue |
||||
else: |
||||
continue |
||||
try: |
||||
_kategories[cls.bl_category].append(cls) |
||||
except LookupError: |
||||
_kategories[cls.bl_category] = [cls,] |
||||
_actual_kategories = [] |
||||
for i in sorted(_kategories.keys(), key=lambda x: _kategory_names[x]): |
||||
# Note that even though we're sorting the category names, Blender appears to not care... |
||||
_kat_items = [NodeItem(j.bl_idname) for j in sorted(_kategories[i], key=lambda x: x.bl_label)] |
||||
_actual_kategories.append(PlasmaNodeCategory(i, _kategory_names[i], items=_kat_items)) |
||||
|
||||
def register(): |
||||
nodeitems_utils.register_node_categories("PLASMA_NODES", _actual_kategories) |
||||
|
||||
def unregister(): |
||||
nodeitems_utils.unregister_node_categories("PLASMA_NODES") |
@ -0,0 +1,178 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
from bpy.props import * |
||||
from PyHSPlasma import * |
||||
|
||||
from .node_core import PlasmaNodeBase, PlasmaNodeSocketBase |
||||
from ..properties.modifiers.physics import bounds_types |
||||
|
||||
class PlasmaConditionSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): |
||||
bl_color = (0.188, 0.086, 0.349, 1.0) |
||||
|
||||
|
||||
class PlasmaVolumeReportNode(PlasmaNodeBase, bpy.types.Node): |
||||
bl_category = "CONDITIONS" |
||||
bl_idname = "PlasmaVoumeReportNode" |
||||
bl_label = "Region Trigger Settings" |
||||
|
||||
report_when = EnumProperty(name="When", |
||||
description="When the region should trigger", |
||||
items=[("each", "Each Event", "The region will trigger on every enter/exit"), |
||||
("first", "First Event", "The region will trigger on the first event only"), |
||||
("count", "Population", "When the region has a certain number of objects inside it")]) |
||||
threshold = IntProperty(name="Threshold", |
||||
description="How many objects should be in the region for it to trigger", |
||||
min=1) |
||||
|
||||
def init(self, context): |
||||
self.outputs.new("PlasmaVolumeSettingsSocketOut", "Trigger Settings") |
||||
|
||||
def draw_buttons(self, context, layout): |
||||
layout.prop(self, "report_when") |
||||
if self.report_when == "count": |
||||
row = layout.row() |
||||
row.label("Threshold: ") |
||||
row.prop(self, "threshold", text="") |
||||
|
||||
|
||||
class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node): |
||||
bl_category = "CONDITIONS" |
||||
bl_idname = "PlasmaVolumeSensorNode" |
||||
bl_label = "Region Sensor" |
||||
bl_width_default = 190 |
||||
|
||||
# Region Mesh |
||||
region = StringProperty(name="Region", |
||||
description="Object that defines the region mesh") |
||||
bounds = EnumProperty(name="Bounds", |
||||
description="Physical object's bounds", |
||||
items=bounds_types) |
||||
|
||||
# Detector Properties |
||||
report_on = EnumProperty(name="Triggerers", |
||||
description="What triggers this region?", |
||||
options={"ANIMATABLE", "ENUM_FLAG"}, |
||||
items=[("avatar", "Avatars", "Avatars trigger this region"), |
||||
("dynamics", "Dynamics", "Any non-avatar dynamic physical object (eg kickables)")], |
||||
default={"avatar"}) |
||||
|
||||
def init(self, context): |
||||
self.inputs.new("PlasmaVolumeSettingsSocketIn", "Trigger on Enter", "enter") |
||||
self.inputs.new("PlasmaVolumeSettingsSocketIn", "Trigger on Exit", "exit") |
||||
self.outputs.new("PlasmaConditionSocket", "Satisfies", "satisfies") |
||||
|
||||
def draw_buttons(self, context, layout): |
||||
layout.prop(self, "report_on") |
||||
|
||||
# Okay, if they changed the name of the ObData, that's THEIR problem... |
||||
layout.prop_search(self, "region", bpy.data, "objects", icon="MESH_DATA") |
||||
layout.prop(self, "bounds") |
||||
|
||||
def export(self, exporter, tree, bo, so): |
||||
interface = exporter.mgr.add_object(plInterfaceInfoModifier, name=self.create_key_name(tree), so=so) |
||||
|
||||
# Region Enters |
||||
enter_simple = self.find_input_socket("enter").allow |
||||
enter_settings = self.find_input("enter", "PlasmaVolumeReportNode") |
||||
if enter_simple or enter_settings is not None: |
||||
key = self._export_volume_event(exporter, tree, bo, so, plVolumeSensorConditionalObject.kTypeEnter, enter_settings) |
||||
interface.addIntfKey(key) |
||||
|
||||
# Region Exits |
||||
exit_simple = self.find_input_socket("exit").allow |
||||
exit_settings = self.find_input("exit", "PlasmaVolumeReportNode") |
||||
if exit_simple or exit_settings is not None: |
||||
key = self._export_volume_event(exporter, tree, bo, so, plVolumeSensorConditionalObject.kTypeExit, exit_settings) |
||||
interface.addIntfKey(key) |
||||
|
||||
# Don't forget to export the physical object itself! |
||||
# [trollface.jpg] |
||||
simIface, physical = exporter.physics.generate_physical(bo, so, "{}_VolumeSensor".format(bo.name)) |
||||
phys_bo = bpy.data.objects[self.region] |
||||
exporter.physics.export(phys_bo, physical, self.bounds) |
||||
|
||||
physical.memberGroup = plSimDefs.kGroupDetector |
||||
if "avatar" in self.report_on: |
||||
physical.reportGroup |= 1 << plSimDefs.kGroupAvatar |
||||
if "dynamics" in self.report_on: |
||||
physical.reportGroup |= 1 << plSimDefs.kGroupDynamic |
||||
|
||||
def _export_volume_event(self, exporter, tree, bo, so, event, settings): |
||||
if event == plVolumeSensorConditionalObject.kTypeEnter: |
||||
suffix = "Enter" |
||||
else: |
||||
suffix = "Exit" |
||||
|
||||
theName = "{}_{}_{}".format(tree.name, self.name, suffix) |
||||
print(" [LogicModifier '{}']".format(theName)) |
||||
logicKey = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so) |
||||
logicmod = logicKey.object |
||||
logicmod.setLogicFlag(plLogicModifier.kMultiTrigger, True) |
||||
|
||||
# LogicMod notification... This is one of the cases where the linked node needs to match |
||||
# exactly one key... |
||||
notify = plNotifyMsg() |
||||
notify.BCastFlags = (plMessage.kNetPropagate | plMessage.kLocalPropagate) |
||||
for i in self.find_outputs("satisfies"): |
||||
key = i.get_key(exporter, tree, so) |
||||
if key is None: |
||||
print(" WARNING: '{}' Node '{}' doesn't expose a key. It won't be triggered!".format(i.bl_idname, i.name)) |
||||
else: |
||||
notify.addReceiver(key) |
||||
logicmod.notify = notify |
||||
|
||||
# Now, the detector objects |
||||
print(" [ObjectInVolumeDetector '{}']".format(theName)) |
||||
detKey = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=theName, so=so) |
||||
det = detKey.object |
||||
|
||||
print(" [VolumeSensorConditionalObject '{}']".format(theName)) |
||||
volKey = exporter.mgr.find_create_key(plVolumeSensorConditionalObject, name=theName, so=so) |
||||
volsens = volKey.object |
||||
|
||||
volsens.type = event |
||||
if settings is not None: |
||||
if settings.report_when == "first": |
||||
volsens.first = True |
||||
elif settings.report_when == "threshold": |
||||
volsens.trigNum = settings.threshold |
||||
|
||||
# There appears to be a mandatory order for these keys... |
||||
det.addReceiver(volKey) |
||||
det.addReceiver(logicKey) |
||||
|
||||
# End mandatory order |
||||
logicmod.addCondition(volKey) |
||||
return logicKey |
||||
|
||||
|
||||
|
||||
class PlasmaVolumeSettingsSocket(PlasmaNodeSocketBase): |
||||
bl_color = (43.1, 24.7, 0.0, 1.0) |
||||
|
||||
|
||||
class PlasmaVolumeSettingsSocketIn(PlasmaVolumeSettingsSocket, bpy.types.NodeSocket): |
||||
allow = BoolProperty() |
||||
|
||||
def draw(self, context, layout, node, text): |
||||
if not self.is_linked: |
||||
layout.prop(self, "allow", text="") |
||||
layout.label(text) |
||||
|
||||
|
||||
class PlasmaVolumeSettingsSocketOut(PlasmaVolumeSettingsSocket, bpy.types.NodeSocket): |
||||
pass |
@ -0,0 +1,139 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import abc |
||||
import bpy |
||||
|
||||
class PlasmaNodeBase: |
||||
def create_key_name(self, tree): |
||||
return "{}_{}".format(tree.name, self.name) |
||||
|
||||
def get_key(self, exporter, tree, so): |
||||
return None |
||||
|
||||
def export(self, exporter, tree, bo, so): |
||||
pass |
||||
|
||||
def find_input(self, key, idname=None): |
||||
for i in self.inputs: |
||||
if i.identifier == key: |
||||
if i.links: |
||||
node = i.links[0].from_node |
||||
if idname is not None and idname != node.bl_idname: |
||||
return None |
||||
return node |
||||
else: |
||||
return None |
||||
raise KeyError(key) |
||||
|
||||
def find_input_socket(self, key): |
||||
for i in self.inputs: |
||||
if i.identifier == key: |
||||
return i |
||||
raise KeyError(key) |
||||
|
||||
def find_output(self, key, idname=None): |
||||
for i in self.outputs: |
||||
if i.identifier == key: |
||||
if i.links: |
||||
node = i.links[0].to_node |
||||
if idname is not None and idname != node.bl_idname: |
||||
return None |
||||
return node |
||||
else: |
||||
return None |
||||
raise KeyError(key) |
||||
|
||||
def find_outputs(self, key, idname=None): |
||||
for i in self.outputs: |
||||
if i.identifier == key: |
||||
for j in i.links: |
||||
node = j.to_node |
||||
if idname is not None and idname != node.bl_idname: |
||||
continue |
||||
yield node |
||||
|
||||
def find_output_socket(self, key): |
||||
for i in self.outputs: |
||||
if i.identifier == key: |
||||
return i |
||||
raise KeyError(key) |
||||
|
||||
def link_input(self, tree, node, out_key, in_key): |
||||
"""Links a given Node's output socket to a given input socket on this Node""" |
||||
if isinstance(in_key, str): |
||||
in_socket = self.find_input_socket(in_key) |
||||
else: |
||||
in_socket = in_key |
||||
if isinstance(out_key, str): |
||||
out_socket = node.find_output_socket(out_key) |
||||
else: |
||||
out_socket = out_key |
||||
link = tree.links.new(in_socket, out_socket) |
||||
|
||||
def link_output(self, tree, node, out_key, in_key): |
||||
"""Links a given Node's input socket to a given output socket on this Node""" |
||||
if isinstance(in_key, str): |
||||
in_socket = node.find_input_socket(in_key) |
||||
else: |
||||
in_socket = in_key |
||||
if isinstance(out_key, str): |
||||
out_socket = self.find_output_socket(out_key) |
||||
else: |
||||
out_socket = out_key |
||||
link = tree.links.new(in_socket, out_socket) |
||||
|
||||
@classmethod |
||||
def poll(cls, context): |
||||
return (context.bl_idname == "PlasmaNodeTree") |
||||
|
||||
|
||||
class PlasmaNodeVariableInput(PlasmaNodeBase): |
||||
def ensure_sockets(self, idname, name, identifier=None): |
||||
"""Ensures there is one (and only one) empty input socket""" |
||||
empty = [i for i in self.inputs if i.bl_idname == idname and not i.links] |
||||
if not empty: |
||||
if identifier is None: |
||||
self.inputs.new(idname, name) |
||||
else: |
||||
self.inputs.new(idname, name, identifier) |
||||
while len(empty) > 1: |
||||
self.inputs.remove(empty.pop()) |
||||
|
||||
|
||||
class PlasmaNodeSocketBase: |
||||
def draw(self, context, layout, node, text): |
||||
layout.label(text) |
||||
|
||||
def draw_color(self, context, node): |
||||
# It's so tempting to just do RGB sometimes... Let's be nice. |
||||
if len(self.bl_color) == 3: |
||||
return tuple(self.bl_color[0], self.bl_color[1], self.bl_color[2], 1.0) |
||||
return self.bl_color |
||||
|
||||
|
||||
class PlasmaNodeTree(bpy.types.NodeTree): |
||||
bl_idname = "PlasmaNodeTree" |
||||
bl_label = "Plasma" |
||||
bl_icon = "NODETREE" |
||||
|
||||
def export(self, exporter, bo, so): |
||||
# just pass it off to each node |
||||
for node in self.nodes: |
||||
node.export(exporter, self, bo, so) |
||||
|
||||
@classmethod |
||||
def poll(cls, context): |
||||
return (context.scene.render.engine == "PLASMA_GAME") |
@ -0,0 +1,46 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
from bpy.props import * |
||||
from PyHSPlasma import * |
||||
|
||||
from .node_core import * |
||||
from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids |
||||
|
||||
class PlasmaMessageSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): |
||||
bl_color = (0.004, 0.282, 0.349, 1.0) |
||||
|
||||
|
||||
class PlasmaFootstepSoundMsgNode(PlasmaNodeBase, bpy.types.Node): |
||||
bl_category = "MSG" |
||||
bl_idname = "PlasmaFootstepSoundMsgNode" |
||||
bl_label = "Footstep Sound" |
||||
|
||||
surface = EnumProperty(name="Surface", |
||||
description="What kind of surface are we walking on?", |
||||
items=footstep_surfaces, |
||||
default="stone") |
||||
|
||||
def init(self, context): |
||||
self.inputs.new("PlasmaMessageSocket", "Sender", "sender") |
||||
|
||||
def draw_buttons(self, context, layout): |
||||
layout.prop(self, "surface") |
||||
|
||||
def convert_message(self, exporter): |
||||
msg = plArmatureEffectStateMsg() |
||||
msg.surface = footstep_surface_ids[self.surface] |
||||
return msg |
@ -0,0 +1,234 @@
|
||||
# This file is part of Korman. |
||||
# |
||||
# Korman is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Korman is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
import bpy |
||||
from bpy.props import * |
||||
from PyHSPlasma import * |
||||
import uuid |
||||
|
||||
from .node_core import * |
||||
|
||||
class PlasmaResponderNode(PlasmaNodeVariableInput, bpy.types.Node): |
||||
bl_category = "LOGIC" |
||||
bl_idname = "PlasmaResponderNode" |
||||
bl_label = "Responder" |
||||
bl_width_default = 145 |
||||
|
||||
detect_trigger = BoolProperty(name="Detect Trigger", |
||||
description="When notified, trigger the Responder", |
||||
default=True) |
||||
detect_untrigger = BoolProperty(name="Detect UnTrigger", |
||||
description="When notified, untrigger the Responder", |
||||
default=False) |
||||
no_ff_sounds = BoolProperty(name="Don't F-Fwd Sounds", |
||||
description="When fast-forwarding, play sound effects", |
||||
default=False) |
||||
|
||||
def init(self, context): |
||||
self.inputs.new("PlasmaConditionSocket", "Condition", "condition") |
||||
self.outputs.new("PlasmaRespStateSocket", "States", "states") |
||||
|
||||
def draw_buttons(self, context, layout): |
||||
self.ensure_sockets("PlasmaConditionSocket", "Condition", "condition") |
||||
|
||||
layout.prop(self, "detect_trigger") |
||||
layout.prop(self, "detect_untrigger") |
||||
layout.prop(self, "no_ff_sounds") |
||||
|
||||
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") |
||||
|
||||
if self.detect_trigger: |
||||
responder.flags |= plResponderModifier.kDetectTrigger |
||||
if self.detect_untrigger: |
||||
responder.flags |= plResponderModifier.kDetectUnTrigger |
||||
if self.no_ff_sounds: |
||||
responder.flags |= plResponderModifier.kSkipFFSound |
||||
|
||||
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" |
||||
bl_idname = "PlasmaResponderStateNode" |
||||
bl_label = "Responder State" |
||||
|
||||
default_state = BoolProperty(name="Default State", |
||||
description="This state is the responder's default", |
||||
default=False) |
||||
|
||||
def init(self, context): |
||||
self.inputs.new("PlasmaRespStateSocket", "Condition", "condition") |
||||
self.outputs.new("PlasmaRespCommandSocket", "Commands", "cmds") |
||||
self.outputs.new("PlasmaRespStateSocket", "Trigger", "gotostate").link_limit = 1 |
||||
|
||||
def draw_buttons(self, context, layout): |
||||
# This actually draws nothing, but it makes sure we have at least one empty input slot |
||||
# We need this because it's possible that multiple OTHER states can call us |
||||
self.ensure_sockets("PlasmaRespStateSocket", "Condition", "condition") |
||||
|
||||
# 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) |
||||
|
||||
|
||||
class PlasmaResponderCommandNode(PlasmaNodeBase, bpy.types.Node): |
||||
bl_category = "LOGIC" |
||||
bl_idname = "PlasmaResponderCommandNode" |
||||
bl_label = "Responder Command" |
||||
|
||||
def init(self, context): |
||||
self.inputs.new("PlasmaRespCommandSocket", "Condition", "whodoneit") |
||||
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) |
||||
|
Loading…
Reference in new issue