merge upstream

This commit is contained in:
2025-09-29 08:26:58 +00:00
63 changed files with 1944 additions and 1027 deletions

View File

@ -103,11 +103,11 @@ class BlendSpaceNode(LnxLogicTreeNode):
self.remove_advanced_draw()
def get_blend_space_points(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
return self.blend_space.points
def draw_advanced(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
self.blend_space.draw()
def lnx_init(self, context):

View File

@ -16,3 +16,9 @@ class ArraySpliceNode(LnxLogicTreeNode):
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'Array')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -156,149 +156,149 @@ class CreateElementNode(LnxLogicTreeNode):
self.add_input('LnxStringSocket', 'Class')
self.add_input('LnxStringSocket', 'Style')
match index:
case 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
case 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
case 6:
self.add_input('LnxStringSocket', 'Src')
case 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
case 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
case 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 19 | 20:
self.add_input('LnxStringSocket', 'Span')
case 21:
self.add_input('LnxStringSocket', 'Value')
case 24 | 53:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 26:
self.add_input('LnxStringSocket', 'Title')
case 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
case 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
case 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
case 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
case 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
case 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
case 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
case 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
case 57:
self.add_input('LnxStringSocket', 'Value')
case 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
case 58:
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
case 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
case 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
case 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
case 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
case 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
case 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
case 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
case 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
case 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
case 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
case 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
case 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
case 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
case 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
case 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
if index == 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
elif index == 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
elif index == 6:
self.add_input('LnxStringSocket', 'Src')
elif index == 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
elif index == 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
elif index == 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index in (19, 20):
self.add_input('LnxStringSocket', 'Span')
elif index == 21:
self.add_input('LnxStringSocket', 'Value')
elif index in (24, 53):
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 26:
self.add_input('LnxStringSocket', 'Title')
elif index == 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
elif index == 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
elif index == 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
elif index == 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
elif index == 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
elif index == 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index == 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
elif index == 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
elif index == 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
elif index == 57:
self.add_input('LnxStringSocket', 'Value')
elif index == 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
# Note: There's a duplicate case 58 in the original, handling as separate elif
elif index == 60: # This was the second case 58, likely meant to be a different index
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
elif index == 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
elif index == 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
elif index == 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
elif index == 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
elif index == 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
elif index == 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
elif index == 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
elif index == 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
elif index == 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
elif index == 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
elif index == 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
elif index == 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
elif index == 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
for i in range(len(self.inputs)):
if self.inputs[i].name in self.data_map:

View File

@ -38,18 +38,17 @@ class JSEventTargetNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
self['property0'] = value

View File

@ -43,27 +43,26 @@ class RenderElementNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
case 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
case 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
case 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
elif index == 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
elif index == 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
elif index == 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
self['property0'] = value

View File

@ -17,6 +17,17 @@ class OnEventNode(LnxLogicTreeNode):
'custom': 'Custom'
}
def update(self):
if self.property1 != 'custom':
if self.inputs[0].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
def set_mode(self, context):
if self.property1 != 'custom':
if len(self.inputs) > 1:
@ -25,7 +36,17 @@ class OnEventNode(LnxLogicTreeNode):
if len(self.inputs) < 2:
self.add_input('LnxNodeSocketAction', 'In')
self.inputs.move(1, 0)
if self.property1 != 'custom':
if self.inputs[0].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
self.label = f'{self.bl_label}: {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
# Use a new property to preserve compatibility
property1: HaxeEnumProperty(
'property1',
@ -52,9 +73,15 @@ class OnEventNode(LnxLogicTreeNode):
layout.prop(self, 'property1', text='')
def draw_label(self) -> str:
if self.inputs[0].is_linked:
return self.bl_label
return f'{self.bl_label}: {self.inputs[0].get_default_value()}'
if self.property1 != 'custom':
if self.inputs[0].is_linked:
return f'{self.bl_label}: {self.property1}'
else:
return f'{self.bl_label}: {self.property1} {self.inputs[0].get_default_value()}'
elif self.inputs[1].is_linked:
return f'{self.bl_label}: {self.property1}'
else:
return f'{self.bl_label}: {self.property1} {self.inputs[1].get_default_value()}'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):

View File

