diff --git a/addons/block_code/block_code_node/block_code.gd b/addons/block_code/block_code_node/block_code.gd index 659719c9..6ea295ba 100644 --- a/addons/block_code/block_code_node/block_code.gd +++ b/addons/block_code/block_code_node/block_code.gd @@ -5,7 +5,8 @@ extends Node const TxUtils := preload("res://addons/block_code/translation/utils.gd") -@export var block_script: BlockScriptSerialization = null +@export var block_script: BlockScriptSerialization = null: + set = _set_block_script func _init(): @@ -37,6 +38,17 @@ func _enter_tree(): block_script = new_block_script +func _set_block_script(value): + if value == null: + # Wipe out the bidirectional link between this block code node and the + # block script + if block_script: + block_script.block_code_node = null + else: + value.block_code_node = self + block_script = value + + func _update_parent_script(): if Engine.is_editor_hint(): push_error("Updating the parent script must happen in game.") diff --git a/addons/block_code/code_generation/blocks_catalog.gd b/addons/block_code/code_generation/blocks_catalog.gd index e4ece40d..683852bf 100644 --- a/addons/block_code/code_generation/blocks_catalog.gd +++ b/addons/block_code/code_generation/blocks_catalog.gd @@ -253,25 +253,52 @@ static func add_custom_blocks( static func get_variable_block_definitions(variables: Array[VariableDefinition]) -> Array[BlockDefinition]: var block_definitions: Array[BlockDefinition] = [] for variable: VariableDefinition in variables: - var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type] + var block_def: BlockDefinition # Getter - var block_def = BlockDefinition.new() - block_def.name = "get_var_%s" % variable.var_name - block_def.category = "Variables" - block_def.type = Types.BlockType.VALUE - block_def.variant_type = variable.var_type - block_def.display_template = variable.var_name - block_def.code_template = variable.var_name + block_def = get_variable_getter_block_definition(variable) block_definitions.append(block_def) # Setter - block_def = BlockDefinition.new() - block_def.name = "set_var_%s" % variable.var_name - block_def.category = "Variables" - block_def.type = Types.BlockType.STATEMENT - block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] - block_def.code_template = "%s = {value}" % [variable.var_name] + block_def = get_variable_setter_block_definition(variable) block_definitions.append(block_def) return block_definitions + + +static func get_variable_getter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := BlockDefinition.new() + + block_def.name = "get_var_%s" % variable.var_name + block_def.category = "Variables" + block_def.type = Types.BlockType.VALUE + block_def.variant_type = variable.var_type + block_def.display_template = variable.var_name + block_def.code_template = variable.var_name + + return block_def + + +static func get_variable_setter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type] + var block_def := BlockDefinition.new() + + block_def.name = "set_var_%s" % variable.var_name + block_def.category = "Variables" + block_def.type = Types.BlockType.STATEMENT + block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] + block_def.code_template = "%s = {value}" % variable.var_name + + return block_def + + +static func get_property_getter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := get_variable_getter_block_definition(variable) + block_def.description = "The %s property" % variable.var_name + return block_def + + +static func get_property_setter_block_definition(variable: VariableDefinition) -> BlockDefinition: + var block_def := get_variable_setter_block_definition(variable) + block_def.description = "Set the %s property" % variable.var_name + return block_def diff --git a/addons/block_code/serialization/block_script_serialization.gd b/addons/block_code/serialization/block_script_serialization.gd index 5f3051e3..3d6210a5 100644 --- a/addons/block_code/serialization/block_script_serialization.gd +++ b/addons/block_code/serialization/block_script_serialization.gd @@ -28,6 +28,8 @@ const SCENE_PER_TYPE = { @export var generated_script: String @export var version: int +var block_code_node: BlockCode + var _available_blocks: Array[BlockDefinition] var _categories: Array[BlockCategory] @@ -81,22 +83,26 @@ func instantiate_block_by_name(block_name: String) -> Block: func get_block_definition(block_name: String) -> BlockDefinition: var split := block_name.split(":", true, 1) + var block_definition: BlockDefinition if len(split) > 1: return _get_parameter_block_definition(split[0], split[1]) - var block_definition = _get_base_block_definition(block_name) + block_definition = _get_base_block_definition(block_name) + if block_definition != null: + return block_definition - if block_definition == null: - # FIXME: This is a workaround for old-style output block references. - # These were generated ahead of time using a block name that has - # a "_" before the parameter name. Now, these parameter blocks - # are generated on demand for any block name containing a ":". - # Please remove this fallback when it is no longer necessary. - split = block_name.rsplit("_", true, 1) - return _get_parameter_block_definition(split[0], split[1]) + block_definition = _get_obj_property_block_definition(block_name) + if block_definition != null: + return block_definition - return block_definition + # FIXME: This is a workaround for old-style output block references. + # These were generated ahead of time using a block name that has + # a "_" before the parameter name. Now, these parameter blocks + # are generated on demand for any block name containing a ":". + # Please remove this fallback when it is no longer necessary. + split = block_name.rsplit("_", true, 1) + return _get_parameter_block_definition(split[0], split[1]) func _get_base_block_definition(block_name: String) -> BlockDefinition: @@ -133,6 +139,49 @@ func _get_parameter_block_definition(block_name: String, parameter_name: String) return block_definition +func _get_obj_property_block_definition(block_name: String) -> BlockDefinition: + var block_definition: BlockDefinition + var variable: VariableDefinition + var property_name: String + var is_getter = true + + if block_name.begins_with("get_var_"): + property_name = block_name.get_slice("get_var_", 1) + elif block_name.begins_with("set_var_"): + property_name = block_name.get_slice("set_var_", 1) + is_getter = false + else: + return null + + # Getter block needs the property's variant type information by visiting the + # block_code_node's parent node because the type is not saved as a key of + # the resource in the scene file + var property_info := _get_parent_node_property_info(property_name) + if not property_info.has("type"): + return null + + if is_getter: + variable = VariableDefinition.new(property_name, property_info["type"]) + block_definition = BlocksCatalog.get_property_getter_block_definition(variable) + else: + variable = VariableDefinition.new(property_name, property_info["type"]) + block_definition = BlocksCatalog.get_property_setter_block_definition(variable) + + return block_definition + + +func _get_parent_node_property_info(property_name: String) -> Dictionary: + if not block_code_node: + return {} + + var properties := block_code_node.get_parent().get_property_list() + for property in properties: + if property["name"] == property_name: + return property + + return {} + + func _update_block_definitions(): _available_blocks.clear() _available_blocks.append_array(_get_inherited_block_definitions()) diff --git a/addons/block_code/ui/block_canvas/block_canvas.gd b/addons/block_code/ui/block_canvas/block_canvas.gd index 2f4fcc51..10be16db 100644 --- a/addons/block_code/ui/block_canvas/block_canvas.gd +++ b/addons/block_code/ui/block_canvas/block_canvas.gd @@ -3,6 +3,7 @@ extends MarginContainer const ASTList = preload("res://addons/block_code/code_generation/ast_list.gd") const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd") +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") const BlockCodePlugin = preload("res://addons/block_code/block_code_plugin.gd") const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") @@ -10,6 +11,7 @@ const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.g const ScriptGenerator = preload("res://addons/block_code/code_generation/script_generator.gd") const Types = preload("res://addons/block_code/types/types.gd") const Util = preload("res://addons/block_code/ui/util.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") const EXTEND_MARGIN: float = 800 const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(25, 8) @@ -79,8 +81,10 @@ func _can_drop_data(at_position: Vector2, data: Variant) -> bool: if typeof(data) != TYPE_DICTIONARY: return false - # Allow dropping property block + # Allow dropping property block of the block code node's parent node if data.get("type", "") == "obj_property": + if data["object"] != _context.parent_node: + return false return true var nodes: Array = data.get("nodes", []) @@ -124,44 +128,20 @@ func _drop_node(at_position: Vector2, data: Variant) -> void: func _drop_obj_property(at_position: Vector2, data: Variant) -> void: - var object_name = str(data["object"]).get_slice(":", 0) var property_name = data["property"] var property_value = data["value"] + var is_getter = !_modifier_ctrl # Prepare a Variable block to set / get the property's value according to # the modifier KEY_CTRL pressing. + var variable := VariableDefinition.new(property_name, typeof(property_value)) var block_definition: BlockDefinition - var property_type = typeof(property_value) - if _modifier_ctrl: - var type_string: String = Types.VARIANT_TYPE_TO_STRING[property_type] - block_definition = ( - BlockDefinition - . new( - &"%s_set_%s" % [object_name, property_name], - object_name, - "Set the %s property" % property_name, - "Variables", - Types.BlockType.STATEMENT, - property_type, - "set %s to {value: %s}" % [property_name.capitalize().to_lower(), type_string], - "%s = {value}" % property_name, - {"value": property_value}, - ) - ) + + if is_getter: + block_definition = BlocksCatalog.get_property_getter_block_definition(variable) else: - block_definition = ( - BlockDefinition - . new( - &"%s_get_%s" % [object_name, property_name], - object_name, - "The %s property" % property_name, - "Variables", - Types.BlockType.VALUE, - property_type, - "%s" % property_name.capitalize().to_lower(), - "%s" % property_name, - ) - ) + block_definition = BlocksCatalog.get_property_setter_block_definition(variable) + block_definition.defaults = {"value": property_value} var block = _context.block_script.instantiate_block(block_definition) add_block(block, at_position)