Browse Source

Begin work on Logic Nodes UI

pull/6/head
Adam Johnson 10 years ago
parent
commit
5192be428e
  1. 3
      korman/__init__.py
  2. 68
      korman/nodes/__init__.py
  3. 98
      korman/nodes/node_conditions.py
  4. 87
      korman/nodes/node_core.py
  5. 31
      korman/nodes/node_logic.py
  6. 40
      korman/nodes/node_messages.py
  7. 108
      korman/nodes/node_responder.py
  8. 1
      korman/operators/__init__.py
  9. 55
      korman/operators/op_nodes.py
  10. 35
      korman/properties/modifiers/region.py

3
korman/__init__.py

@ -16,6 +16,7 @@
import bpy import bpy
from . import exporter, render from . import exporter, render
from . import properties, ui from . import properties, ui
from . import nodes
from . import operators from . import operators
bl_info = { bl_info = {
@ -37,6 +38,7 @@ def register():
bpy.utils.register_module(__name__) bpy.utils.register_module(__name__)
# Sigh... Blender isn't totally automated. # Sigh... Blender isn't totally automated.
nodes.register()
operators.register() operators.register()
properties.register() properties.register()
@ -44,6 +46,7 @@ def register():
def unregister(): def unregister():
"""Unregisters all Blender operators and GUI items""" """Unregisters all Blender operators and GUI items"""
bpy.utils.unregister_module(__name__) bpy.utils.unregister_module(__name__)
nodes.unregister()
operators.unregister() operators.unregister()

68
korman/nodes/__init__.py

@ -0,0 +1,68 @@
# 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_logic 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")

98
korman/nodes/node_conditions.py

@ -0,0 +1,98 @@
# 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 .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"),
("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, "meshes", icon="MESH_DATA")
layout.prop(self, "bounds")
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

87
korman/nodes/node_core.py

@ -0,0 +1,87 @@
# 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
class PlasmaNodeBase:
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_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"""
in_socket = self.find_input_socket(in_key)
out_socket = node.find_output_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"""
in_socket = node.find_input_socket(in_key)
out_socket = self.find_output_socket(out_key)
link = tree.links.new(in_socket, out_socket)
@classmethod
def poll(cls, context):
return (context.bl_idname == "PlasmaNodeTree")
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"
@classmethod
def poll(cls, context):
return (context.scene.render.engine == "PLASMA_GAME")

31
korman/nodes/node_logic.py

@ -0,0 +1,31 @@
# 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 .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)

40
korman/nodes/node_messages.py

@ -0,0 +1,40 @@
# 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 .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")

108
korman/nodes/node_responder.py

@ -0,0 +1,108 @@
# 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 *
import uuid
from .node_core import *
class PlasmaResponderNode(PlasmaNodeBase, bpy.types.Node):
bl_category = "LOGIC"
bl_idname = "PlasmaResponderNode"
bl_label = "Responder"
def init(self, context):
self.inputs.new("PlasmaRespTriggerSocket", "Trigger", "whodoneit")
self.outputs.new("PlasmaRespStateSocket", "States", "states")
class PlasmaResponderStateNode(PlasmaNodeBase, bpy.types.Node):
bl_category = "LOGIC"
bl_idname = "PlasmaResponderStateNode"
bl_label = "Responder State"
def init(self, context):
self.inputs.new("PlasmaRespStateSocket", "Condition", "whodoneit")
self.outputs.new("PlasmaRespCommandSocket", "Commands", "cmds")
self.outputs.new("PlasmaRespStateSocket", "Trigger", "gotostate").link_limit = 1
class PlasmaRespStateSocketBase(PlasmaNodeSocketBase):
bl_color = (0.388, 0.78, 0.388, 1.0)
class PlasmaRespStateSocket(PlasmaRespStateSocketBase, bpy.types.NodeSocket):
default_state = BoolProperty(name="Default State",
description="This state is the Responder's default",
default=False)
def draw(self, context, layout, node, text):
# If this is a RespoderState node and the parent is a Responder, offer the user the
# ability to make this the default state.
if self.is_linked and not self.is_output:
# Before we do anything, see if we need to do a delayed update...
if node.bl_idname == "PlasmaResponderStateNode":
parent = node.find_input("whodoneit", "PlasmaResponderNode")
if parent is not None:
layout.prop(self, "default_state")
return
# Still here? Draw the text.
layout.label(text)
class PlasmaResponderStateListNode(PlasmaNodeBase, bpy.types.Node):
bl_category = "LOGIC"
bl_idname = "PlasmaResponderStateListNode"
bl_label = "Responder State List"
def add_state_input(self):
self.inputs.new("PlasmaRespStateListSocket", str(uuid.uuid4()))
def init(self, context):
# Inputs will be added by the user
self.outputs.new("PlasmaRespStateSocket", "Go To State", "gotostate")
def draw_buttons(self, context, layout):
# This will allow us to add input states on the fly.
# Caveat: We're only showing this operator in the properties because we need the node
# to be active in the operator...
op = layout.operator("node.plasma_add_responder_state", text="Add State", icon="ZOOMIN")
op.node_name = self.name
class PlasmaRespStateListSocket(PlasmaRespStateSocketBase, bpy.types.NodeSocket):
def draw(self, context, layout, node, text):
# We'll allow them to delete all their inputs if they want to be stupid...
props = layout.operator("node.plasma_remove_responder_state", text="", icon="X")
props.node_name = node.name
props.socket_name = self.name
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")
class PlasmaRespCommandSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket):
bl_color = (0.451, 0.0, 0.263, 1.0)

1
korman/operators/__init__.py

@ -16,6 +16,7 @@
from . import op_export as exporter from . import op_export as exporter
from . import op_lightmap as lightmap from . import op_lightmap as lightmap
from . import op_modifier as modifier from . import op_modifier as modifier
from . import op_nodes as node
from . import op_world as world from . import op_world as world
def register(): def register():

55
korman/operators/op_nodes.py

@ -0,0 +1,55 @@
# 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 *
class NodeOperator:
def get_node_tree(self, context):
space = context.space_data
if space.type != "NODE_EDITOR":
raise RuntimeError("Operator '{}' should only be used in the node editor".format(self.bl_idname))
return space.node_tree
@classmethod
def poll(cls, context):
return context.scene.render.engine == "PLASMA_GAME"
class ResponderStateAddOperator(NodeOperator, bpy.types.Operator):
bl_idname = "node.plasma_add_responder_state"
bl_label = "Add Responder State Socket"
node_name = StringProperty(name="Node's name", options={"HIDDEN"})
def execute(self, context):
tree = self.get_node_tree(context)
tree.nodes[self.node_name].add_state_input()
return {"FINISHED"}
class ResponderStateRemoveOperator(NodeOperator, bpy.types.Operator):
bl_idname = "node.plasma_remove_responder_state"
bl_label = "Remove Responder State Socket"
node_name = StringProperty(name="Node's name", options={"HIDDEN"})
socket_name = StringProperty(name="Socket name to remove", options={"HIDDEN"})
def execute(self, context):
tree = self.get_node_tree(context)
node = tree.nodes[self.node_name]
socket = node.inputs[self.socket_name]
node.inputs.remove(socket)
return {"FINISHED"}

35
korman/properties/modifiers/region.py

@ -19,6 +19,41 @@ from PyHSPlasma import *
from .base import PlasmaModifierProperties from .base import PlasmaModifierProperties
footstep_surface_ids = {
"dirt": 0,
# 1 = NULL
"puddle": 2,
# 3 = tile (NULL in MOUL)
"metal": 4,
"woodbridge": 5,
"rope": 6,
"grass": 7,
# 8 = NULL
"woodfloor": 9,
"rug": 10,
"stone": 11,
# 12 = NULL
# 13 = metal ladder (dupe of metal)
"woodladder": 14,
"water": 15,
# 16 = maintainer's glass (NULL in PotS)
# 17 = maintainer's metal grating (NULL in PotS)
# 18 = swimming (why would you want this?)
}
footstep_surfaces = [("dirt", "Dirt", "Dirt"),
("grass", "Grass", "Grass"),
("metal", "Metal", "Metal Catwalk"),
("puddle", "Puddle", "Shallow Water"),
("rope", "Rope", "Rope Ladder"),
("rug", "Rug", "Carpet Rug"),
("stone", "Stone", "Stone Tile"),
("water", "Water", "Deep Water"),
("woodbridge", "Wood Bridge", "Wood Bridge"),
("woodfloor", "Wood Floor", "Wood Floor"),
("woodladder", "Wood Ladder", "Wood Ladder")]
class PlasmaPanicLinkRegion(PlasmaModifierProperties): class PlasmaPanicLinkRegion(PlasmaModifierProperties):
pl_id = "paniclink" pl_id = "paniclink"

Loading…
Cancel
Save