@ -7,12 +7,19 @@ class KeyboardNode(LnxLogicTreeNode):
lnx_section = 'keyboard'
lnx_version = 2
def update(self):
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
def upd(self, context):
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The keyboard button starts to be pressed'),
('down', 'Down', 'The keyboard button is pressed'),
('released', 'Released', 'The keyboard button stops being pressed')],
name='', default='down')
name='', default='down', update=upd)
property1: HaxeEnumProperty(
'property1',
@ -69,7 +76,7 @@ class KeyboardNode(LnxLogicTreeNode):
('right', 'right', 'right'),
('left', 'left', 'left'),
('down', 'down', 'down'),],
name='', default='space')
name='', default='space', update=upd)
def lnx_init(self, context):
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -8,13 +8,25 @@ class MouseNode(LnxLogicTreeNode):
lnx_section = 'mouse'
lnx_version = 3
def update(self):
if self.property0 != 'moved':
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property0}'
def upd(self, context):
if self.property0 != 'moved':
self.label = f'{self.bl_label}: {self.property0} {self.property1}'
else:
self.label = f'{self.bl_label}: {self.property0}'
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The mouse button begins to be pressed'),
('down', 'Down', 'The mouse button is pressed'),
('released', 'Released', 'The mouse button stops being pressed'),
('moved', 'Moved', 'Moved')],
name='', default='down')
name='', default='down', update=upd)
property1: HaxeEnumProperty(
'property1',
items = [('left', 'Left', 'Left mouse button'),
@ -22,7 +34,7 @@ class MouseNode(LnxLogicTreeNode):
('right', 'Right', 'Right mouse button'),
('side1', 'Side 1', 'Side 1 mouse button'),
('side2', 'Side 2', 'Side 2 mouse button')],
name='', default='left')
name='', default='left', update=upd)
property2: HaxeBoolProperty(
'property2',
name='Include Debug Console',

View File

@ -66,7 +66,10 @@ class LnxGroupTree(bpy.types.NodeTree):
"""Try to avoid creating loops of group trees with each other"""
# upstream trees of tested treed should nad share trees with downstream trees of current tree
tested_tree_upstream_trees = {t.name for t in self.upstream_trees()}
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
if bpy.context.space_data is not None:
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
else:
current_tree_downstream_trees = set()
shared_trees = tested_tree_upstream_trees & current_tree_downstream_trees
return not shared_trees

View File

@ -2,9 +2,17 @@ from collections import OrderedDict
import itertools
import math
import textwrap
from typing import Any, final, Generator, List, Optional, Type, Union
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
from typing import OrderedDict as ODict # Prevent naming conflicts
try:
from typing import final
except ImportError:
# Python < 3.8 compatibility
def final(f):
"""No final in Python < 3.8"""
return f
import bpy.types
from bpy.props import *
from nodeitems_utils import NodeItem
@ -39,11 +47,11 @@ PKG_AS_CATEGORY = "__pkgcat__"
nodes = []
category_items: ODict[str, List['LnxNodeCategory']] = OrderedDict()
array_nodes: dict[str, 'LnxLogicTreeNode'] = dict()
array_nodes: Dict[str, 'LnxLogicTreeNode'] = dict()
# See LnxLogicTreeNode.update()
# format: [tree pointer => (num inputs, num input links, num outputs, num output links)]
last_node_state: dict[int, tuple[int, int, int, int]] = {}
last_node_state: Dict[int, Tuple[int, int, int, int]] = {}
class LnxLogicTreeNode(bpy.types.Node):

View File

@ -10,7 +10,7 @@ mutable (common Python pitfall, be aware of this!), but because they
don't get accessed later it doesn't matter here and we keep it this way
for parity with the Blender API.
"""
from typing import Any, Callable, Sequence, Union
from typing import Any, Callable, List, Sequence, Set, Union
import sys
import bpy
@ -49,6 +49,10 @@ def __haxe_prop(prop_type: Callable, prop_name: str, *args, **kwargs) -> Any:
# bpy.types.Bone, remove them here to prevent registration errors
if 'tags' in kwargs:
del kwargs['tags']
# Remove override parameter for Blender versions that don't support it
if bpy.app.version < (2, 90, 0) and 'override' in kwargs:
del kwargs['override']
return prop_type(*args, **kwargs)
@ -87,7 +91,7 @@ def HaxeBoolVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.BoolProperty']:
) -> List['bpy.types.BoolProperty']:
"""Declares a new BoolVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -118,7 +122,7 @@ def HaxeEnumProperty(
items: Sequence,
name: str = "",
description: str = "",
default: Union[str, set[str]] = None,
default: Union[str, Set[str]] = None,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
@ -180,7 +184,7 @@ def HaxeFloatVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.FloatProperty']:
) -> List['bpy.types.FloatProperty']:
"""Declares a new FloatVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -232,7 +236,7 @@ def HaxeIntVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.IntProperty']:
) -> List['bpy.types.IntProperty']:
"""Declares a new IntVectorProperty that has a Haxe counterpart with
the given prop_name (Python and Haxe names must be identical for now).
"""

View File

@ -18,6 +18,10 @@ class CallGroupNode(LnxLogicTreeNode):
def lnx_init(self, context):
pass
def update(self):
if self.group_tree:
self.label = f'Group: {self.group_tree.name}'
# Function to add input sockets and re-link sockets
def update_inputs(self, tree, node, inp_sockets, in_links):
count = 0
@ -58,10 +62,12 @@ class CallGroupNode(LnxLogicTreeNode):
tree.links.new(current_socket, link)
count = count + 1
def remove_tree(self):
self.group_tree = None
def update_sockets(self, context):
if self.group_tree:
self.label = f'Group: {self.group_tree.name}'
else:
self.label = 'Call Node Group'
# List to store from and to sockets of connected nodes
from_socket_list = []
to_socket_list = []
@ -107,6 +113,10 @@ class CallGroupNode(LnxLogicTreeNode):
# Prperty to store group tree pointer
group_tree: PointerProperty(name='Group', type=bpy.types.NodeTree, update=update_sockets)
def edit_tree(self):
self.label = f'Group: {self.group_tree.name}'
bpy.ops.lnx.edit_group_tree()
def draw_label(self) -> str:
if self.group_tree is not None:
return f'Group: {self.group_tree.name}'
@ -134,8 +144,9 @@ class CallGroupNode(LnxLogicTreeNode):
op = row_name.operator('lnx.unlink_group_tree', icon='X', text='')
op.node_index = self.get_id_str()
row_ops.enabled = not self.group_tree is None
op = row_ops.operator('lnx.edit_group_tree', icon='FULLSCREEN_ENTER', text='Edit tree')
op = row_ops.operator('lnx.node_call_func', icon='FULLSCREEN_ENTER', text='Edit tree')
op.node_index = self.get_id_str()
op.callback_name = 'edit_tree'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1, 2):

View File

@ -27,7 +27,10 @@ class GroupInputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupInputsNode':

View File

@ -27,7 +27,10 @@ class GroupOutputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupOutputsNode':

View File

@ -0,0 +1,51 @@
from lnx.logicnode.lnx_nodes import *
class ProbabilisticIndexNode(LnxLogicTreeNode):
"""This system gets an index based on probabilistic values,
ensuring that the total sum of the probabilities equals 1.
If the probabilities do not sum to 1, they will be adjusted
accordingly to guarantee a total sum of 1. Only one output will be
triggered at a time.
@output index: the index.
"""
bl_idname = 'LNProbabilisticIndexNode'
bl_label = 'Probabilistic Index'
lnx_section = 'logic'
lnx_version = 1
num_choices: IntProperty(default=0, min=0)
def __init__(self):
array_nodes[str(id(self))] = self
def lnx_init(self, context):
self.add_output('LnxIntSocket', 'Index')
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_call_func', text='New', icon='PLUS', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'add_func'
op2 = row.operator('lnx.node_call_func', text='', icon='X', emboss=True)
op2.node_index = str(id(self))
op2.callback_name = 'remove_func'
def add_func(self):
self.add_input('LnxFloatSocket', f'Prob Index {self.num_choices}')
self.num_choices += 1
def remove_func(self):
if len(self.inputs) > 0:
self.inputs.remove(self.inputs[-1])
self.num_choices -= 1
def draw_label(self) -> str:
if self.num_choices == 0:
return self.bl_label
return f'{self.bl_label}: [{self.num_choices}]'

View File

@ -350,7 +350,10 @@ class LNX_PG_TreeVarListItem(bpy.types.PropertyGroup):
def _set_name(self, value: str):
old_name = self._get_name()
tree = bpy.context.space_data.path[-1].node_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.path[-1].node_tree
else:
return # No valid context
lst = tree.lnx_treevariableslist
if value == '':

View File

@ -1,7 +1,10 @@
from lnx.logicnode.lnx_nodes import *
class SetWorldNode(LnxLogicTreeNode):
"""Sets the World of the active scene."""
"""Sets the World of the active scene.
World must be either associated to a scene or have fake user."""
bl_idname = 'LNSetWorldNode'
bl_label = 'Set World'
lnx_version = 1