|
|
|
@ -21,8 +21,9 @@ from PyHSPlasma import *
|
|
|
|
|
import uuid |
|
|
|
|
|
|
|
|
|
from .node_core import * |
|
|
|
|
from .node_deprecated import PlasmaVersionedNode |
|
|
|
|
|
|
|
|
|
class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node): |
|
|
|
|
class PlasmaResponderNode(PlasmaVersionedNode, bpy.types.Node): |
|
|
|
|
bl_category = "LOGIC" |
|
|
|
|
bl_idname = "PlasmaResponderNode" |
|
|
|
|
bl_label = "Responder" |
|
|
|
@ -40,6 +41,8 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
no_ff_sounds = BoolProperty(name="Don't F-Fwd Sounds", |
|
|
|
|
description="When fast-forwarding, play sound effects", |
|
|
|
|
default=False) |
|
|
|
|
default_state = IntProperty(name="Default State Index", |
|
|
|
|
options=set()) |
|
|
|
|
|
|
|
|
|
input_sockets = OrderedDict([ |
|
|
|
|
("condition", { |
|
|
|
@ -55,9 +58,22 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
"type": "PlasmaPythonReferenceNodeSocket", |
|
|
|
|
"valid_link_nodes": {"PlasmaPythonFileNode"}, |
|
|
|
|
}), |
|
|
|
|
("state_refs", { |
|
|
|
|
"text": "State", |
|
|
|
|
"type": "PlasmaRespStateRefSocket", |
|
|
|
|
"valid_link_nodes": "PlasmaResponderStateNode", |
|
|
|
|
"valid_link_sockets": "PlasmaRespStateRefSocket", |
|
|
|
|
"link_limit": 1, |
|
|
|
|
"spawn_empty": True, |
|
|
|
|
}), |
|
|
|
|
|
|
|
|
|
# This version of the states socket has been deprecated. |
|
|
|
|
# We need to be able to track 1 socket -> 1 state to manage |
|
|
|
|
# responder state IDs |
|
|
|
|
("states", { |
|
|
|
|
"text": "States", |
|
|
|
|
"type": "PlasmaRespStateSocket", |
|
|
|
|
"hidden": True, |
|
|
|
|
}), |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
@ -80,6 +96,7 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
responder.flags |= plResponderModifier.kDetectUnTrigger |
|
|
|
|
if self.no_ff_sounds: |
|
|
|
|
responder.flags |= plResponderModifier.kSkipFFSound |
|
|
|
|
responder.curState = self.default_state |
|
|
|
|
|
|
|
|
|
class ResponderStateMgr: |
|
|
|
|
def __init__(self, respNode, respMod): |
|
|
|
@ -87,25 +104,58 @@ class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
self.parent = respNode |
|
|
|
|
self.responder = respMod |
|
|
|
|
|
|
|
|
|
def convert_states(self, exporter, so): |
|
|
|
|
# This could implicitly export more states... |
|
|
|
|
i = 0 |
|
|
|
|
while i < len(self.states): |
|
|
|
|
node, state = self.states[i] |
|
|
|
|
node.convert_state(exporter, so, state, i, self) |
|
|
|
|
i += 1 |
|
|
|
|
|
|
|
|
|
resp = self.responder |
|
|
|
|
resp.clearStates() |
|
|
|
|
for node, state in self.states: |
|
|
|
|
resp.addState(state) |
|
|
|
|
|
|
|
|
|
def get_state(self, node): |
|
|
|
|
for idx, (theNode, theState) in enumerate(self.states): |
|
|
|
|
if theNode == node: |
|
|
|
|
return (idx, theState, True) |
|
|
|
|
return (idx, theState) |
|
|
|
|
state = plResponderModifier_State() |
|
|
|
|
self.states.append((node, state)) |
|
|
|
|
return (len(self.states) - 1, state, False) |
|
|
|
|
return (len(self.states) - 1, state) |
|
|
|
|
|
|
|
|
|
def save(self): |
|
|
|
|
resp = self.responder |
|
|
|
|
resp.clearStates() |
|
|
|
|
for node, state in self.states: |
|
|
|
|
resp.addState(state) |
|
|
|
|
def register_state(self, node): |
|
|
|
|
self.states.append((node, plResponderModifier_State())) |
|
|
|
|
|
|
|
|
|
# Convert the Responder states |
|
|
|
|
stateMgr = ResponderStateMgr(self, responder) |
|
|
|
|
for stateNode in self.find_outputs("states", "PlasmaResponderStateNode"): |
|
|
|
|
stateNode.convert_state(exporter, so, stateMgr) |
|
|
|
|
stateMgr.save() |
|
|
|
|
for stateNode in self.find_outputs("state_refs", "PlasmaResponderStateNode"): |
|
|
|
|
stateMgr.register_state(stateNode) |
|
|
|
|
stateMgr.convert_states(exporter, so) |
|
|
|
|
|
|
|
|
|
def upgrade(self): |
|
|
|
|
# In version 1 responder nodes, responder states could be linked to the responder |
|
|
|
|
# or to subsequent responder state nodes and be exported. The problem with this |
|
|
|
|
# is that to use responder states in Python attributes, we need to be able to |
|
|
|
|
# inform the user as to what the ID of the responder state will be. |
|
|
|
|
# Version 2 make it slightly more mandatory that states be linked to a responder |
|
|
|
|
# and will display the ID of each state linked to the responder. Any states only |
|
|
|
|
# linked to other states will be converted at the end of the list. |
|
|
|
|
if self.version == 1: |
|
|
|
|
states = set() |
|
|
|
|
def _link_states(state): |
|
|
|
|
if state in states: |
|
|
|
|
return |
|
|
|
|
states.add(state) |
|
|
|
|
self.link_output(state, "state_refs", "resp") |
|
|
|
|
goto = state.find_output("gotostate") |
|
|
|
|
if goto is not None: |
|
|
|
|
_link_states(goto) |
|
|
|
|
for i in self.find_outputs("states"): |
|
|
|
|
_link_states(i) |
|
|
|
|
self.unlink_outputs("states", "socket deprecated (upgrade complete)") |
|
|
|
|
self.version = 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node): |
|
|
|
@ -113,16 +163,45 @@ class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
bl_idname = "PlasmaResponderStateNode" |
|
|
|
|
bl_label = "Responder State" |
|
|
|
|
|
|
|
|
|
def _get_default_state(self): |
|
|
|
|
resp_node = self.find_input("resp") |
|
|
|
|
if resp_node is not None: |
|
|
|
|
try: |
|
|
|
|
state_idx = next((idx for idx, node in enumerate(resp_node.find_outputs("state_refs")) if node == self)) |
|
|
|
|
except StopIteration: |
|
|
|
|
return False |
|
|
|
|
else: |
|
|
|
|
return resp_node.default_state == state_idx |
|
|
|
|
return False |
|
|
|
|
def _set_default_state(self, value): |
|
|
|
|
if value: |
|
|
|
|
resp_node = self.find_input("resp") |
|
|
|
|
if resp_node is not None: |
|
|
|
|
try: |
|
|
|
|
state_idx = next((idx for idx, node in enumerate(resp_node.find_outputs("state_refs")) if node == self)) |
|
|
|
|
except StopIteration: |
|
|
|
|
self._whine("unable to set default state on responder") |
|
|
|
|
else: |
|
|
|
|
resp_node.default_state = state_idx |
|
|
|
|
|
|
|
|
|
default_state = BoolProperty(name="Default State", |
|
|
|
|
description="This state is the responder's default", |
|
|
|
|
default=False) |
|
|
|
|
get=_get_default_state, |
|
|
|
|
set=_set_default_state, |
|
|
|
|
options=set()) |
|
|
|
|
|
|
|
|
|
input_sockets = OrderedDict([ |
|
|
|
|
("condition", { |
|
|
|
|
"text": "Condition", |
|
|
|
|
"text": "Triggers State", |
|
|
|
|
"type": "PlasmaRespStateSocket", |
|
|
|
|
"spawn_empty": True, |
|
|
|
|
}), |
|
|
|
|
("resp", { |
|
|
|
|
"text": "Responder", |
|
|
|
|
"type": "PlasmaRespStateRefSocket", |
|
|
|
|
"valid_link_nodes": "PlasmaResponderNode", |
|
|
|
|
"valid_link_sockets": "PlasmaRespStateRefSocket", |
|
|
|
|
}), |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
output_sockets = OrderedDict([ |
|
|
|
@ -141,30 +220,23 @@ class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
}), |
|
|
|
|
("gotostate", { |
|
|
|
|
"link_limit": 1, |
|
|
|
|
"text": "Trigger", |
|
|
|
|
"text": "Triggers State", |
|
|
|
|
"type": "PlasmaRespStateSocket", |
|
|
|
|
}), |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
def draw_buttons(self, context, layout): |
|
|
|
|
layout.active = self.find_input("resp") is not None |
|
|
|
|
layout.prop(self, "default_state") |
|
|
|
|
|
|
|
|
|
def convert_state(self, exporter, so, stateMgr): |
|
|
|
|
idx, state, converted = stateMgr.get_state(self) |
|
|
|
|
|
|
|
|
|
# No sanity checking here. Hopefully nothing crazy has happened in the UI. |
|
|
|
|
if self.default_state: |
|
|
|
|
stateMgr.responder.curState = idx |
|
|
|
|
|
|
|
|
|
def convert_state(self, exporter, so, state, idx, stateMgr): |
|
|
|
|
# Where do we go from heah? |
|
|
|
|
toStateNode = self.find_output("gotostate", "PlasmaResponderStateNode") |
|
|
|
|
if toStateNode is None: |
|
|
|
|
state.switchToState = idx |
|
|
|
|
else: |
|
|
|
|
toIdx, toState, converted = stateMgr.get_state(toStateNode) |
|
|
|
|
toIdx, toState = stateMgr.get_state(toStateNode) |
|
|
|
|
state.switchToState = toIdx |
|
|
|
|
if not converted: |
|
|
|
|
toStateNode.convert_state(exporter, so, stateMgr) |
|
|
|
|
|
|
|
|
|
class CommandMgr: |
|
|
|
|
def __init__(self): |
|
|
|
@ -229,15 +301,21 @@ class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node):
|
|
|
|
|
for i in msgNode.find_outputs("msgs"): |
|
|
|
|
self._generate_command(exporter, so, responder, commandMgr, i, childWaitOn) |
|
|
|
|
|
|
|
|
|
def update(self): |
|
|
|
|
super().update() |
|
|
|
|
|
|
|
|
|
# Check to see if we're the default state |
|
|
|
|
if not self.default_state: |
|
|
|
|
inputs = list(self.find_input_sockets("condition", "PlasmaResponderNode")) |
|
|
|
|
if len(inputs) == 1: |
|
|
|
|
self.default_state = True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlasmaRespStateSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): |
|
|
|
|
bl_color = (0.388, 0.78, 0.388, 1.0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlasmaRespStateRefSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): |
|
|
|
|
bl_color = (1.00, 0.980, 0.322, 1.0) |
|
|
|
|
|
|
|
|
|
def draw(self, context, layout, node, text): |
|
|
|
|
if isinstance(node, PlasmaResponderNode): |
|
|
|
|
try: |
|
|
|
|
idx = next((idx for idx, socket in enumerate(node.find_output_sockets("state_refs")) if socket == self)) |
|
|
|
|
except StopIteration: |
|
|
|
|
layout.label(text) |
|
|
|
|
else: |
|
|
|
|
layout.label("State (ID: {})".format(idx)) |
|
|
|
|
else: |
|
|
|
|
layout.label(text) |
|
|
|
|