From 92a9e91554e024b9ae83319e1ea46b8d04abfb15 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 10 Mar 2021 16:29:17 +0100 Subject: [PATCH] Add Globals UI --- __init__.py | 21 ++- basicnodes/__init__.py | 222 +++++++++++++++++++++++++--- game/bgelogic.py | 44 +++++- ops/__init__.py | 171 ++++++++++++++++++++- templates/prefabs/4keymovement.json | 52 ++++--- ui/__init__.py | 146 ++++++++++++++++++ utilities/__init__.py | 24 +++ 7 files changed, 624 insertions(+), 56 deletions(-) diff --git a/__init__.py b/__init__.py index c3382b0..664bfc2 100644 --- a/__init__.py +++ b/__init__.py @@ -405,8 +405,8 @@ class LogicNodesAddonPreferences(bpy.types.AddonPreferences): def draw(self, context): layout = self.layout - col = layout.column() main_row = layout.row() + col = layout.column() debug_col = main_row.column() ui_col = main_row.column() code_col = main_row.column() @@ -436,8 +436,9 @@ def draw(self, context): text="Generate Code after editing (Slow)." ) col.separator() - link_row = col.row() + link_row = col.row(align=True) link_row.operator("bge_netlogic.github", icon="URL") + link_row.operator("bge_netlogic.update_tree_version", icon='PLUGIN') link_row.operator("bge_netlogic.donate", icon="FUND") contrib_row = col.row() contrib_row.label(text='Contributors: L_P, Mike King') @@ -461,6 +462,7 @@ def draw(self, context): ops.NLMakeGroupOperator, ops.NLLoadSoundOperator, ops.NLSwitchInitialNetworkStatusOperator, + ops.NLUpdateTreeVersionOperator, ops.NLAddPropertyOperator, ops.NLAddComponentOperator, ops.NLRemovePropertyOperator, @@ -471,6 +473,10 @@ def draw(self, context): ops.NLBGEDocsButton, ops.NLUPBGEDocsButton, ops.NLDocsButton, + ops.NLAddGlobalOperator, + ops.NLRemoveGlobalOperator, + ops.NLAddGlobalCatOperator, + ops.NLRemoveGlobalCatOperator, NLNodeTreeReference ] @@ -483,8 +489,13 @@ def draw(self, context): LogicNodesAddonPreferences, ui.BGEPropFilter, ui.BGEGroupName, + ui.BGEGlobalValue, + ui.BGEGlobalValueCategory, + ui.NL_UL_glcategory, + ui.NL_UL_glvalue, ui.BGE_PT_LogicPanel, ui.BGE_PT_LogicTreeInfoPanel, + ui.BGE_PT_GlobalValuePanel, ui.BGE_PT_GamePropertyPanel, ui.BGE_PT_HelpPanel, ui.BGE_PT_LogicNodeSettingsObject, @@ -605,6 +616,12 @@ def register(): bpy.types.Scene.logic_node_settings = bpy.props.PointerProperty( type=NLAddonSettings ) + bpy.types.Scene.nl_global_categories = bpy.props.CollectionProperty( + type=ui.BGEGlobalValueCategory + ) + bpy.types.Scene.nl_global_cat_selected = bpy.props.IntProperty( + name='Category' + ) # blender add-on unregistration callback diff --git a/basicnodes/__init__.py b/basicnodes/__init__.py index 5a93fcd..8311a7b 100644 --- a/basicnodes/__init__.py +++ b/basicnodes/__init__.py @@ -500,7 +500,6 @@ def filter_armatures(self, item): def filter_logic_trees(self, item): - print(item) if ( isinstance(item, bge_netlogic.ui.BGELogicTree) ): @@ -544,21 +543,21 @@ def parse_field_value(value_type, value): def update_tree_code(self, context): utils.set_compile_status(utils.TREE_MODIFIED) + if utils.is_compile_status(utils.TREE_COMPILED_ALL): + return + if not hasattr(context.space_data, 'edit_tree'): + return tree = context.space_data.edit_tree for node in tree.nodes: if isinstance(node, NetLogicStatementGenerator) and not node.hide: try: node.update_draw() - except Exception as e: - print(e) + except Exception: + pass if not getattr(bpy.context.scene.logic_node_settings, 'auto_compile'): return bge_netlogic.update_current_tree_code() - if not hasattr(context.space_data, 'edit_tree'): - utils.warn('Wrong editor context! Skipping nodesocket updates.') - return - def socket_field(s): return parse_field_value(s.value_type, s.value) @@ -1162,10 +1161,10 @@ def draw(self, context, layout, node, text): if self.name: row = col.row() row.label(text=self.name) - if game_object: + if game_object or game_obj_socket.is_linked: # game_object = bpy.data.objects[game_object.split('NLO:')[-1]] - game = game_object.game if not game_obj_socket.is_linked: + game = game_object.game col.prop_search( self, 'value', @@ -1601,6 +1600,81 @@ def get_unlinked_value(self): _sockets.append(NLSoundFileSocket) +############################################################################### +# String Pointer Sockets +############################################################################### + + +class NLGlobalCatSocket(bpy.types.NodeSocket, NetLogicSocketType): + bl_idname = "NLGlobalCatSocket" + bl_label = "Category" + value: bpy.props.StringProperty( + update=update_tree_code + ) + + def draw_color(self, context, node): + return PARAMETER_SOCKET_COLOR + + def draw(self, context, layout, node, text): + if self.is_linked or self.is_output: + layout.label(text=text) + else: + col = layout.column() + col.prop_search( + self, + "value", + context.scene, + 'nl_global_categories', + icon='OUTLINER_COLLECTION', + text='' + ) + + def get_unlinked_value(self): + return '"{}"'.format(self.value) + + +_sockets.append(NLGlobalCatSocket) + + +class NLGlobalPropSocket(bpy.types.NodeSocket, NetLogicSocketType): + bl_idname = "NLGlobalPropSocket" + bl_label = "Category" + value: bpy.props.StringProperty( + update=update_tree_code + ) + ref_index: bpy.props.IntProperty( + update=update_tree_code + ) + + def draw_color(self, context, node): + return PARAMETER_SOCKET_COLOR + + def draw(self, context, layout, node, text): + if self.is_linked or self.is_output: + layout.label(text=text) + else: + col = layout.column() + ref_socket = self.node.inputs[self.ref_index] + if ref_socket.is_linked: + col.prop(self, 'value', text='') + else: + cat = context.scene.nl_global_categories[ref_socket.value] + col.prop_search( + self, + "value", + cat, + 'content', + icon='DOT', + text='' + ) + + def get_unlinked_value(self): + return '"{}"'.format(self.value) + + +_sockets.append(NLGlobalPropSocket) + + ############################################################################### # Value Sockets ############################################################################### @@ -2271,6 +2345,97 @@ def draw(self, context, layout, node, text): _sockets.append(NLValueFieldSocket) +class NLOptionalValueFieldSocket(bpy.types.NodeSocket, NetLogicSocketType): + bl_idname = "NLOptionalValueFieldSocket" + bl_label = "Value" + value: bpy.props.StringProperty(update=update_tree_code) + + def on_type_changed(self, context): + if self.value_type == "BOOLEAN": + self.value = str(self.bool_editor) + if self.value_type == "STRING": + self.value = str(self.string_editor) + if self.value_type == "FILE_PATH": + self.value = str(self.path_editor) + update_tree_code(self, context) + + value_type: bpy.props.EnumProperty( + items=_enum_field_value_types, + update=on_type_changed + ) + + use_value: bpy.props.BoolProperty( + update=update_tree_code + ) + + def store_boolean_value(self, context): + self.value = str(self.bool_editor) + update_tree_code(self, context) + + bool_editor: bpy.props.BoolProperty(update=store_boolean_value) + + def store_int_value(self, context): + self.value = str(self.int_editor) + + int_editor: bpy.props.IntProperty(update=store_int_value) + + def store_float_value(self, context): + self.value = str(self.float_editor) + + float_editor: bpy.props.FloatProperty(update=store_float_value) + + def store_string_value(self, context): + self.value = self.string_editor + + string_editor: bpy.props.StringProperty(update=store_string_value) + + def store_path_value(self, context): + self.value = self.path_editor + + path_editor: bpy.props.StringProperty( + update=store_path_value, + subtype='FILE_PATH' + ) + + def draw_color(self, context, node): + return PARAMETER_SOCKET_COLOR + + def get_unlinked_value(self): + return socket_field(self) if self.use_value else None + + def draw(self, context, layout, node, text): + if self.is_linked or self.is_output: + layout.label(text=text) + else: + col = layout.column() + if text: + name_row = col.row() + name_row.label(text=text) + name_row.prop(self, "use_value", text="") + if not self.use_value: + return + val_line = col.row() + val_row = val_line.split() + if self.value_type == "BOOLEAN": + val_row.prop(self, "value_type", text="") + val_row.prop(self, "bool_editor", text="") + elif self.value_type == "INTEGER": + val_row.prop(self, "value_type", text="") + val_row.prop(self, "int_editor", text="") + elif self.value_type == "FLOAT": + val_row.prop(self, "value_type", text="") + val_row.prop(self, "float_editor", text="") + elif self.value_type == "STRING": + val_row.prop(self, "value_type", text="") + val_row.prop(self, "string_editor", text="") + elif self.value_type == "FILE_PATH": + val_row.prop(self, "value_type", text="") + val_row.prop(self, "path_editor", text="") + + +_sockets.append(NLOptionalValueFieldSocket) + + class NLNumericFieldSocket(bpy.types.NodeSocket, NetLogicSocketType): bl_idname = "NLNumericFieldSocket" bl_label = "Value" @@ -6029,16 +6194,24 @@ def draw_buttons(self, context, layout): def init(self, context): NLConditionNode.init(self, context) - self.inputs.new(NLPositiveFloatSocket.bl_idname, "Threshold") self.inputs.new(NLValueFieldSocket.bl_idname, "") self.inputs.new(NLValueFieldSocket.bl_idname, "") + self.inputs.new(NLPositiveFloatSocket.bl_idname, "Threshold") self.outputs.new(NLConditionSocket.bl_idname, "If True") + def update_draw(self): + numerics = ['INTEGER', 'FLOAT'] + self.inputs[2].enabled = ( + self.inputs[0].value_type in numerics or self.inputs[0].is_linked + and + self.inputs[1].value_type in numerics or self.inputs[1].is_linked + ) + def get_netlogic_class_name(self): return "bgelogic.ConditionLogicOp" def get_input_sockets_field_names(self): - return ['threshold', "param_a", "param_b"] + return ["param_a", "param_b", 'threshold'] def init_cell_fields(self, cell_varname, uids, line_writer): NetLogicStatementGenerator.init_cell_fields( @@ -10036,13 +10209,15 @@ class NLParameterGetGlobalValue(bpy.types.Node, NLParameterNode): def init(self, context): NLParameterNode.init(self, context) - self.inputs.new(NLQuotedStringFieldSocket.bl_idname, "Name") - self.inputs[-1].value = 'general' - self.inputs.new(NLQuotedStringFieldSocket.bl_idname, "Key") - self.inputs[-1].value = 'var_name' - self.inputs.new(NLValueFieldSocket.bl_idname, "Default Value") + self.inputs.new(NLGlobalCatSocket.bl_idname, "Category") + self.inputs.new(NLGlobalPropSocket.bl_idname, "Property") + self.inputs[-1].ref_index = 0 + self.inputs.new(NLOptionalValueFieldSocket.bl_idname, "Default Value") self.outputs.new(NLParameterSocket.bl_idname, "Value") + def update_draw(self): + self.inputs[1].enabled = True if self.inputs[0].value or self.inputs[0].is_linked else False + def get_input_sockets_field_names(self): return ["data_id", "key", 'default'] @@ -10062,19 +10237,22 @@ class NLActionSetGlobalValue(bpy.types.Node, NLActionNode): def init(self, context): NLActionNode.init(self, context) self.inputs.new(NLPseudoConditionSocket.bl_idname, "Condition") - self.inputs.new(NLQuotedStringFieldSocket.bl_idname, "Name") - self.inputs[-1].value = 'general' - self.inputs.new(NLBooleanSocket.bl_idname, "Persistent") - self.inputs.new(NLQuotedStringFieldSocket.bl_idname, "Key") - self.inputs[-1].value = 'var_name' + self.inputs.new(NLGlobalCatSocket.bl_idname, "Category") + self.inputs.new(NLGlobalPropSocket.bl_idname, "Property") + self.inputs[-1].ref_index = 1 self.inputs.new(NLValueFieldSocket.bl_idname, "") + self.inputs.new(NLBooleanSocket.bl_idname, "Persistent") self.outputs.new(NLConditionSocket.bl_idname, 'Done') + def update_draw(self): + self.inputs[2].enabled = True if self.inputs[1].value or self.inputs[1].is_linked else False + self.inputs[3].enabled = self.inputs[4].enabled = self.inputs[2].enabled + def get_output_socket_varnames(self): return ["OUT"] def get_input_sockets_field_names(self): - return ["condition", "data_id", "persistent", "key", "value"] + return ["condition", "data_id", "key", "value", 'persistent'] def get_netlogic_class_name(self): return "bgelogic.ActionSetGlobalValue" diff --git a/game/bgelogic.py b/game/bgelogic.py index c7121b1..561a9b9 100644 --- a/game/bgelogic.py +++ b/game/bgelogic.py @@ -73,7 +73,7 @@ def write(self, value, line_writer): raise NotImplementedError() serializers = {} - storage_dir = logic.expandPath("//bgelogic/storage") + storage_dir = logic.expandPath("//Globals") shared_dbs = {} @classmethod @@ -160,6 +160,10 @@ def compress(cls, fname, data): def __init__(self, file_name): self.fname = file_name self.data = {} + + filter(lambda a: a is not remove_globals, bpy.app.handlers.game_post) + bpy.app.handlers.game_post.append(remove_globals) + log_size = SimpleLoggingDatabase.read(self.fname, self.data) if log_size > (5 * len(self.data)): debug("Compressing sld {}".format(file_name)) @@ -502,6 +506,12 @@ def stop_all_sounds(a, b): delattr(bpy.types.Scene, 'nl_aud_system') +def remove_globals(a, b): + if not hasattr(bpy.types.Scene, 'nl_globals_initialized'): + return + delattr(bpy.types.Scene, 'nl_globals_initialized') + + def check_game_object(query, scene=None): if not scene: scene = logic.getCurrentScene() @@ -608,7 +618,6 @@ def __init__(self): self.device3D.doppler_factor = bpy.context.scene.audio_doppler_factor filter(lambda a: a is not stop_all_sounds, bpy.app.handlers.game_post) - # bpy.app.handlers.game_post.clear() bpy.app.handlers.game_post.append(stop_all_sounds) def get_distance_model(self, name): @@ -782,6 +791,7 @@ def __init__(self): self.mouse_motion_delta = [0.0, 0.0] self.mouse_wheel_delta = 0 self.aud_system_owner = False + self.init_glob_cats() self.audio_system = self.create_aud_system() self.sub_networks = [] # a list of networks updated by this network self.capslock_pressed = False @@ -794,6 +804,32 @@ def create_aud_system(self): else: return bpy.types.Scene.nl_aud_system + def init_glob_cats(self): + if not hasattr(bpy.types.Scene, 'nl_globals_initialized'): + scene = bge.logic.getCurrentScene() + cats = bpy.data.scenes[scene.name].nl_global_categories + + msg = '' + + dat = { + 'STRING': 'string_val', + 'FLOAT': 'float_val', + 'INTEGER': 'int_val', + 'BOOLEAN': 'bool_val', + 'FILE_PATH': 'filepath_val' + } + + for c in cats: + db = SimpleLoggingDatabase.get_or_create_shared_db(c.name) + msg += f' {c.name},' + for v in c.content: + val = v.get(dat.get(v.value_type, 'FLOAT')) + print(val) + db.put(v.name, val, v.persistent) + + debug(f'Globals Initialized:{msg[:-1]}') + bpy.types.Scene.nl_globals_initialized = True + def ray_cast( self, caster_object, @@ -4751,7 +4787,7 @@ def evaluate(self): self.done = False condition = self.get_parameter_value(self.condition) game_object = self.get_parameter_value(self.game_object) - slot = self.get_parameter_value(self.slot) + slot = self.get_parameter_value(self.slot) - 1 mat_name = self.get_parameter_value(self.mat_name) if not_met(condition): return @@ -8599,9 +8635,9 @@ def __init__(self): ActionCell.__init__(self) self.condition = None self.data_id = None - self.persistent = None self.key = None self.value = None + self.persistent = None self.done = None self.OUT = LogicNetworkSubCell(self, self.get_done) diff --git a/ops/__init__.py b/ops/__init__.py index 084c43e..475ca33 100644 --- a/ops/__init__.py +++ b/ops/__init__.py @@ -323,6 +323,65 @@ def remove_tree_from_object_pcoll(self, ob, treename): ob.bgelogic_treelist.remove(index) +class NLUpdateTreeVersionOperator(bpy.types.Operator): + bl_idname = "bge_netlogic.update_tree_version" + bl_label = "Update Trees" + bl_description = "Update trees to the current version of the Addon" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + for tree in bpy.data.node_groups: + if tree.bl_idname == bge_netlogic.ui.BGELogicTree.bl_idname: + for node in tree.nodes: + if node.bl_idname == 'NLConditionLogicOperation': + self.update_compare_node(tree, node) + return {'FINISHED'} + + def update_compare_node(self, tree, node): + if node.inputs[0].bl_idname != 'NLPositiveFloatSocket': + return + replacer = tree.nodes.new('NLConditionLogicOperation') + replacer.location = node.location + replacer.label = node.label + replacer.operator = node.operator + replacer.inputs[0].value_type = node.inputs[1].value_type + replacer.inputs[0].value = node.inputs[1].value + replacer.inputs[1].value_type = node.inputs[2].value_type + replacer.inputs[1].value = node.inputs[2].value + + if node.inputs[0].is_linked: + link = node.inputs[0].links[0] + tree.links.new( + link.from_socket, + replacer.inputs[2] + ) + if node.inputs[1].is_linked: + link = node.inputs[1].links[0] + tree.links.new( + link.from_socket, + replacer.inputs[0] + ) + if node.inputs[2].is_linked: + link = node.inputs[2].links[0] + tree.links.new( + link.from_socket, + replacer.inputs[1] + ) + idx = 0 + for o in node.outputs: + if o.is_linked: + for link in o.links: + tree.links.new( + replacer.outputs[idx], + link.to_socket + ) + idx += 1 + tree.nodes.remove(node) + + class NLMakeGroupOperator(bpy.types.Operator): bl_idname = "bge_netlogic.make_group" bl_label = "Pack Into New Tree" @@ -349,6 +408,7 @@ def group_make(self, group_name, add_nodes): 'game_object', 'default_value', 'use_toggle', + 'use_value', 'true_label', 'false_label', 'value_type', @@ -372,6 +432,7 @@ def group_make(self, group_name, add_nodes): 'pulse', 'hide', 'label', + 'ref_index', 'use_owner', 'advanced' ] @@ -524,7 +585,9 @@ def get_template_path(self): def execute(self, context): utils.debug('Adding template...') tree = context.space_data.edit_tree - content = json.load(open(self.get_template_path()))['nodes'] + jf = json.load(open(self.get_template_path())) + globs = jf['globals'] + content = jf['nodes'] if tree is None: utils.error('Cannot add template! Aborting...') @@ -532,6 +595,18 @@ def execute(self, context): for node in tree.nodes: node.select = False + for g in globs.keys(): + db = context.scene.nl_global_categories + if g not in db: + cat = db.add() + cat.name = g + for v in globs[g].keys(): + if v not in db[g].content: + val = db[g].content.add() + val.name = v + for entry in globs[g][v]: + setattr(val, entry, globs[g][v][entry]) + nodes = [] for c in content: self.add_node( @@ -664,13 +739,13 @@ def _setup_logic_bricks_for_object( ) controller = game_settings.controllers[-1] controller.show_expanded = False - if 'NL_OR' not in game_settings.controllers: - bpy.ops.logic.controller_add( - type="LOGIC_OR", - object=obj.name, - name='NL_OR' - ) - game_settings.controllers[-1].show_expanded = False + # if 'NL_OR' not in game_settings.controllers: + # bpy.ops.logic.controller_add( + # type="LOGIC_OR", + # object=obj.name, + # name='NL_OR' + # ) + # game_settings.controllers[-1].show_expanded = False controller.name = controller_name controller.type = "PYTHON" controller.mode = "MODULE" @@ -825,6 +900,86 @@ def execute(self, context): return {"FINISHED"} +class NLAddGlobalOperator(bpy.types.Operator): + bl_idname = "bge_netlogic.add_global" + bl_label = "Add Global" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Add a value accessible from anywhere" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + category = utils.get_global_category() + prop = category.content.add() + prop.name = 'prop' + prop.string_val = '' + prop.float_val = 0.0 + prop.int_val = 0 + prop.bool_val = False + prop.filepath_val = '' + category.selected = len(category.content) - 1 + + return {'FINISHED'} + + +class NLRemoveGlobalOperator(bpy.types.Operator): + bl_idname = "bge_netlogic.remove_global" + bl_label = "Remove Global" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Remove a value accessible from anywhere" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + category = utils.get_global_category() + category.content.remove(category.selected) + if category.selected > len(category.content) - 1: + category.selected = len(category.content) - 1 + return {'FINISHED'} + + +class NLAddGlobalCatOperator(bpy.types.Operator): + bl_idname = "bge_netlogic.add_global_cat" + bl_label = "Add Global" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Add a global value category" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + cats = scene.nl_global_categories + cat = cats.add() + cat.name = 'category' + scene.nl_global_cat_selected = len(scene.nl_global_categories) - 1 + + return {'FINISHED'} + + +class NLRemoveGlobalCatOperator(bpy.types.Operator): + bl_idname = "bge_netlogic.remove_global_cat" + bl_label = "Remove Global" + bl_options = {'REGISTER', 'UNDO'} + bl_description = "Remove a global value category" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + scene.nl_global_categories.remove(scene.nl_global_cat_selected) + if scene.nl_global_cat_selected > len(scene.nl_global_categories) - 1: + scene.nl_global_cat_selected = len(scene.nl_global_categories) - 1 + return {'FINISHED'} + + class NLLoadSoundOperator(bpy.types.Operator, ImportHelper): bl_idname = "bge_netlogic.load_sound" bl_label = "Load Sound" diff --git a/templates/prefabs/4keymovement.json b/templates/prefabs/4keymovement.json index c3d87ad..398e0ce 100644 --- a/templates/prefabs/4keymovement.json +++ b/templates/prefabs/4keymovement.json @@ -1,4 +1,16 @@ { + "globals": { + "movement": { + "x": { + "value_type": "FLOAT", + "float_val": 0 + }, + "y": { + "value_type": "FLOAT", + "float_val": 0 + } + } + }, "nodes": [ { "x": 0, @@ -71,14 +83,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "x" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": 1 } @@ -94,14 +106,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "x" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": -1 } @@ -117,14 +129,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "y" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": 1 } @@ -140,14 +152,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "y" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": -1 } @@ -191,14 +203,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "x" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": 0 } @@ -214,14 +226,14 @@ "index": 1, "value": "movement" }, { - "index": 3, + "index": 2, "value": "y" }, { - "index": 4, + "index": 3, "type": "value_type", "value": "INTEGER" }, { - "index": 4, + "index": 3, "type": "int_editor", "value": 0 } @@ -278,8 +290,8 @@ "links": [], "values": [ { - "index": 0, - "value": 5 + "value": 5, + "index": 0 } ] }, { diff --git a/ui/__init__.py b/ui/__init__.py index 8077c07..27504c3 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -57,6 +57,152 @@ class BGEGroupName(bpy.types.PropertyGroup): enabled: bpy.props.BoolProperty() +def check_double_prop(self, context): + category = utils.get_global_category() + props = category.content + check_double_name(self, props) + + +def check_double_cat(self, context): + cats = bpy.context.scene.nl_global_categories + check_double_name(self, cats) + + +def check_double_name(self, data): + name = base = self.name + names = [] + for p in data: + if p != self: + names.append(p.name) + if base in names: + count = 1 + name = f'{base}{count}' + while name in names: + count += 1 + name = f'{base}{count}' + self.name = name + + +_enum_value_types = [ + ("FLOAT", "Float", "A Float value"), + ("STRING", "String", "A String"), + ("INTEGER", "Integer", "An Integer value"), + ("BOOLEAN", "Bool", "A True/False value"), + ("FILE_PATH", "File Path", 'Choose a file path') +] + + +class BGEGlobalValue(bpy.types.PropertyGroup): + name: bpy.props.StringProperty(name='Name', default='prop', update=check_double_prop) + value_type: bpy.props.EnumProperty(items=_enum_value_types, name='Value Types') + string_val: bpy.props.StringProperty(name='String') + float_val: bpy.props.FloatProperty(name='Floating Point') + int_val: bpy.props.IntProperty(name='Integer') + bool_val: bpy.props.BoolProperty(name='Boolean') + filepath_val: bpy.props.StringProperty(name='File Path', subtype='FILE_PATH') + persistent: bpy.props.BoolProperty(name='Persistent') + + +class BGEGlobalValueCategory(bpy.types.PropertyGroup): + name: bpy.props.StringProperty(name='Name', default='category', update=check_double_cat) + content: bpy.props.CollectionProperty(type=BGEGlobalValue) + selected: bpy.props.IntProperty(name='Value') + + +class NL_UL_glcategory(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + layout.prop(item, "name", text="", emboss=False) + + +class NL_UL_glvalue(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + dat = { + 'STRING': 'string_val', + 'FLOAT': 'float_val', + 'INTEGER': 'int_val', + 'BOOLEAN': 'bool_val', + 'FILE_PATH': 'filepath_val' + } + row = layout.split() + row.prop(item, "name", text="", emboss=False) + # row.prop(item, 'value_type', text='', emboss=False) + emboss = item.value_type == 'BOOLEAN' or item.value_type == 'STRING' + row.prop(item, dat.get(item.value_type, 'FLOAT'), text='', emboss=emboss) + + +class BGE_PT_GlobalValuePanel(bpy.types.Panel): + bl_label = "Globals" + bl_space_type = "NODE_EDITOR" + bl_region_type = "UI" + bl_category = "Item" + + @classmethod + def poll(cls, context): + enabled = (context.space_data.tree_type == BGELogicTree.bl_idname) + return enabled + + def draw(self, context): + layout = self.layout + categories = bpy.context.scene.nl_global_categories + if len(categories) > 0: + category = utils.get_global_category() + row = layout.row() + row.template_list( + 'NL_UL_glcategory', + '', + bpy.context.scene, + 'nl_global_categories', + bpy.context.scene, + 'nl_global_cat_selected', + rows=3 + ) + opts = row.column(align=True) + opts.operator( + bge_netlogic.ops.NLAddGlobalCatOperator.bl_idname, + text='', + icon='PLUS' + ) + opts.operator( + bge_netlogic.ops.NLRemoveGlobalCatOperator.bl_idname, + text='', + icon='REMOVE' + ) + # Draw Category Properties + row = layout.row() + value = utils.get_global_value() + row.template_list( + 'NL_UL_glvalue', + '', + category, + 'content', + category, + 'selected', + rows=3 + ) + opts = row.column() + adders = opts.column(align=True) + adders.operator( + bge_netlogic.ops.NLAddGlobalOperator.bl_idname, + text='', + icon='PLUS' + ) + adders.operator( + bge_netlogic.ops.NLRemoveGlobalOperator.bl_idname, + text='', + icon='REMOVE' + ) + if value: + opts.prop(value, 'persistent', text='', icon='RADIOBUT_ON' if value.persistent else 'RADIOBUT_OFF') + row2 = layout.row() + row2.prop(value, 'value_type', text='Type') + else: + layout.operator( + bge_netlogic.ops.NLAddGlobalCatOperator.bl_idname, + text='Add Global Value', + icon='PLUS' + ) + + class BGE_PT_GamePropertyPanel(bpy.types.Panel): bl_label = "Object Properties" bl_space_type = "NODE_EDITOR" diff --git a/utilities/__init__.py b/utilities/__init__.py index 938843d..a92edb8 100644 --- a/utilities/__init__.py +++ b/utilities/__init__.py @@ -243,6 +243,30 @@ def success(message): print(f'[Logic Nodes][{ansicol.GREEN}SUCCESS{ansicol.END}] ' + message) +def get_global_category(): + scene = bpy.context.scene + return ( + scene.nl_global_categories[0] + if + scene.nl_global_cat_selected > len(scene.nl_global_categories) - 1 + else + scene.nl_global_categories[scene.nl_global_cat_selected] + ) + + +def get_global_value(): + cat = get_global_category() + if len(cat.content) < 1: + return None + return ( + cat.content[0] + if + cat.selected > len(cat.content) - 1 + else + cat.content[cat.selected] + ) + + def register_inputs(node, *data): assert isinstance(node, bpy.types.Node) i = 0