diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py
index fc24434..4d3dfa6 100644
--- a/korman/exporter/convert.py
+++ b/korman/exporter/convert.py
@@ -111,7 +111,7 @@ class Exporter:
# Instead of exporting a skeleton now, we'll just make an orphaned CI.
# The bl_obj export will make this work.
- parent_ci = self.mgr.find_create_key(parent, plCoordinateInterface).object
+ parent_ci = self.mgr.find_create_key(plCoordinateInterface, bl=bo, so=so).object
parent_ci.addChild(so.key)
else:
self.report.warn("You have parented Plasma Object '{}' to '{}', which has not been marked for export. \
@@ -122,7 +122,7 @@ class Exporter:
"""Ensures that the SceneObject has a CoordinateInterface"""
if not so.coord:
print(" Exporting CoordinateInterface")
- ci = self.mgr.find_create_key(bo, plCoordinateInterface).object
+ ci = self.mgr.find_create_key(plCoordinateInterface, bl=bo, so=so).object
# Now we have the "fun" work of filling in the CI
ci.localToWorld = utils.matrix44(bo.matrix_basis)
@@ -148,7 +148,7 @@ class Exporter:
# Create a sceneobject if one does not exist.
# Before we call the export_fn, we need to determine if this object is an actor of any
# sort, and barf out a CI.
- sceneobject = self.mgr.find_create_key(bl_obj, plSceneObject).object
+ sceneobject = self.mgr.find_create_key(plSceneObject, bl=bl_obj).object
self._export_actor(sceneobject, bl_obj)
export_fn(sceneobject, bl_obj)
diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py
index 7706ea7..fa7e10d 100644
--- a/korman/exporter/manager.py
+++ b/korman/exporter/manager.py
@@ -91,7 +91,7 @@ class ExportManager:
if isinstance(pl, plObjInterface):
if so is None:
- key = self.find_key(bl, plSceneObject)
+ key = self.find_key(plSceneObject, bl)
# prevent race conditions
if key is None:
so = self.add_object(plSceneObject, name=name, loc=location)
@@ -158,22 +158,26 @@ class ExportManager:
self._nodes[location] = None
return location
- def find_create_key(self, bl_obj, pClass, so=None):
- key = self.find_key(bl_obj, pClass, so=so)
+ def find_create_key(self, pClass, bl=None, name=None, so=None):
+ key = self.find_key(pClass, bl, name, so)
if key is None:
- key = self.add_object(pl=pClass, bl=bl_obj, so=so).key
+ key = self.add_object(pl=pClass, name=name, bl=bl, so=so).key
return key
- def find_key(self, bl_obj, pClass, so=None):
+ def find_key(self, pClass, bl=None, name=None, so=None):
"""Given a blender Object and a Plasma class, find (or create) an exported plKey"""
+ assert (bl or name) and (bl or so)
+
if so is None:
- location = self._pages[bl_obj.plasma_object.page]
+ location = self._pages[bl.plasma_object.page]
else:
location = so.key.location
+ if name is None:
+ name = bl.name
index = plFactory.ClassIndex(pClass.__name__)
for key in self.mgr.getKeys(location, index):
- if bl_obj.name == key.name:
+ if name == key.name:
return key
return None
diff --git a/korman/exporter/rtlight.py b/korman/exporter/rtlight.py
index 0fc8bbf..7cf29df 100644
--- a/korman/exporter/rtlight.py
+++ b/korman/exporter/rtlight.py
@@ -92,7 +92,7 @@ class LightConverter:
def _create_light_key(self, bo, bl_light, so):
try:
xlate = _BL2PL[bl_light.type]
- return self.mgr.find_create_key(bo, xlate, so=so)
+ return self.mgr.find_create_key(xlate, bl=bo, so=so)
except LookupError:
raise BlenderOptionNotSupported("Object ('{}') lamp type '{}'".format(bo.name, bl_light.type))
diff --git a/korman/nodes/node_core.py b/korman/nodes/node_core.py
index 574fecb..f214310 100644
--- a/korman/nodes/node_core.py
+++ b/korman/nodes/node_core.py
@@ -13,9 +13,19 @@
# You should have received a copy of the GNU General Public License
# along with Korman. If not, see .
+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:
@@ -34,6 +44,18 @@ class PlasmaNodeBase:
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:
@@ -107,6 +129,11 @@ class PlasmaNodeTree(bpy.types.NodeTree):
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")
diff --git a/korman/nodes/node_logic.py b/korman/nodes/node_logic.py
deleted file mode 100644
index 48971cc..0000000
--- a/korman/nodes/node_logic.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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 .
-
-import bpy
-
-from .node_core import *
-
-class PlasmaLogicTriggerNode(PlasmaNodeBase, bpy.types.Node):
- bl_category = "LOGIC"
- bl_idname = "PlasmaLogicTriggerNode"
- bl_label = "Logic Trigger"
-
- def init(self, context):
- self.inputs.new("PlasmaConditionSocket", "Condition", "condition")
- self.outputs.new("PlasmaRespTriggerSocket", "Trigger", "trigger")
-
-
-class PlasmaRespTriggerSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
- bl_color = (0.384, 0.239, 0.239, 1.0)
diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py
index bec5981..ccb8080 100644
--- a/korman/nodes/node_messages.py
+++ b/korman/nodes/node_messages.py
@@ -15,6 +15,7 @@
import bpy
from bpy.props import *
+from PyHSPlasma import *
from .node_core import *
from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids
@@ -38,3 +39,8 @@ class PlasmaFootstepSoundMsgNode(PlasmaNodeBase, bpy.types.Node):
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
diff --git a/korman/nodes/node_responder.py b/korman/nodes/node_responder.py
index 36ed13a..ca71fe2 100644
--- a/korman/nodes/node_responder.py
+++ b/korman/nodes/node_responder.py
@@ -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)
diff --git a/korman/properties/modifiers/region.py b/korman/properties/modifiers/region.py
index c239eec..407d454 100644
--- a/korman/properties/modifiers/region.py
+++ b/korman/properties/modifiers/region.py
@@ -74,6 +74,13 @@ class PlasmaFootstepRegion(PlasmaModifierProperties, PlasmaModifierLogicWiz):
def created(self, obj):
self.display_name = "{}_FootRgn".format(obj.name)
+ def export(self, exporter, bo, so):
+ # Generate the logic nodes now
+ self.logicwiz(bo)
+
+ # Now, export the node tree
+ self.node_tree.export(exporter, bo, so)
+
def logicwiz(self, bo):
tree = self.node_tree
nodes = tree.nodes