diff --git a/addons/block_code/block_code_plugin.gd b/addons/block_code/block_code_plugin.gd index f7e0ac8a..f1cc1a0a 100644 --- a/addons/block_code/block_code_plugin.gd +++ b/addons/block_code/block_code_plugin.gd @@ -24,8 +24,6 @@ const DISABLED_CLASSES := [ "ParameterBlock", "StatementBlock", "SnapPoint", - "BlockSerialization", - "BlockSerializedProperties", "BlockScriptSerialization", "CategoryFactory", ] diff --git a/addons/block_code/blocks/communication/define_method.tres b/addons/block_code/blocks/communication/define_method.tres index 4278151d..f3bd8926 100644 --- a/addons/block_code/blocks/communication/define_method.tres +++ b/addons/block_code/blocks/communication/define_method.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_6e473") name = &"define_method" +target_node_class = "" description = "Define a method/function with following statements" category = "Communication | Methods" type = 1 variant_type = 0 -display_template = "Define method {method_name: NIL}" +display_template = "Define method {method_name: STRING_NAME}" code_template = "func {method_name}():" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/animationplayer_play.tres b/addons/block_code/blocks/graphics/animationplayer_play.tres index 43bbce1e..2a2f373f 100644 --- a/addons/block_code/blocks/graphics/animationplayer_play.tres +++ b/addons/block_code/blocks/graphics/animationplayer_play.tres @@ -1,7 +1,7 @@ [gd_resource type="Resource" load_steps=4 format=3 uid="uid://c5e1byehtxwc0"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_emeuv"] -[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_xu43h"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_xu43h"] [sub_resource type="Resource" id="Resource_vnp2w"] script = ExtResource("1_xu43h") diff --git a/addons/block_code/blocks/logic/compare.tres b/addons/block_code/blocks/logic/compare.tres index 8e124cf7..f657ba44 100644 --- a/addons/block_code/blocks/logic/compare.tres +++ b/addons/block_code/blocks/logic/compare.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" load_steps=4 format=3 uid="uid://pr5wnn3ltkbo"] -[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_hcv2h"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_hcv2h"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_wp40r"] [sub_resource type="Resource" id="Resource_ie4sg"] diff --git a/addons/block_code/blocks/sounds/pause_continue_sound.tres b/addons/block_code/blocks/sounds/pause_continue_sound.tres index 074e0352..3cbfab88 100644 --- a/addons/block_code/blocks/sounds/pause_continue_sound.tres +++ b/addons/block_code/blocks/sounds/pause_continue_sound.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" load_steps=4 format=3 uid="uid://wpdspamg3f6g"] -[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="1_ilhdq"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_ilhdq"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_q04gm"] [sub_resource type="Resource" id="Resource_lalgp"] diff --git a/addons/block_code/code_generation/ast_list.gd b/addons/block_code/code_generation/ast_list.gd new file mode 100644 index 00000000..aafb6caa --- /dev/null +++ b/addons/block_code/code_generation/ast_list.gd @@ -0,0 +1,37 @@ +extends RefCounted + +const Types = preload("res://addons/block_code/types/types.gd") +const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd") + +var array: Array[ASTPair] + + +class ASTPair: + var ast: BlockAST + var canvas_position: Vector2 + + func _init(p_ast: BlockAST, p_canvas_position: Vector2): + ast = p_ast + canvas_position = p_canvas_position + + +func _init(): + array = [] + + +func append(ast: BlockAST, canvas_position: Vector2): + array.append(ASTPair.new(ast, canvas_position)) + + +func clear(): + array.clear() + + +func get_top_level_nodes_of_type(block_type: Types.BlockType) -> Array[BlockAST]: + var asts: Array[BlockAST] = [] + + for ast_pair in array: + if ast_pair.ast.root.data.type == block_type: + asts.append(ast_pair.ast) + + return asts diff --git a/addons/block_code/code_generation/block_ast.gd b/addons/block_code/code_generation/block_ast.gd new file mode 100644 index 00000000..df35e354 --- /dev/null +++ b/addons/block_code/code_generation/block_ast.gd @@ -0,0 +1,145 @@ +extends RefCounted + +const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd") +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") +const Types = preload("res://addons/block_code/types/types.gd") + +var root: ASTNode + + +class IDHandler: + static var counts: Dictionary = {} + + static func reset(): + counts = {} + + static func get_unique_id(str: String) -> int: + if not counts.has(str): + counts[str] = 0 + + counts[str] += 1 + + return counts[str] + + static func make_unique(formatted_string: String) -> String: + var unique_string = formatted_string + var regex = RegEx.new() + regex.compile("\\b__[^\\s]+") + var ids: Dictionary = {} + for result in regex.search_all(formatted_string): + var result_string = result.get_string() + if not ids.has(result_string): + ids[result_string] = get_unique_id(result_string) + unique_string = unique_string.replace(result_string, result_string + "_%d" % ids[result_string]) + + return unique_string + + +class ASTNode: + var data #: BlockDefinition + var children: Array[ASTNode] + var arguments: Dictionary # String, ASTValueNode + + func _init(): + children = [] + arguments = {} + + func get_code_block() -> String: + var code_block: String = data.code_template # get multiline code_template from block definition + + # insert args + + # check if args match an overload in the resource + + for arg_name in arguments: + # Use parentheses to be safe + var argument = arguments[arg_name] + var code_string: String + if argument is ASTValueNode: + code_string = argument.get_code() + else: + code_string = BlockAST.raw_input_to_code_string(argument) + + code_block = code_block.replace("{%s}" % arg_name, code_string) + + return IDHandler.make_unique(code_block) + + func get_code(depth: int) -> String: + var code: String = "" + + # append code block + var code_block := get_code_block() + code_block = code_block.indent("\t".repeat(depth)) + + code += code_block + "\n" + + # fill empty entry and control blocks with pass + if children.is_empty() and (data.type == Types.BlockType.ENTRY || data.type == Types.BlockType.CONTROL): + code += "pass\n".indent("\t".repeat(depth + 1)) + + for child in children: + code += child.get_code(depth + 1) + + return code + + +class ASTValueNode: + var data #: BlockDefinition + var arguments: Dictionary # String, ASTValueNode + + func _init(): + arguments = {} + + func get_code() -> String: + var code: String = data.code_template # get code_template from block definition + + # check if args match an overload in the resource + + for arg_name in arguments: + # Use parentheses to be safe + var argument = arguments[arg_name] + var code_string: String + if argument is ASTValueNode: + code_string = argument.get_code() + else: + code_string = BlockAST.raw_input_to_code_string(argument) + + code = code.replace("{%s}" % arg_name, code_string) + + return IDHandler.make_unique("(%s)" % code) + + +func get_code() -> String: + IDHandler.reset() + return root.get_code(0) + + +func _to_string(): + return to_string_recursive(root, 0) + + +func to_string_recursive(node: ASTNode, depth: int) -> String: + var string: String = "%s %s\n" % ["-".repeat(depth), node.data.display_template] + + for c in node.children: + string += to_string_recursive(c, depth + 1) + + return string + + +static func raw_input_to_code_string(input) -> String: + match typeof(input): + TYPE_STRING: + return "'%s'" % input.replace("\\", "\\\\").replace("'", "\\'") + TYPE_VECTOR2: + return "Vector2%s" % str(input) + TYPE_COLOR: + return "Color%s" % str(input) + TYPE_OBJECT: + if input is OptionData: + var option_data := input as OptionData + return option_data.items[option_data.selected] + _: + return "%s" % input + + return "" diff --git a/addons/block_code/code_generation/blocks_catalog.gd b/addons/block_code/code_generation/blocks_catalog.gd index 18ec616b..8ab6f73a 100644 --- a/addons/block_code/code_generation/blocks_catalog.gd +++ b/addons/block_code/code_generation/blocks_catalog.gd @@ -1,8 +1,10 @@ extends Object const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") const Types = preload("res://addons/block_code/types/types.gd") const Util = preload("res://addons/block_code/code_generation/util.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") const _BLOCKS_PATH = "res://addons/block_code/blocks/" @@ -94,6 +96,40 @@ static func _setup_definitions_from_files(): _by_class_name[target][block_definition.name] = block_definition +static func _add_output_definitions(definitions: Array[BlockDefinition]): + # Capture things of format [test] + var _output_regex := RegEx.create_from_string("\\[([^\\]]+)\\]") + + for definition in definitions: + if definition.type != Types.BlockType.ENTRY: + continue + + for reg_match in _output_regex.search_all(definition.display_template): + var parts := reg_match.get_string(1).split(": ") + var param_name := parts[0] + var param_type: Variant.Type = Types.STRING_TO_VARIANT_TYPE[parts[1]] + + var output_def := BlockDefinition.new() + output_def.name = &"%s_%s" % [definition.name, param_name] + output_def.target_node_class = definition.target_node_class + output_def.category = definition.category + output_def.type = Types.BlockType.VALUE + output_def.variant_type = param_type + output_def.display_template = param_name + output_def.code_template = param_name + output_def.scope = definition.code_template + + # Note that these are not added to the _by_class_name dict + # because they only make sense within the entry block scope. + _catalog[output_def.name] = output_def + + +static func _setup_output_definitions(): + var definitions: Array[BlockDefinition] + definitions.assign(_catalog.values()) + _add_output_definitions(definitions) + + static func _add_property_definitions(_class_name: String, property_list: Array[Dictionary], property_settings: Dictionary): for property in property_list: if not property.name in property_settings: @@ -218,6 +254,7 @@ static func setup(): _catalog = {} _setup_definitions_from_files() + _setup_output_definitions() _setup_properties_for_class() _setup_input_block() @@ -237,6 +274,45 @@ static func get_blocks_by_class(_class_name: String): return block_definitions.values() +static func _get_builtin_parents(_class_name: String) -> Array[String]: + var parents: Array[String] = [] + var current = _class_name + + while current != "": + parents.append(current) + current = ClassDB.get_parent_class(current) + + return parents + + +static func _get_custom_parent_class_name(_custom_class_name: String) -> String: + for class_dict in ProjectSettings.get_global_class_list(): + if class_dict.class != _custom_class_name: + continue + var script = load(class_dict.path) + var builtin_class = script.get_instance_base_type() + return builtin_class + return "Node" + + +static func _get_parents(_class_name: String) -> Array[String]: + if ClassDB.class_exists(_class_name): + return _get_builtin_parents(_class_name) + var parents: Array[String] = [_class_name] + var _parent_class_name = _get_custom_parent_class_name(_class_name) + parents.append_array(_get_builtin_parents(_parent_class_name)) + return parents + + +static func get_inherited_blocks(_class_name: String) -> Array[BlockDefinition]: + setup() + + var definitions: Array[BlockDefinition] = [] + for _parent_class_name in _get_parents(_class_name): + definitions.append_array(get_blocks_by_class(_parent_class_name)) + return definitions + + static func add_custom_blocks( _class_name, block_definitions: Array[BlockDefinition] = [], @@ -252,4 +328,32 @@ static func add_custom_blocks( _catalog[block_definition.name] = block_definition _by_class_name[_class_name][block_definition.name] = block_definition + _add_output_definitions(block_definitions) _add_property_definitions(_class_name, property_list, property_settings) + + +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] + + # 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_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_definitions.append(block_def) + + return block_definitions diff --git a/addons/block_code/ui/block_canvas/option_data.gd b/addons/block_code/code_generation/option_data.gd similarity index 88% rename from addons/block_code/ui/block_canvas/option_data.gd rename to addons/block_code/code_generation/option_data.gd index d233c819..20364c04 100644 --- a/addons/block_code/ui/block_canvas/option_data.gd +++ b/addons/block_code/code_generation/option_data.gd @@ -1,4 +1,3 @@ -class_name OptionData extends Resource @export var selected: int diff --git a/addons/block_code/code_generation/script_generator.gd b/addons/block_code/code_generation/script_generator.gd new file mode 100644 index 00000000..264b73f4 --- /dev/null +++ b/addons/block_code/code_generation/script_generator.gd @@ -0,0 +1,63 @@ +extends Object + +const Types = preload("res://addons/block_code/types/types.gd") +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 BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") + + +static func generate_script(ast_list: ASTList, block_script: BlockScriptSerialization) -> String: + var entry_asts: Array[BlockAST] = ast_list.get_top_level_nodes_of_type(Types.BlockType.ENTRY) + + var init_ast := BlockAST.new() + init_ast.root = BlockAST.ASTNode.new() + init_ast.root.data = BlockDefinition.new() + init_ast.root.data.type = Types.BlockType.ENTRY + init_ast.root.data.code_template = "func _init():" + + var combined_entry_asts = {} + + # Combine entry asts with same root statement + for entry_ast in entry_asts: + var statement = entry_ast.root.get_code(0) + if not statement in combined_entry_asts: + var new_ast := BlockAST.new() + var root = BlockAST.ASTNode.new() + root.data = entry_ast.root.data + root.arguments = entry_ast.root.arguments + root.children = entry_ast.root.children.duplicate() + new_ast.root = root + combined_entry_asts[statement] = new_ast + else: + combined_entry_asts[statement].root.children.append_array(entry_ast.root.children) + + # Connect signals on _init + for entry_statement in combined_entry_asts: + var entry_ast: BlockAST = combined_entry_asts[entry_statement] + var signal_name = entry_ast.root.data.signal_name + if signal_name != "": + var signal_node := BlockAST.ASTNode.new() + signal_node.data = BlockDefinition.new() + signal_node.data.code_template = "{0}.connect(_on_{0})".format([signal_name]) + init_ast.root.children.append(signal_node) + + # Generate script extends statement + var script := "extends %s\n\n" % block_script.script_inherits + + # Generate variables + for variable in block_script.variables: + script += "var %s: %s\n\n" % [variable.var_name, type_string(variable.var_type)] + + script += "\n" + + # Generate _init + if not init_ast.root.children.is_empty(): + script += init_ast.get_code() + "\n" + + # Generate other entry methods + for entry_statement in combined_entry_asts: + var entry_ast: BlockAST = combined_entry_asts[entry_statement] + script += entry_ast.get_code() + script += "\n" + + return script diff --git a/addons/block_code/ui/block_canvas/variable_resource.gd b/addons/block_code/code_generation/variable_definition.gd similarity index 87% rename from addons/block_code/ui/block_canvas/variable_resource.gd rename to addons/block_code/code_generation/variable_definition.gd index 119d4371..680de228 100644 --- a/addons/block_code/ui/block_canvas/variable_resource.gd +++ b/addons/block_code/code_generation/variable_definition.gd @@ -1,4 +1,3 @@ -class_name VariableResource extends Resource @export var var_name: String diff --git a/addons/block_code/drag_manager/drag.gd b/addons/block_code/drag_manager/drag.gd index 7017c8c4..76d41b2c 100644 --- a/addons/block_code/drag_manager/drag.gd +++ b/addons/block_code/drag_manager/drag.gd @@ -5,7 +5,6 @@ const Background = preload("res://addons/block_code/ui/blocks/utilities/backgrou const BlockCanvas = preload("res://addons/block_code/ui/block_canvas/block_canvas.gd") const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") const Constants = preload("res://addons/block_code/ui/constants.gd") -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") const Types = preload("res://addons/block_code/types/types.gd") enum DragAction { NONE, PLACE, REMOVE } @@ -124,11 +123,12 @@ func _snaps_to(node: Node) -> bool: # We only snap to blocks on the canvas: return false - if _block.block_type != _snap_point.block_type: - # We only snap to the same block type: - return false + # We only snap to the same block type: (HACK: Control blocks can snap to statements) + if not (_block.definition.type == Types.BlockType.CONTROL and _snap_point.block_type == Types.BlockType.STATEMENT): + if _block.definition.type != _snap_point.block_type: + return false - if _block.block_type == Types.BlockType.VALUE and not Types.can_cast(_block.variant_type, _snap_point.variant_type): + if _block.definition.type == Types.BlockType.VALUE and not Types.can_cast(_block.definition.variant_type, _snap_point.variant_type): # We only snap Value blocks to snaps that can cast to same variant: return false @@ -141,7 +141,7 @@ func _snaps_to(node: Node) -> bool: # Check if scope is valid if _block_scope != "": if top_block is EntryBlock: - if _block_scope != top_block.get_entry_statement(): + if _block_scope != top_block.definition.code_template: return false elif top_block: var tree_scope := BlockTreeUtil.get_tree_scope(top_block) diff --git a/addons/block_code/drag_manager/drag_manager.gd b/addons/block_code/drag_manager/drag_manager.gd index c12dd984..033f1e8d 100644 --- a/addons/block_code/drag_manager/drag_manager.gd +++ b/addons/block_code/drag_manager/drag_manager.gd @@ -7,8 +7,8 @@ signal block_modified const BlockCanvas = preload("res://addons/block_code/ui/block_canvas/block_canvas.gd") const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") const Drag = preload("res://addons/block_code/drag_manager/drag.gd") -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") const Picker = preload("res://addons/block_code/ui/picker/picker.gd") +const Util = preload("res://addons/block_code/ui/util.gd") @export var picker_path: NodePath @export var block_canvas_path: NodePath @@ -64,7 +64,9 @@ func drag_block(block: Block, copied_from: Block = null): func copy_block(block: Block) -> Block: - return block.duplicate(DUPLICATE_USE_INSTANTIATION) # use instantiation + var new_block = Util.instantiate_block(block.definition) + new_block.color = block.color + return new_block func copy_picked_block_and_drag(block: Block): diff --git a/addons/block_code/examples/pong_game/pong_game.tscn b/addons/block_code/examples/pong_game/pong_game.tscn index ec3ab35c..ef359575 100644 --- a/addons/block_code/examples/pong_game/pong_game.tscn +++ b/addons/block_code/examples/pong_game/pong_game.tscn @@ -1,85 +1,72 @@ -[gd_scene load_steps=119 format=3 uid="uid://tf7b8c64ecc0"] +[gd_scene load_steps=81 format=3 uid="uid://tf7b8c64ecc0"] [ext_resource type="PackedScene" uid="uid://cg8ibi18um3vg" path="res://addons/block_code/examples/pong_game/space.tscn" id="1_y56ac"] [ext_resource type="Script" path="res://addons/block_code/block_code_node/block_code.gd" id="3_6jaq8"] [ext_resource type="Script" path="res://addons/block_code/simple_nodes/simple_character/simple_character.gd" id="3_mdrcv"] [ext_resource type="Script" path="res://addons/block_code/serialization/block_serialization.gd" id="4_qtggh"] [ext_resource type="Texture2D" uid="uid://tplpgtnfeda0" path="res://addons/block_code/examples/pong_game/assets/paddle.png" id="4_ra7bh"] -[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialized_properties.gd" id="5_wr38c"] -[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/option_data.gd" id="7_3q6bj"] +[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialization_tree.gd" id="5_omlge"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="7_3q6bj"] [ext_resource type="Script" path="res://addons/block_code/serialization/block_script_serialization.gd" id="7_uuuue"] -[ext_resource type="Script" path="res://addons/block_code/ui/block_canvas/variable_resource.gd" id="9_lo3p1"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/variable_definition.gd" id="9_lo3p1"] [ext_resource type="PackedScene" uid="uid://c7l70grmkauij" path="res://addons/block_code/examples/pong_game/ball.tscn" id="9_xrqll"] +[ext_resource type="Script" path="res://addons/block_code/serialization/value_block_serialization.gd" id="11_yafka"] [ext_resource type="PackedScene" uid="uid://fhoapg3anjsu" path="res://addons/block_code/examples/pong_game/goal_area.tscn" id="12_nqmxu"] [ext_resource type="Script" path="res://addons/block_code/simple_nodes/simple_scoring/simple_scoring.gd" id="13_tg3yk"] -[sub_resource type="Resource" id="Resource_iegsu"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_6fwti"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"value": Vector2(0, 1000) -}]] - -[sub_resource type="Resource" id="Resource_bxt7h"] +[sub_resource type="Resource" id="Resource_7e5rp"] script = ExtResource("4_qtggh") name = &"SimpleCharacter_set_speed" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_6fwti") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"value": Vector2(0, 1000) +} -[sub_resource type="Resource" id="Resource_xsn6l"] +[sub_resource type="Resource" id="Resource_inqps"] script = ExtResource("4_qtggh") name = &"ready" -position = Vector2(75, 175) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_bxt7h")]] -block_serialized_properties = SubResource("Resource_iegsu") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_7e5rp")]) +arguments = {} -[sub_resource type="Resource" id="Resource_41mcx"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] +[sub_resource type="Resource" id="Resource_yjwqr"] +script = ExtResource("5_omlge") +root = SubResource("Resource_inqps") +canvas_position = Vector2(50, 25) -[sub_resource type="Resource" id="Resource_rwx76"] +[sub_resource type="Resource" id="Resource_ytth5"] script = ExtResource("7_3q6bj") selected = 0 items = ["top-down", "platformer", "spaceship"] -[sub_resource type="Resource" id="Resource_ksb0o"] +[sub_resource type="Resource" id="Resource_ujg3k"] script = ExtResource("7_3q6bj") selected = 0 items = ["player_1", "player_2"] -[sub_resource type="Resource" id="Resource_82o3l"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"kind": SubResource("Resource_rwx76"), -"player": SubResource("Resource_ksb0o") -}]] - -[sub_resource type="Resource" id="Resource_4vl0d"] +[sub_resource type="Resource" id="Resource_ocowr"] script = ExtResource("4_qtggh") name = &"simplecharacter_move" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_82o3l") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"kind": SubResource("Resource_ytth5"), +"player": SubResource("Resource_ujg3k") +} -[sub_resource type="Resource" id="Resource_wntwg"] +[sub_resource type="Resource" id="Resource_at2ah"] script = ExtResource("4_qtggh") name = &"process" -position = Vector2(75, 300) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_4vl0d")]] -block_serialized_properties = SubResource("Resource_41mcx") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_ocowr")]) +arguments = {} + +[sub_resource type="Resource" id="Resource_1w7mq"] +script = ExtResource("5_omlge") +root = SubResource("Resource_at2ah") +canvas_position = Vector2(50, 175) [sub_resource type="Resource" id="Resource_m2svk"] script = ExtResource("7_uuuue") script_inherits = "SimpleCharacter" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_xsn6l"), SubResource("Resource_wntwg")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_yjwqr"), SubResource("Resource_1w7mq")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends SimpleCharacter @@ -93,73 +80,59 @@ func _process(delta): " version = 0 -[sub_resource type="Resource" id="Resource_lhobc"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_1ycg8"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"value": Vector2(0, 1000) -}]] - -[sub_resource type="Resource" id="Resource_nkjuo"] +[sub_resource type="Resource" id="Resource_qmwe2"] script = ExtResource("4_qtggh") name = &"SimpleCharacter_set_speed" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_1ycg8") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"value": Vector2(0, 1000) +} -[sub_resource type="Resource" id="Resource_t1m7j"] +[sub_resource type="Resource" id="Resource_ehp62"] script = ExtResource("4_qtggh") name = &"ready" -position = Vector2(50, 175) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_nkjuo")]] -block_serialized_properties = SubResource("Resource_lhobc") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_qmwe2")]) +arguments = {} -[sub_resource type="Resource" id="Resource_ud032"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] +[sub_resource type="Resource" id="Resource_sgmlh"] +script = ExtResource("5_omlge") +root = SubResource("Resource_ehp62") +canvas_position = Vector2(25, 0) -[sub_resource type="Resource" id="Resource_ixcua"] +[sub_resource type="Resource" id="Resource_6cn1w"] script = ExtResource("7_3q6bj") selected = 0 items = ["top-down", "platformer", "spaceship"] -[sub_resource type="Resource" id="Resource_728pr"] +[sub_resource type="Resource" id="Resource_i3nv8"] script = ExtResource("7_3q6bj") selected = 1 items = ["player_1", "player_2"] -[sub_resource type="Resource" id="Resource_7aw38"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"kind": SubResource("Resource_ixcua"), -"player": SubResource("Resource_728pr") -}]] - -[sub_resource type="Resource" id="Resource_gtejw"] +[sub_resource type="Resource" id="Resource_im4n0"] script = ExtResource("4_qtggh") name = &"simplecharacter_move" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_7aw38") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"kind": SubResource("Resource_6cn1w"), +"player": SubResource("Resource_i3nv8") +} -[sub_resource type="Resource" id="Resource_03rje"] +[sub_resource type="Resource" id="Resource_plb5i"] script = ExtResource("4_qtggh") name = &"process" -position = Vector2(50, 300) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_gtejw")]] -block_serialized_properties = SubResource("Resource_ud032") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_im4n0")]) +arguments = {} + +[sub_resource type="Resource" id="Resource_xbg8g"] +script = ExtResource("5_omlge") +root = SubResource("Resource_plb5i") +canvas_position = Vector2(25, 175) [sub_resource type="Resource" id="Resource_ysbi4"] script = ExtResource("7_uuuue") script_inherits = "SimpleCharacter" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_t1m7j"), SubResource("Resource_03rje")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_sgmlh"), SubResource("Resource_xbg8g")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends SimpleCharacter @@ -173,657 +146,433 @@ func _process(delta): " version = 0 -[sub_resource type="Resource" id="Resource_d8f3t"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_wtbtc"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +[sub_resource type="Resource" id="Resource_olii0"] +script = ExtResource("4_qtggh") +name = &"load_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "file_path": "res://addons/block_code/examples/pong_game/assets/paddle_hit.ogg", "name": "paddle_hit" -}]] +} -[sub_resource type="Resource" id="Resource_st0iw"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +[sub_resource type="Resource" id="Resource_31ax8"] +script = ExtResource("4_qtggh") +name = &"load_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "file_path": "res://addons/block_code/examples/pong_game/assets/wall_hit.ogg", "name": "wall_hit" -}]] +} -[sub_resource type="Resource" id="Resource_jp6b8"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +[sub_resource type="Resource" id="Resource_2duo7"] +script = ExtResource("4_qtggh") +name = &"load_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "file_path": "res://addons/block_code/examples/pong_game/assets/score.ogg", "name": "score_sound" -}]] +} -[sub_resource type="Resource" id="Resource_xs0e6"] +[sub_resource type="Resource" id="Resource_04bql"] script = ExtResource("4_qtggh") -name = &"load_sound" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_jp6b8") +name = &"ready" +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_olii0"), SubResource("Resource_31ax8"), SubResource("Resource_2duo7")]) +arguments = {} -[sub_resource type="Resource" id="Resource_spen4"] -script = ExtResource("4_qtggh") -name = &"load_sound" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_xs0e6")]] -block_serialized_properties = SubResource("Resource_st0iw") +[sub_resource type="Resource" id="Resource_t5jjg"] +script = ExtResource("5_omlge") +root = SubResource("Resource_04bql") +canvas_position = Vector2(25, 0) -[sub_resource type="Resource" id="Resource_r5331"] -script = ExtResource("4_qtggh") -name = &"load_sound" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_spen4")]] -block_serialized_properties = SubResource("Resource_wtbtc") +[sub_resource type="Resource" id="Resource_trtrd"] +script = ExtResource("11_yafka") +name = &"viewport_center" +arguments = {} -[sub_resource type="Resource" id="Resource_phaw7"] +[sub_resource type="Resource" id="Resource_nc43w"] script = ExtResource("4_qtggh") -name = &"ready" -position = Vector2(50, 50) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_r5331")]] -block_serialized_properties = SubResource("Resource_d8f3t") - -[sub_resource type="Resource" id="Resource_47jf7"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"method_name": "reset" -}]] - -[sub_resource type="Resource" id="Resource_obbdk"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"position": Vector2(0, 0) -}]] - -[sub_resource type="Resource" id="Resource_qae7k"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] +name = &"rigidbody2d_physics_position" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"position": SubResource("Resource_trtrd") +} -[sub_resource type="Resource" id="Resource_1sea1"] +[sub_resource type="Resource" id="Resource_1dd5f"] script = ExtResource("4_qtggh") -name = &"viewport_center" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_qae7k") - -[sub_resource type="Resource" id="Resource_3b4ka"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +name = &"play_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "db": 0.0, "name": "score_sound", "pitch": 1.0 -}]] +} -[sub_resource type="Resource" id="Resource_16pmr"] -script = ExtResource("4_qtggh") -name = &"play_sound" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_3b4ka") - -[sub_resource type="Resource" id="Resource_ugq5m"] -script = ExtResource("4_qtggh") -name = &"rigidbody2d_physics_position" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/TopMarginContainer/MarginContainer/HBoxContainer/ParameterInput0/SnapPoint"), SubResource("Resource_1sea1")], [NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_16pmr")]] -block_serialized_properties = SubResource("Resource_obbdk") - -[sub_resource type="Resource" id="Resource_ane3b"] +[sub_resource type="Resource" id="Resource_klc3o"] script = ExtResource("4_qtggh") name = &"define_method" -position = Vector2(50, 275) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_ugq5m")]] -block_serialized_properties = SubResource("Resource_47jf7") - -[sub_resource type="Resource" id="Resource_7kl7g"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_vqtp0"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_bgppn"] -script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_vqtp0") - -[sub_resource type="Resource" id="Resource_dnsl7"] -script = ExtResource("5_wr38c") -block_class = &"ControlBlock" -serialized_props = [["scope", ""], ["param_input_strings_array", [{ -"condition": false -}]]] - -[sub_resource type="Resource" id="Resource_kvrwg"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_nc43w"), SubResource("Resource_1dd5f")]) +arguments = { +"method_name": &"reset" +} + +[sub_resource type="Resource" id="Resource_2q6mf"] +script = ExtResource("5_omlge") +root = SubResource("Resource_klc3o") +canvas_position = Vector2(25, 250) + +[sub_resource type="Resource" id="Resource_hmv46"] +script = ExtResource("11_yafka") +name = &"rigidbody2d_on_entered_body" +arguments = {} + +[sub_resource type="Resource" id="Resource_l16xj"] +script = ExtResource("11_yafka") +name = &"is_node_in_group" +arguments = { "group": "paddles", -"node": "" -}]] +"node": SubResource("Resource_hmv46") +} -[sub_resource type="Resource" id="Resource_et4ib"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_707bn"] +[sub_resource type="Resource" id="Resource_0ltlg"] script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_et4ib") - -[sub_resource type="Resource" id="Resource_dj7fd"] -script = ExtResource("4_qtggh") -name = &"is_node_in_group" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("MarginContainer/HBoxContainer/ParameterInput0/SnapPoint"), SubResource("Resource_707bn")]] -block_serialized_properties = SubResource("Resource_kvrwg") - -[sub_resource type="Resource" id="Resource_t4jty"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +name = &"play_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "db": 0.0, "name": "paddle_hit", "pitch": 1.0 -}]] +} -[sub_resource type="Resource" id="Resource_2lgdm"] +[sub_resource type="Resource" id="Resource_pgmj0"] script = ExtResource("4_qtggh") -name = &"play_sound" -position = Vector2(20, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_t4jty") - -[sub_resource type="Resource" id="Resource_e1awu"] -script = ExtResource("5_wr38c") -block_class = &"ControlBlock" -serialized_props = [["scope", ""], ["param_input_strings_array", [{ -"condition": false -}]]] - -[sub_resource type="Resource" id="Resource_1en27"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +name = &"if" +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_0ltlg")]) +arguments = { +"condition": SubResource("Resource_l16xj") +} + +[sub_resource type="Resource" id="Resource_1d2v5"] +script = ExtResource("11_yafka") +name = &"rigidbody2d_on_entered_body" +arguments = {} + +[sub_resource type="Resource" id="Resource_cikin"] +script = ExtResource("11_yafka") +name = &"is_node_in_group" +arguments = { "group": "walls", -"node": "" -}]] +"node": SubResource("Resource_1d2v5") +} -[sub_resource type="Resource" id="Resource_mur5c"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_wo7ob"] +[sub_resource type="Resource" id="Resource_iue1k"] script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_mur5c") - -[sub_resource type="Resource" id="Resource_jb4li"] -script = ExtResource("4_qtggh") -name = &"is_node_in_group" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("MarginContainer/HBoxContainer/ParameterInput0/SnapPoint"), SubResource("Resource_wo7ob")]] -block_serialized_properties = SubResource("Resource_1en27") - -[sub_resource type="Resource" id="Resource_s6roe"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +name = &"play_sound" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { "db": 0.0, "name": "wall_hit", "pitch": 1.0 -}]] - -[sub_resource type="Resource" id="Resource_kd52t"] -script = ExtResource("4_qtggh") -name = &"play_sound" -position = Vector2(20, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_s6roe") - -[sub_resource type="Resource" id="Resource_ky5ld"] -script = ExtResource("4_qtggh") -name = &"if" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/MarginContainer/Rows/Row0/RowHBoxContainer/RowHBox/ParameterInput0/SnapPoint"), SubResource("Resource_jb4li")], [NodePath("VBoxContainer/MarginContainer/Rows/SnapContainer0/SnapPoint"), SubResource("Resource_kd52t")]] -block_serialized_properties = SubResource("Resource_e1awu") +} -[sub_resource type="Resource" id="Resource_ef7x8"] +[sub_resource type="Resource" id="Resource_bk4nw"] script = ExtResource("4_qtggh") name = &"if" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/MarginContainer/Rows/Row0/RowHBoxContainer/RowHBox/ParameterInput0/SnapPoint"), SubResource("Resource_dj7fd")], [NodePath("VBoxContainer/MarginContainer/Rows/SnapContainer0/SnapPoint"), SubResource("Resource_2lgdm")], [NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_ky5ld")]] -block_serialized_properties = SubResource("Resource_dnsl7") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_iue1k")]) +arguments = { +"condition": SubResource("Resource_cikin") +} -[sub_resource type="Resource" id="Resource_1bd3d"] +[sub_resource type="Resource" id="Resource_u4qvk"] script = ExtResource("4_qtggh") name = &"rigidbody2d_on_entered" -position = Vector2(50, 475) -path_child_pairs = [[NodePath("VBoxContainer/TopMarginContainer/MarginContainer/HBoxContainer/ParameterOutput0/SnapPoint"), SubResource("Resource_bgppn")], [NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_ef7x8")]] -block_serialized_properties = SubResource("Resource_7kl7g") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_pgmj0"), SubResource("Resource_bk4nw")]) +arguments = {} + +[sub_resource type="Resource" id="Resource_88olk"] +script = ExtResource("5_omlge") +root = SubResource("Resource_u4qvk") +canvas_position = Vector2(25, 450) [sub_resource type="Resource" id="Resource_6m2mk"] script = ExtResource("7_uuuue") script_inherits = "RigidBody2D" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_phaw7"), SubResource("Resource_ane3b"), SubResource("Resource_1bd3d")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_t5jjg"), SubResource("Resource_2q6mf"), SubResource("Resource_88olk")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends RigidBody2D +func _init(): + body_entered.connect(_on_body_entered) + func _ready(): var __sound_1 = AudioStreamPlayer.new() __sound_1.name = 'paddle_hit' __sound_1.set_stream(load('res://addons/block_code/examples/pong_game/assets/paddle_hit.ogg')) add_child(__sound_1) + var __sound_2 = AudioStreamPlayer.new() __sound_2.name = 'wall_hit' __sound_2.set_stream(load('res://addons/block_code/examples/pong_game/assets/wall_hit.ogg')) add_child(__sound_2) + var __sound_3 = AudioStreamPlayer.new() __sound_3.name = 'score_sound' __sound_3.set_stream(load('res://addons/block_code/examples/pong_game/assets/score.ogg')) add_child(__sound_3) + func reset(): PhysicsServer2D.body_set_state( get_rid(), PhysicsServer2D.BODY_STATE_TRANSFORM, - Transform2D.IDENTITY.translated((func (): var transform: Transform2D = get_viewport_transform(); var scale: Vector2 = transform.get_scale(); return -transform.origin / scale + get_viewport_rect().size / scale / 2).call()) + Transform2D.IDENTITY.translated(((func (): var transform: Transform2D = get_viewport_transform(); var scale: Vector2 = transform.get_scale(); return -transform.origin / scale + get_viewport_rect().size / scale / 2).call())) ) + var __sound_node_1 = get_node('score_sound') __sound_node_1.volume_db = 0 __sound_node_1.pitch_scale = 1 __sound_node_1.play() + func _on_body_entered(body: Node2D): - if body.is_in_group('paddles'): + if ((body).is_in_group('paddles')): var __sound_node_1 = get_node('paddle_hit') __sound_node_1.volume_db = 0 __sound_node_1.pitch_scale = 1 __sound_node_1.play() - if body.is_in_group('walls'): + + if ((body).is_in_group('walls')): var __sound_node_2 = get_node('wall_hit') __sound_node_2.volume_db = 0 __sound_node_2.pitch_scale = 1 __sound_node_2.play() -func _init(): - body_entered.connect(_on_body_entered) + " version = 0 -[sub_resource type="Resource" id="Resource_xxj4u"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_lnfkn"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_r1q4l"] -script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_lnfkn") - -[sub_resource type="Resource" id="Resource_ft8ne"] -script = ExtResource("5_wr38c") -block_class = &"ControlBlock" -serialized_props = [["scope", ""], ["param_input_strings_array", [{ -"condition": false -}]]] - -[sub_resource type="Resource" id="Resource_s06y5"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"group": "balls", -"node": "" -}]] - -[sub_resource type="Resource" id="Resource_5itae"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] +[sub_resource type="Resource" id="Resource_v1wmj"] +script = ExtResource("11_yafka") +name = &"area2d_on_entered_body" +arguments = {} -[sub_resource type="Resource" id="Resource_ht2bw"] -script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_5itae") - -[sub_resource type="Resource" id="Resource_iyvmc"] -script = ExtResource("4_qtggh") +[sub_resource type="Resource" id="Resource_vfj0r"] +script = ExtResource("11_yafka") name = &"is_node_in_group" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("MarginContainer/HBoxContainer/ParameterInput0/SnapPoint"), SubResource("Resource_ht2bw")]] -block_serialized_properties = SubResource("Resource_s06y5") - -[sub_resource type="Resource" id="Resource_eyu21"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"group": "scoring", -"method_name": "goal_left" -}]] - -[sub_resource type="Resource" id="Resource_5hog4"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +arguments = { "group": "balls", -"method_name": "reset" -}]] +"node": SubResource("Resource_v1wmj") +} -[sub_resource type="Resource" id="Resource_x6rc5"] +[sub_resource type="Resource" id="Resource_n8gnx"] script = ExtResource("4_qtggh") name = &"call_method_group" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_5hog4") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"group": "scoring", +"method_name": "goal_left" +} -[sub_resource type="Resource" id="Resource_s8f8n"] +[sub_resource type="Resource" id="Resource_bt4xb"] script = ExtResource("4_qtggh") name = &"call_method_group" -position = Vector2(20, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_x6rc5")]] -block_serialized_properties = SubResource("Resource_eyu21") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"group": "balls", +"method_name": "reset" +} -[sub_resource type="Resource" id="Resource_fs8jx"] +[sub_resource type="Resource" id="Resource_ckhru"] script = ExtResource("4_qtggh") name = &"if" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/MarginContainer/Rows/Row0/RowHBoxContainer/RowHBox/ParameterInput0/SnapPoint"), SubResource("Resource_iyvmc")], [NodePath("VBoxContainer/MarginContainer/Rows/SnapContainer0/SnapPoint"), SubResource("Resource_s8f8n")]] -block_serialized_properties = SubResource("Resource_ft8ne") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_n8gnx"), SubResource("Resource_bt4xb")]) +arguments = { +"condition": SubResource("Resource_vfj0r") +} -[sub_resource type="Resource" id="Resource_m4esq"] +[sub_resource type="Resource" id="Resource_0wq4q"] script = ExtResource("4_qtggh") name = &"area2d_on_entered" -position = Vector2(100, 350) -path_child_pairs = [[NodePath("VBoxContainer/TopMarginContainer/MarginContainer/HBoxContainer/ParameterOutput0/SnapPoint"), SubResource("Resource_r1q4l")], [NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_fs8jx")]] -block_serialized_properties = SubResource("Resource_xxj4u") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_ckhru")]) +arguments = {} + +[sub_resource type="Resource" id="Resource_5xtxj"] +script = ExtResource("5_omlge") +root = SubResource("Resource_0wq4q") +canvas_position = Vector2(0, 25) [sub_resource type="Resource" id="Resource_4xylj"] script = ExtResource("7_uuuue") script_inherits = "Area2D" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_m4esq")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_5xtxj")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends Area2D +func _init(): + body_entered.connect(_on_body_entered) + func _on_body_entered(body: Node2D): - if body.is_in_group('balls'): + if ((body).is_in_group('balls')): get_tree().call_group('scoring', 'goal_left') get_tree().call_group('balls', 'reset') -func _init(): - body_entered.connect(_on_body_entered) " version = 0 -[sub_resource type="Resource" id="Resource_tfqiy"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_5i6ga"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_oyktt"] -script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_5i6ga") - -[sub_resource type="Resource" id="Resource_pssgx"] -script = ExtResource("5_wr38c") -block_class = &"ControlBlock" -serialized_props = [["scope", ""], ["param_input_strings_array", [{ -"condition": false -}]]] - -[sub_resource type="Resource" id="Resource_2w0n0"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"group": "balls", -"node": "" -}]] - -[sub_resource type="Resource" id="Resource_q1iue"] -script = ExtResource("5_wr38c") -block_class = &"ParameterBlock" -serialized_props = [["block_name", &"parameter_block"], ["label", "Param"], ["color", Color(0.294118, 0.482353, 0.92549, 1)], ["block_type", 3], ["position", Vector2(0, 0)], ["scope", "func _on_body_entered(body: Node2D): -"], ["block_format", "body"], ["statement", "body"], ["defaults", {}], ["variant_type", 24], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_aaxrr"] -script = ExtResource("4_qtggh") -name = &"parameter_block" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_q1iue") +[sub_resource type="Resource" id="Resource_iwaix"] +script = ExtResource("11_yafka") +name = &"area2d_on_entered_body" +arguments = {} -[sub_resource type="Resource" id="Resource_005bn"] -script = ExtResource("4_qtggh") +[sub_resource type="Resource" id="Resource_imoq7"] +script = ExtResource("11_yafka") name = &"is_node_in_group" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("MarginContainer/HBoxContainer/ParameterInput0/SnapPoint"), SubResource("Resource_aaxrr")]] -block_serialized_properties = SubResource("Resource_2w0n0") - -[sub_resource type="Resource" id="Resource_1n3fn"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"group": "scoring", -"method_name": "goal_right" -}]] - -[sub_resource type="Resource" id="Resource_jr3es"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { +arguments = { "group": "balls", -"method_name": "reset" -}]] +"node": SubResource("Resource_iwaix") +} -[sub_resource type="Resource" id="Resource_6moia"] +[sub_resource type="Resource" id="Resource_uqjge"] script = ExtResource("4_qtggh") name = &"call_method_group" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_jr3es") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"group": "scoring", +"method_name": "goal_right" +} -[sub_resource type="Resource" id="Resource_gy7s5"] +[sub_resource type="Resource" id="Resource_s5k8u"] script = ExtResource("4_qtggh") name = &"call_method_group" -position = Vector2(20, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_6moia")]] -block_serialized_properties = SubResource("Resource_1n3fn") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"group": "balls", +"method_name": "reset" +} -[sub_resource type="Resource" id="Resource_2ys4x"] +[sub_resource type="Resource" id="Resource_utwdq"] script = ExtResource("4_qtggh") name = &"if" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/MarginContainer/Rows/Row0/RowHBoxContainer/RowHBox/ParameterInput0/SnapPoint"), SubResource("Resource_005bn")], [NodePath("VBoxContainer/MarginContainer/Rows/SnapContainer0/SnapPoint"), SubResource("Resource_gy7s5")]] -block_serialized_properties = SubResource("Resource_pssgx") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_uqjge"), SubResource("Resource_s5k8u")]) +arguments = { +"condition": SubResource("Resource_imoq7") +} -[sub_resource type="Resource" id="Resource_mi6hv"] +[sub_resource type="Resource" id="Resource_bm8bv"] script = ExtResource("4_qtggh") name = &"area2d_on_entered" -position = Vector2(75, 350) -path_child_pairs = [[NodePath("VBoxContainer/TopMarginContainer/MarginContainer/HBoxContainer/ParameterOutput0/SnapPoint"), SubResource("Resource_oyktt")], [NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_2ys4x")]] -block_serialized_properties = SubResource("Resource_tfqiy") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_utwdq")]) +arguments = {} + +[sub_resource type="Resource" id="Resource_pnsgy"] +script = ExtResource("5_omlge") +root = SubResource("Resource_bm8bv") +canvas_position = Vector2(50, 25) [sub_resource type="Resource" id="Resource_xoc8a"] script = ExtResource("7_uuuue") script_inherits = "Area2D" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_mi6hv")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_pnsgy")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends Area2D +func _init(): + body_entered.connect(_on_body_entered) + func _on_body_entered(body: Node2D): - if body.is_in_group('balls'): + if ((body).is_in_group('balls')): get_tree().call_group('scoring', 'goal_right') get_tree().call_group('balls', 'reset') -func _init(): - body_entered.connect(_on_body_entered) " version = 0 -[sub_resource type="Resource" id="Resource_n6aol"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_76s7d"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"score": 0 -}]] - -[sub_resource type="Resource" id="Resource_clf6h"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"score": 0 -}]] - -[sub_resource type="Resource" id="Resource_rpicc"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"group": "scoring" -}]] - -[sub_resource type="Resource" id="Resource_hxlka"] +[sub_resource type="Resource" id="Resource_o7ugj"] script = ExtResource("4_qtggh") -name = &"add_to_group" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_rpicc") +name = &"simplescoring_set_score_player_1" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"score": 0 +} -[sub_resource type="Resource" id="Resource_os82j"] +[sub_resource type="Resource" id="Resource_spykn"] script = ExtResource("4_qtggh") name = &"simplescoring_set_score_player_2" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_hxlka")]] -block_serialized_properties = SubResource("Resource_clf6h") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"score": 0 +} -[sub_resource type="Resource" id="Resource_2cci1"] +[sub_resource type="Resource" id="Resource_nucg0"] script = ExtResource("4_qtggh") -name = &"simplescoring_set_score_player_1" -position = Vector2(0, 0) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_os82j")]] -block_serialized_properties = SubResource("Resource_76s7d") +name = &"add_to_group" +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"group": "scoring" +} -[sub_resource type="Resource" id="Resource_e0qr3"] +[sub_resource type="Resource" id="Resource_6brri"] script = ExtResource("4_qtggh") name = &"ready" -position = Vector2(50, 25) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_2cci1")]] -block_serialized_properties = SubResource("Resource_n6aol") - -[sub_resource type="Resource" id="Resource_mhqvs"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"method_name": "goal_right" -}]] +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_o7ugj"), SubResource("Resource_spykn"), SubResource("Resource_nucg0")]) +arguments = {} -[sub_resource type="Resource" id="Resource_nd6ab"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"score": 1 -}]] +[sub_resource type="Resource" id="Resource_v71gd"] +script = ExtResource("5_omlge") +root = SubResource("Resource_6brri") +canvas_position = Vector2(25, 0) -[sub_resource type="Resource" id="Resource_m8e8y"] +[sub_resource type="Resource" id="Resource_4y2px"] script = ExtResource("4_qtggh") name = &"simplescoring_change_score_player_1" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_nd6ab") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"score": 1 +} -[sub_resource type="Resource" id="Resource_kr7eg"] +[sub_resource type="Resource" id="Resource_ilawr"] script = ExtResource("4_qtggh") name = &"define_method" -position = Vector2(50, 275) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_m8e8y")]] -block_serialized_properties = SubResource("Resource_mhqvs") - -[sub_resource type="Resource" id="Resource_3fbnf"] -script = ExtResource("5_wr38c") -block_class = &"EntryBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"method_name": "goal_left" -}]] +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_4y2px")]) +arguments = { +"method_name": &"goal_right" +} -[sub_resource type="Resource" id="Resource_7rc07"] -script = ExtResource("5_wr38c") -block_class = &"StatementBlock" -serialized_props = [["scope", ""], ["param_input_strings", { -"score": 1 -}]] +[sub_resource type="Resource" id="Resource_8e220"] +script = ExtResource("5_omlge") +root = SubResource("Resource_ilawr") +canvas_position = Vector2(25, 250) -[sub_resource type="Resource" id="Resource_7dmgm"] +[sub_resource type="Resource" id="Resource_1fx1n"] script = ExtResource("4_qtggh") name = &"simplescoring_change_score_player_2" -position = Vector2(0, 0) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_7rc07") +children = Array[ExtResource("4_qtggh")]([]) +arguments = { +"score": 1 +} -[sub_resource type="Resource" id="Resource_cnona"] +[sub_resource type="Resource" id="Resource_mef5h"] script = ExtResource("4_qtggh") name = &"define_method" -position = Vector2(50, 425) -path_child_pairs = [[NodePath("VBoxContainer/SnapPoint"), SubResource("Resource_7dmgm")]] -block_serialized_properties = SubResource("Resource_3fbnf") +children = Array[ExtResource("4_qtggh")]([SubResource("Resource_1fx1n")]) +arguments = { +"method_name": &"goal_left" +} + +[sub_resource type="Resource" id="Resource_uty0f"] +script = ExtResource("5_omlge") +root = SubResource("Resource_mef5h") +canvas_position = Vector2(25, 400) [sub_resource type="Resource" id="Resource_q418f"] script = ExtResource("7_uuuue") script_inherits = "SimpleScoring" -block_trees = Array[ExtResource("4_qtggh")]([SubResource("Resource_e0qr3"), SubResource("Resource_kr7eg"), SubResource("Resource_cnona")]) +block_serialization_trees = Array[ExtResource("5_omlge")]([SubResource("Resource_v71gd"), SubResource("Resource_8e220"), SubResource("Resource_uty0f")]) variables = Array[ExtResource("9_lo3p1")]([]) generated_script = "extends SimpleScoring diff --git a/addons/block_code/instruction_tree/instruction_tree.gd b/addons/block_code/instruction_tree/instruction_tree.gd deleted file mode 100644 index 0b6d5011..00000000 --- a/addons/block_code/instruction_tree/instruction_tree.gd +++ /dev/null @@ -1,58 +0,0 @@ -extends Object - - -class TreeNode: - var data: String - var children: Array[TreeNode] - var next: TreeNode - - func _init(_data: String): - data = _data - - func add_child(node: TreeNode): - children.append(node) - - -class IDHandler: - static var counts: Dictionary = {} - - static func reset(): - counts = {} - - static func get_unique_id(str: String) -> int: - if not counts.has(str): - counts[str] = 0 - - counts[str] += 1 - - return counts[str] - - static func make_unique(formatted_string: String) -> String: - var unique_string = formatted_string - var regex = RegEx.new() - regex.compile("\\b__[^\\s]+") - var ids: Dictionary = {} - for result in regex.search_all(formatted_string): - var result_string = result.get_string() - if not ids.has(result_string): - ids[result_string] = get_unique_id(result_string) - unique_string = unique_string.replace(result_string, result_string + "_%d" % ids[result_string]) - - return unique_string - - -static func generate_text(root_node: TreeNode, start_depth: int = 0) -> String: - var out = PackedStringArray() - generate_text_recursive(root_node, start_depth, out) - return "".join(out) - - -static func generate_text_recursive(node: TreeNode, depth: int, out: PackedStringArray): - if node.data != "": - out.append("\t".repeat(depth) + node.data + "\n") - - for c in node.children: - generate_text_recursive(c, depth + 1, out) - - if node.next: - generate_text_recursive(node.next, depth, out) diff --git a/addons/block_code/serialization/block_script_serialization.gd b/addons/block_code/serialization/block_script_serialization.gd index 74be0856..02d61d22 100644 --- a/addons/block_code/serialization/block_script_serialization.gd +++ b/addons/block_code/serialization/block_script_serialization.gd @@ -1,16 +1,154 @@ +@tool class_name BlockScriptSerialization extends Resource +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 BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd") +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") +const BlockSerialization = preload("res://addons/block_code/serialization/block_serialization.gd") +const BlockSerializationTree = preload("res://addons/block_code/serialization/block_serialization_tree.gd") +const ValueBlockSerialization = preload("res://addons/block_code/serialization/value_block_serialization.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") + @export var script_inherits: String -@export var block_trees: Array[BlockSerialization] -@export var variables: Array[VariableResource] +@export var block_serialization_trees: Array[BlockSerializationTree] +@export var variables: Array[VariableDefinition]: + set = _set_variables @export var generated_script: String @export var version: int +var _var_block_definitions: Dictionary # String, BlockDefinition + -func _init(p_script_inherits: String = "", p_block_trees: Array[BlockSerialization] = [], p_variables: Array[VariableResource] = [], p_generated_script: String = "", p_version = 0): +func _init( + p_script_inherits: String = "", p_block_serialization_trees: Array[BlockSerializationTree] = [], p_variables: Array[VariableDefinition] = [], p_generated_script: String = "", p_version = 0 +): script_inherits = p_script_inherits - block_trees = p_block_trees + block_serialization_trees = p_block_serialization_trees generated_script = p_generated_script variables = p_variables version = p_version + + +func _set_variables(value): + variables = value + _refresh_var_block_definitions() + + +func _refresh_var_block_definitions(): + _var_block_definitions.clear() + for block_def in BlocksCatalog.get_variable_block_definitions(variables): + _var_block_definitions[block_def.name] = block_def + + +func _get_block(block_name: StringName) -> BlockDefinition: + var block_def: BlockDefinition = _var_block_definitions.get(block_name) + if block_def == null: + block_def = BlocksCatalog.get_block(block_name) + return block_def + + +func get_definitions() -> Array[BlockDefinition]: + for class_dict in ProjectSettings.get_global_class_list(): + if class_dict.class == script_inherits: + var script = load(class_dict.path) + if script.has_method("setup_custom_blocks"): + script.setup_custom_blocks() + break + + return BlocksCatalog.get_inherited_blocks(script_inherits) + + +func get_categories() -> Array[BlockCategory]: + for class_dict in ProjectSettings.get_global_class_list(): + if class_dict.class == script_inherits: + var script = load(class_dict.path) + if script.has_method("get_custom_categories"): + return script.get_custom_categories() + + return [] + + +func generate_ast_list() -> ASTList: + var ast_list := ASTList.new() + for tree in block_serialization_trees: + var ast: BlockAST = _tree_to_ast(tree) + ast_list.append(ast, tree.canvas_position) + return ast_list + + +func _tree_to_ast(tree: BlockSerializationTree) -> BlockAST: + var ast: BlockAST = BlockAST.new() + ast.root = _block_to_ast_node(tree.root) + return ast + + +func _block_to_ast_node(node: BlockSerialization) -> BlockAST.ASTNode: + var ast_node := BlockAST.ASTNode.new() + ast_node.data = _get_block(node.name) + + for arg_name in node.arguments: + var argument = node.arguments[arg_name] + if argument is ValueBlockSerialization: + argument = _value_to_ast_value(argument) + ast_node.arguments[arg_name] = argument + + var children: Array[BlockAST.ASTNode] + for c in node.children: + children.append(_block_to_ast_node(c)) + ast_node.children = children + + return ast_node + + +func _value_to_ast_value(value_node: ValueBlockSerialization) -> BlockAST.ASTValueNode: + var ast_value_node := BlockAST.ASTValueNode.new() + ast_value_node.data = _get_block(value_node.name) + + for arg_name in value_node.arguments: + var argument = value_node.arguments[arg_name] + if argument is ValueBlockSerialization: + argument = _value_to_ast_value(argument) + ast_value_node.arguments[arg_name] = argument + + return ast_value_node + + +func update_from_ast_list(ast_list: ASTList): + var trees: Array[BlockSerializationTree] + for ast_pair in ast_list.array: + var root := _block_from_ast_node(ast_pair.ast.root) + var tree := BlockSerializationTree.new(root, ast_pair.canvas_position) + trees.append(tree) + block_serialization_trees = trees + + +func _block_from_ast_node(ast_node: BlockAST.ASTNode) -> BlockSerialization: + var block := BlockSerialization.new(ast_node.data.name) + + for arg_name in ast_node.arguments: + var argument = ast_node.arguments[arg_name] + if argument is BlockAST.ASTValueNode: + argument = _value_from_ast_value(argument) + block.arguments[arg_name] = argument + + var children: Array[BlockSerialization] + for c in ast_node.children: + children.append(_block_from_ast_node(c)) + block.children = children + + return block + + +func _value_from_ast_value(ast_node: BlockAST.ASTValueNode) -> ValueBlockSerialization: + var value := ValueBlockSerialization.new(ast_node.data.name) + + for arg_name in ast_node.arguments: + var argument = ast_node.arguments[arg_name] + if argument is BlockAST.ASTValueNode: + argument = _value_from_ast_value(argument) + value.arguments[arg_name] = argument + + return value diff --git a/addons/block_code/serialization/block_serialization.gd b/addons/block_code/serialization/block_serialization.gd index 433ca567..5a62b008 100644 --- a/addons/block_code/serialization/block_serialization.gd +++ b/addons/block_code/serialization/block_serialization.gd @@ -1,16 +1,13 @@ -class_name BlockSerialization extends Resource -@export var name: StringName -@export var position: Vector2 -@export var path_child_pairs: Array +const BlockSerialization = preload("res://addons/block_code/serialization/block_serialization.gd") -# TODO: Remove once the data/UI decouple is done. -@export var block_serialized_properties: BlockSerializedProperties +@export var name: StringName +@export var children: Array[BlockSerialization] +@export var arguments: Dictionary # String, ValueBlockSerialization -func _init(p_name: StringName, p_position: Vector2 = Vector2.ZERO, p_block_serialized_properties: BlockSerializedProperties = null, p_path_child_pairs: Array = []): +func _init(p_name: StringName = &"", p_children: Array[BlockSerialization] = [], p_arguments: Dictionary = {}): name = p_name - position = p_position - block_serialized_properties = p_block_serialized_properties - path_child_pairs = p_path_child_pairs + children = p_children + arguments = p_arguments diff --git a/addons/block_code/serialization/block_serialization_tree.gd b/addons/block_code/serialization/block_serialization_tree.gd new file mode 100644 index 00000000..1da2babe --- /dev/null +++ b/addons/block_code/serialization/block_serialization_tree.gd @@ -0,0 +1,24 @@ +extends Resource + +const BlockSerialization = preload("res://addons/block_code/serialization/block_serialization.gd") + +@export var root: BlockSerialization +@export var canvas_position: Vector2 + + +func _init(p_root: BlockSerialization = null, p_canvas_position: Vector2 = Vector2(0, 0)): + root = p_root + canvas_position = p_canvas_position + + +func _to_string(): + return to_string_recursive(root, 0) + + +func to_string_recursive(node: BlockSerialization, depth: int) -> String: + var string: String = "%s %s\n" % ["-".repeat(depth), node.block_name] + + for c in node.children: + string += to_string_recursive(c, depth + 1) + + return string diff --git a/addons/block_code/serialization/block_serialized_properties.gd b/addons/block_code/serialization/block_serialized_properties.gd deleted file mode 100644 index 2841c6ca..00000000 --- a/addons/block_code/serialization/block_serialized_properties.gd +++ /dev/null @@ -1,12 +0,0 @@ -class_name BlockSerializedProperties -extends Resource - -# TODO: Remove this class after removing the remaining serialization. - -@export var block_class: StringName -@export var serialized_props: Array - - -func _init(p_block_class: StringName = "", p_serialized_props: Array = []): - block_class = p_block_class - serialized_props = p_serialized_props diff --git a/addons/block_code/serialization/default_block_script.tres b/addons/block_code/serialization/default_block_script.tres index bb6cc2ce..438e3fd5 100644 --- a/addons/block_code/serialization/default_block_script.tres +++ b/addons/block_code/serialization/default_block_script.tres @@ -1,25 +1,25 @@ -[gd_resource type="Resource" script_class="BlockScriptSerialization" load_steps=6 format=3 uid="uid://dit7fykhl3h48"] +[gd_resource type="Resource" script_class="BlockScriptSerialization" load_steps=7 format=3 uid="uid://i7adsp6x51ml"] -[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialization.gd" id="1_barc5"] -[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialized_properties.gd" id="2_cgfpx"] -[ext_resource type="Script" path="res://addons/block_code/serialization/block_script_serialization.gd" id="4_cqq7x"] +[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialization_tree.gd" id="1_opywe"] +[ext_resource type="Script" path="res://addons/block_code/serialization/block_serialization.gd" id="2_h8ehk"] +[ext_resource type="Script" path="res://addons/block_code/serialization/block_script_serialization.gd" id="2_yjlcv"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/variable_definition.gd" id="3_wb2fg"] -[sub_resource type="Resource" id="Resource_b0aen"] -script = ExtResource("2_cgfpx") -block_class = &"EntryBlock" -serialized_props = [["color", Color(0.92549, 0.231373, 0.34902, 1)], ["scope", ""], ["param_input_strings", {}]] - -[sub_resource type="Resource" id="Resource_1h6wi"] -script = ExtResource("1_barc5") +[sub_resource type="Resource" id="Resource_oalom"] +script = ExtResource("2_h8ehk") name = &"ready" -position = Vector2(54, 47) -path_child_pairs = [] -block_serialized_properties = SubResource("Resource_b0aen") +children = Array[ExtResource("2_h8ehk")]([]) +arguments = {} + +[sub_resource type="Resource" id="Resource_8sqy5"] +script = ExtResource("1_opywe") +root = SubResource("Resource_oalom") +canvas_position = Vector2(54, 47) [resource] -script = ExtResource("4_cqq7x") +script = ExtResource("2_yjlcv") script_inherits = "INHERIT_DEFAULT" -block_trees = Array[ExtResource("1_barc5")]([SubResource("Resource_1h6wi")]) -variables = Array[Resource("res://addons/block_code/ui/block_canvas/variable_resource.gd")]([]) +block_serialization_trees = Array[ExtResource("1_opywe")]([SubResource("Resource_8sqy5")]) +variables = Array[ExtResource("3_wb2fg")]([]) generated_script = "extends INHERIT_DEFAULT" version = 0 diff --git a/addons/block_code/serialization/value_block_serialization.gd b/addons/block_code/serialization/value_block_serialization.gd new file mode 100644 index 00000000..6aea5a77 --- /dev/null +++ b/addons/block_code/serialization/value_block_serialization.gd @@ -0,0 +1,9 @@ +extends Resource + +@export var name: StringName +@export var arguments: Dictionary # String, ValueBlockSerialization + + +func _init(p_name: StringName = &"", p_arguments: Dictionary = {}): + name = p_name + arguments = p_arguments diff --git a/addons/block_code/simple_nodes/simple_character/simple_character.gd b/addons/block_code/simple_nodes/simple_character/simple_character.gd index dffaff35..9e2012e4 100644 --- a/addons/block_code/simple_nodes/simple_character/simple_character.gd +++ b/addons/block_code/simple_nodes/simple_character/simple_character.gd @@ -4,6 +4,7 @@ extends CharacterBody2D const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") const Types = preload("res://addons/block_code/types/types.gd") @export var texture: Texture2D: diff --git a/addons/block_code/types/types.gd b/addons/block_code/types/types.gd index aab4bf79..99f64c36 100644 --- a/addons/block_code/types/types.gd +++ b/addons/block_code/types/types.gd @@ -18,6 +18,7 @@ const VARIANT_TYPE_TO_STRING: Dictionary = { TYPE_NODE_PATH: "NODE_PATH", TYPE_OBJECT: "OBJECT", TYPE_NIL: "NIL", + TYPE_STRING_NAME: "STRING_NAME", } const STRING_TO_VARIANT_TYPE: Dictionary = { @@ -30,6 +31,7 @@ const STRING_TO_VARIANT_TYPE: Dictionary = { "NODE_PATH": TYPE_NODE_PATH, "OBJECT": TYPE_OBJECT, "NIL": TYPE_NIL, + "STRING_NAME": TYPE_STRING_NAME, } const cast_relationships = [ diff --git a/addons/block_code/ui/block_canvas/block_canvas.gd b/addons/block_code/ui/block_canvas/block_canvas.gd index ffcc4b86..c3095170 100644 --- a/addons/block_code/ui/block_canvas/block_canvas.gd +++ b/addons/block_code/ui/block_canvas/block_canvas.gd @@ -1,10 +1,12 @@ @tool 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 BlockCodePlugin = preload("res://addons/block_code/block_code_plugin.gd") const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.gd") -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") +const ScriptGenerator = preload("res://addons/block_code/code_generation/script_generator.gd") const Util = preload("res://addons/block_code/ui/util.gd") const EXTEND_MARGIN: float = 800 @@ -34,7 +36,7 @@ const ZOOM_FACTOR: float = 1.1 @onready var _zoom_button: Button = %ZoomButton var _current_block_script: BlockScriptSerialization -var _block_scenes_by_class = {} +var _current_ast_list: ASTList var _panning := false var zoom: float: set(value): @@ -52,17 +54,6 @@ signal replace_block_code func _ready(): if not _open_scene_button.icon and not Util.node_is_part_of_edited_scene(self): _open_scene_button.icon = _open_scene_icon - _populate_block_scenes_by_class() - - -func _populate_block_scenes_by_class(): - for _class in ProjectSettings.get_global_class_list(): - if not _class.base.ends_with("Block"): - continue - var _script = load(_class.path) - if not _script.has_method("get_scene_path"): - continue - _block_scenes_by_class[_class.class] = _script.get_scene_path() func add_block(block: Block, position: Vector2 = Vector2.ZERO) -> void: @@ -143,8 +134,62 @@ func block_script_selected(block_script: BlockScriptSerialization): func _load_block_script(block_script: BlockScriptSerialization): - for tree in block_script.block_trees: - load_tree(_window, tree) + _current_ast_list = block_script.generate_ast_list() + reload_ui_from_ast_list() + + +func reload_ui_from_ast_list(): + for ast_pair in _current_ast_list.array: + var root_block = ui_tree_from_ast_node(ast_pair.ast.root) + root_block.position = ast_pair.canvas_position + _window.add_child(root_block) + + +func ui_tree_from_ast_node(ast_node: BlockAST.ASTNode) -> Block: + var block: Block = Util.instantiate_block(ast_node.data) + block.color = Util.get_category_color(ast_node.data.category) + # Args + for arg_name in ast_node.arguments: + var argument = ast_node.arguments[arg_name] + if argument is BlockAST.ASTValueNode: + var value_block = ui_tree_from_ast_value_node(argument) + block.args_to_add_after_format[arg_name] = value_block + else: # Argument is not a node, but a user input value + block.args_to_add_after_format[arg_name] = argument + + # Children + var current_block: Block = block + + var i: int = 0 + for c in ast_node.children: + var child_block: Block = ui_tree_from_ast_node(c) + + if i == 0: + current_block.child_snap.add_child(child_block) + else: + current_block.bottom_snap.add_child(child_block) + + current_block = child_block + i += 1 + + reconnect_block.emit(block) + return block + + +func ui_tree_from_ast_value_node(ast_value_node: BlockAST.ASTValueNode) -> Block: + var block: Block = Util.instantiate_block(ast_value_node.data) + block.color = Util.get_category_color(ast_value_node.data.category) + # Args + for arg_name in ast_value_node.arguments: + var argument = ast_value_node.arguments[arg_name] + if argument is BlockAST.ASTValueNode: + var value_block = ui_tree_from_ast_value_node(argument) + block.args_to_add_after_format[arg_name] = value_block + else: # Argument is not a node, but a user input value + block.args_to_add_after_format[arg_name] = argument + + reconnect_block.emit(block) + return block func clear_canvas(): @@ -153,50 +198,68 @@ func clear_canvas(): child.queue_free() -func load_tree(parent: Node, node: BlockSerialization): - var scene: Block = Util.instantiate_block_by_name(node.name) +func rebuild_ast_list(): + _current_ast_list.clear() - # TODO: Remove once the data/UI decouple is done. - if scene == null: - var _block_scene_path = _block_scenes_by_class[node.block_serialized_properties.block_class] - scene = load(_block_scene_path).instantiate() - for prop_pair in node.block_serialized_properties.serialized_props: - scene.set(prop_pair[0], prop_pair[1]) + for c in _window.get_children(): + if c is StatementBlock: + var root: BlockAST.ASTNode = build_ast(c) + var ast: BlockAST = BlockAST.new() + ast.root = root + _current_ast_list.append(ast, c.position) + + +func build_ast(block: Block) -> BlockAST.ASTNode: + var ast_node := BlockAST.ASTNode.new() + ast_node.data = block.definition + + for arg_name in block.arg_name_to_param_input_dict: + var param_input = block.arg_name_to_param_input_dict[arg_name] + var snap_point = param_input.snap_point + var snapped_block = snap_point.get_snapped_block() + if snapped_block: + ast_node.arguments[arg_name] = build_value_ast(snapped_block) + else: + ast_node.arguments[arg_name] = param_input.get_raw_input() - scene.position = node.position - scene.resource = node - parent.add_child(scene) + var children: Array[BlockAST.ASTNode] = [] - var scene_block: Block = scene as Block - reconnect_block.emit(scene_block) + if block.child_snap: + var child: Block = block.child_snap.get_snapped_block() - for c in node.path_child_pairs: - load_tree(scene.get_node(c[0]), c[1]) + while child != null: + var child_ast_node := build_ast(child) + child_ast_node.data = child.definition + children.append(child_ast_node) + if child.bottom_snap == null: + child = null + else: + child = child.bottom_snap.get_snapped_block() -func rebuild_block_trees(undo_redo): - var block_trees: Array[BlockSerialization] - for c in _window.get_children(): - block_trees.append(build_tree(c, undo_redo)) - undo_redo.add_undo_property(_current_block_script, "block_trees", _current_block_script.block_trees) - undo_redo.add_do_property(_current_block_script, "block_trees", block_trees) + ast_node.children = children + return ast_node -func build_tree(block: Block, undo_redo: EditorUndoRedoManager) -> BlockSerialization: - var path_child_pairs = [] - block.update_resources(undo_redo) - for snap in find_snaps(block): - var snapped_block = snap.get_snapped_block() - if snapped_block == null: - continue - path_child_pairs.append([block.get_path_to(snap), build_tree(snapped_block, undo_redo)]) +func build_value_ast(block: ParameterBlock) -> BlockAST.ASTValueNode: + var ast_node := BlockAST.ASTValueNode.new() + ast_node.data = block.definition + + for arg_name in block.arg_name_to_param_input_dict: + var param_input = block.arg_name_to_param_input_dict[arg_name] + var snap_point = param_input.snap_point + var snapped_block = snap_point.get_snapped_block() + if snapped_block: + ast_node.arguments[arg_name] = build_value_ast(snapped_block) + else: + ast_node.arguments[arg_name] = param_input.get_raw_input() + + return ast_node - if block.resource.path_child_pairs != path_child_pairs: - undo_redo.add_undo_property(block.resource, "path_child_pairs", block.resource.path_child_pairs) - undo_redo.add_do_property(block.resource, "path_child_pairs", path_child_pairs) - return block.resource +func rebuild_block_serialization_trees(): + _current_block_script.update_from_ast_list(_current_ast_list) func find_snaps(node: Node) -> Array[SnapPoint]: @@ -216,7 +279,7 @@ func set_scope(scope: String): var valid := false if block is EntryBlock: - if scope == block.get_entry_statement(): + if scope == block.definition.code_template: valid = true else: var tree_scope := BlockTreeUtil.get_tree_scope(block) @@ -319,9 +382,8 @@ func set_mouse_override(override: bool): _mouse_override.mouse_default_cursor_shape = Control.CURSOR_ARROW -func generate_script_from_current_window(block_script: BlockScriptSerialization) -> String: - # TODO: implement multiple windows - return BlockTreeUtil.generate_script_from_nodes(_window.get_children(), block_script) +func generate_script_from_current_window() -> String: + return ScriptGenerator.generate_script(_current_ast_list, _current_block_script) func _on_zoom_button_pressed(): diff --git a/addons/block_code/ui/block_tree_util.gd b/addons/block_code/ui/block_tree_util.gd index d5ebf718..4cc49898 100644 --- a/addons/block_code/ui/block_tree_util.gd +++ b/addons/block_code/ui/block_tree_util.gd @@ -1,77 +1,11 @@ extends Object -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") - - -static func generate_script_from_nodes(nodes: Array[Node], block_script: BlockScriptSerialization) -> String: - var entry_blocks_by_entry_statement: Dictionary = {} - - for block in nodes: - if !(block is Block): - continue - - if block is EntryBlock: - var entry_statement = block.get_entry_statement() - if not entry_blocks_by_entry_statement.has(entry_statement): - entry_blocks_by_entry_statement[entry_statement] = [] - entry_blocks_by_entry_statement[entry_statement].append(block) - - var script: String = "" - - script += "extends %s\n\n" % block_script.script_inherits - - for variable in block_script.variables: - script += "var %s: %s\n\n" % [variable.var_name, type_string(variable.var_type)] - - script += "\n" - - var init_func = InstructionTree.TreeNode.new("func _init():") - - for entry_statement in entry_blocks_by_entry_statement: - var entry_blocks: Array[EntryBlock] - entry_blocks.assign(entry_blocks_by_entry_statement[entry_statement]) - script += _generate_script_from_entry_blocks(entry_statement, entry_blocks, init_func) - - if init_func.children: - script += InstructionTree.generate_text(init_func) - - return script - - -static func _generate_script_from_entry_blocks(entry_statement: String, entry_blocks: Array[EntryBlock], init_func: InstructionTree.TreeNode) -> String: - var script = entry_statement + "\n" - var signal_node: InstructionTree.TreeNode - var is_empty = true - - InstructionTree.IDHandler.reset() - - for entry_block in entry_blocks: - var next_block := entry_block.bottom_snap.get_snapped_block() - - if next_block != null: - var instruction_node: InstructionTree.TreeNode = next_block.get_instruction_node() - var to_append := InstructionTree.generate_text(instruction_node, 1) - script += to_append - script += "\n" - is_empty = false - - if signal_node == null and entry_block.signal_name: - signal_node = InstructionTree.TreeNode.new("{0}.connect(_on_{0})".format([entry_block.signal_name])) - - if signal_node: - init_func.add_child(signal_node) - - if is_empty: - script += "\tpass\n\n" - - return script - ## Returns the scope of the first non-empty scope child block static func get_tree_scope(node: Node) -> String: if node is Block: - if node.scope != "": - return node.scope + if node.definition.scope != "": + return node.definition.scope for c in node.get_children(): var scope := get_tree_scope(c) diff --git a/addons/block_code/ui/blocks/block/block.gd b/addons/block_code/ui/blocks/block/block.gd index 3a4e056a..741d31c4 100644 --- a/addons/block_code/ui/blocks/block/block.gd +++ b/addons/block_code/ui/blocks/block/block.gd @@ -2,55 +2,38 @@ class_name Block extends MarginContainer -const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") -const Types = preload("res://addons/block_code/types/types.gd") +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") signal drag_started(block: Block) signal modified -## Name of the block to be referenced by others in search -@export var block_name: StringName - -## Label of block (optionally used to draw block labels) -@export var label: String = "" - ## Color of block (optionally used to draw block color) @export var color: Color = Color(1., 1., 1.) -## Type of block to check if can be attached to snap point -@export var block_type: Types.BlockType = Types.BlockType.STATEMENT - ## Category to add the block to @export var category: String +# FIXME Note: This used to be a NodePath. There is a bug in Godot 4.2 that causes the +# reference to not be set properly when the node is duplicated. Since we don't +# use the Node duplicate function anymore, this is okay. +# https://github.com/godotengine/godot/issues/82670 ## The next block in the line of execution (can be null if end) -@export var bottom_snap_path: NodePath: - set = _set_bottom_snap_path +@export var bottom_snap: SnapPoint = null + +## Snap point that holds blocks that should be nested under this block +@export var child_snap: SnapPoint = null ## The scope of the block (statement of matching entry block) @export var scope: String = "" -## The resource containing the block properties and the snapped blocks -@export var resource: BlockSerialization - -# FIXME: Add export to this variable and remove bottom_snap_path above. -# There is a bug in Godot 4.2 that prevents using SnapPoint directly: -# https://github.com/godotengine/godot/issues/82670 -var bottom_snap: SnapPoint +## The resource containing the definition of the block +@export var definition: BlockDefinition ## Whether the block can be deleted by the Delete key. var can_delete: bool = true -func _set_bottom_snap_path(value: NodePath): - bottom_snap_path = value - bottom_snap = get_node_or_null(bottom_snap_path) - - func _ready(): - if bottom_snap == null: - _set_bottom_snap_path(bottom_snap_path) focus_mode = FocusMode.FOCUS_ALL mouse_filter = Control.MOUSE_FILTER_IGNORE @@ -103,54 +86,8 @@ func disconnect_signals(): drag_started.disconnect(c.callable) -# Override this method to create custom block functionality -func get_instruction_node() -> InstructionTree.TreeNode: - var node: InstructionTree.TreeNode = InstructionTree.TreeNode.new("") - - if bottom_snap: - var snapped_block: Block = bottom_snap.get_snapped_block() - if snapped_block: - node.next = snapped_block.get_instruction_node() - - return node - - -func update_resources(undo_redo: EditorUndoRedoManager): - if resource == null: - var block_serialized_properties = BlockSerializedProperties.new(get_block_class(), get_serialized_props()) - resource = BlockSerialization.new(block_name, position, block_serialized_properties) - return - - if resource.position != position: - undo_redo.add_undo_property(resource, "position", resource.position) - undo_redo.add_do_property(resource, "position", position) - - var serialized_props = get_serialized_props() - - if serialized_props != resource.block_serialized_properties.serialized_props: - undo_redo.add_undo_property(resource.block_serialized_properties, "serialized_props", resource.block_serialized_properties.serialized_props) - undo_redo.add_do_property(resource.block_serialized_properties, "serialized_props", serialized_props) - - -# Override this method to add more serialized properties -func get_serialized_props() -> Array: - if not BlocksCatalog.has_block(block_name): - return serialize_props(["block_name", "label", "color", "block_type", "position", "scope"]) - - # TODO: Remove remaining serialization: - # - Handle scope in a different way? - return serialize_props(["scope"]) - - func _to_string(): - return "<{block_class}:{block_name}#{rid}>".format({"block_name": block_name, "block_class": get_block_class(), "rid": get_instance_id()}) - - -func serialize_props(prop_names: Array) -> Array: - var pairs := [] - for p in prop_names: - pairs.append([p, self.get(p)]) - return pairs + return "<{block_class}:{block_name}#{rid}>".format({"block_name": definition.name, "block_class": get_block_class(), "rid": get_instance_id()}) func _make_custom_tooltip(for_text) -> Control: diff --git a/addons/block_code/ui/blocks/control_block/control_block.gd b/addons/block_code/ui/blocks/control_block/control_block.gd index 0e40f101..c4d4590b 100644 --- a/addons/block_code/ui/blocks/control_block/control_block.gd +++ b/addons/block_code/ui/blocks/control_block/control_block.gd @@ -2,89 +2,38 @@ class_name ControlBlock extends Block -const Background = preload("res://addons/block_code/ui/blocks/utilities/background/background.gd") const Constants = preload("res://addons/block_code/ui/constants.gd") -const DragDropArea = preload("res://addons/block_code/ui/blocks/utilities/drag_drop_area/drag_drop_area.gd") -const Gutter = preload("res://addons/block_code/ui/blocks/utilities/background/gutter.gd") -@export var block_formats: Array = [] -@export var statements: Array = [] -@export var defaults: Dictionary = {} - -var snaps: Array -var param_name_input_pairs_array: Array -var param_input_strings_array: Array # Only loaded from serialized +var arg_name_to_param_input_dict: Dictionary +var args_to_add_after_format: Dictionary # Only used when loading func _ready(): super() + %TopBackground.color = color + %TopBackground.shift_bottom = Constants.CONTROL_MARGIN + %BottomBackground.color = color + %BottomBackground.shift_top = Constants.CONTROL_MARGIN + %SnapPoint.add_theme_constant_override("margin_left", Constants.CONTROL_MARGIN) + %SnapGutter.color = color + %SnapGutter.custom_minimum_size.x = Constants.CONTROL_MARGIN + format() - if param_input_strings_array: - for i in param_name_input_pairs_array.size(): - for pair in param_name_input_pairs_array[i]: - pair[1].set_raw_input(param_input_strings_array[i][pair[0]]) + for arg_name in arg_name_to_param_input_dict: + if arg_name in args_to_add_after_format: + var argument = args_to_add_after_format[arg_name] + if argument is Block: + arg_name_to_param_input_dict[arg_name].snap_point.add_child(argument) + else: + arg_name_to_param_input_dict[arg_name].set_raw_input(argument) func _on_drag_drop_area_mouse_down(): _drag_started() -# Override this method to create custom block functionality -func get_instruction_node() -> InstructionTree.TreeNode: - var root: InstructionTree.TreeNode = InstructionTree.TreeNode.new("") - var node: InstructionTree.TreeNode - - for i in snaps.size(): - var snap: SnapPoint = snaps[i] - var formatted_statement: String = statements[i] - - for pair in param_name_input_pairs_array[i]: - formatted_statement = formatted_statement.replace("{%s}" % pair[0], pair[1].get_string()) - - formatted_statement = InstructionTree.IDHandler.make_unique(formatted_statement) - - var new_node := InstructionTree.TreeNode.new(formatted_statement) - if i == 0: - node = new_node - root = node - else: - node.next = new_node - node = node.next - - var snapped_block: Block = snap.get_snapped_block() - if snapped_block: - node.add_child(snapped_block.get_instruction_node()) - else: - node.add_child(InstructionTree.TreeNode.new("pass")) -# - if bottom_snap: - var snapped_block: Block = bottom_snap.get_snapped_block() - if snapped_block: - node.next = snapped_block.get_instruction_node() - - return root - - -func get_serialized_props() -> Array: - var props := super() - if not BlocksCatalog.has_block(block_name): - props.append_array(serialize_props(["block_formats", "statements", "defaults"])) - - var _param_input_strings_array = [] - for param_name_input_pairs in param_name_input_pairs_array: - var _param_input_strings: Dictionary = {} - - for pair in param_name_input_pairs: - _param_input_strings[pair[0]] = pair[1].get_raw_input() - - _param_input_strings_array.append(_param_input_strings) - - props.append(["param_input_strings_array", _param_input_strings_array]) - return props - - static func get_block_class(): return "ControlBlock" @@ -94,78 +43,4 @@ static func get_scene_path(): func format(): - snaps = [] - param_name_input_pairs_array = [] - - if block_formats.size() == 0: - return - - for i in block_formats.size(): - var row := MarginContainer.new() - row.name = "Row%d" % i - row.custom_minimum_size.x = 80 - row.custom_minimum_size.y = 30 - row.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN - - var bg := Background.new() - bg.name = "Background" - bg.color = color - if i != 0: - bg.shift_top = Constants.CONTROL_MARGIN - bg.shift_bottom = Constants.CONTROL_MARGIN - row.add_child(bg) - - if i == 0: - var drag_drop: DragDropArea = preload("res://addons/block_code/ui/blocks/utilities/drag_drop_area/drag_drop_area.tscn").instantiate() - row.add_child(drag_drop) - drag_drop.mouse_down.connect(_drag_started) - - var row_hbox_container := MarginContainer.new() - row_hbox_container.name = "RowHBoxContainer" - row_hbox_container.add_theme_constant_override("margin_left", 10) - row_hbox_container.add_theme_constant_override("margin_right", 6) - row_hbox_container.add_theme_constant_override("margin_top", 12) - row_hbox_container.add_theme_constant_override("margin_bottom", 6) - row_hbox_container.mouse_filter = Control.MOUSE_FILTER_IGNORE - row.add_child(row_hbox_container) - - var row_hbox := HBoxContainer.new() - row_hbox.name = "RowHBox" - row_hbox.add_theme_constant_override("separation", 0) - row_hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE - row_hbox_container.add_child(row_hbox) - - param_name_input_pairs_array.append(StatementBlock.format_string(self, row_hbox, block_formats[i], defaults)) - - %Rows.add_child(row) - - var snap_container := MarginContainer.new() - snap_container.name = "SnapContainer%d" % i - snap_container.mouse_filter = Control.MOUSE_FILTER_IGNORE - snap_container.custom_minimum_size.x = 30 - snap_container.custom_minimum_size.y = 30 - snap_container.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN - - var snap_gutter := Gutter.new() - snap_gutter.name = "Background" - snap_gutter.custom_minimum_size.x = Constants.CONTROL_MARGIN - snap_gutter.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN - snap_gutter.size_flags_vertical = Control.SIZE_EXPAND_FILL - snap_gutter.color = color - snap_container.add_child(snap_gutter) - - var snap_point: SnapPoint = preload("res://addons/block_code/ui/blocks/utilities/snap_point/snap_point.tscn").instantiate() - snap_point.add_theme_constant_override("margin_left", Constants.CONTROL_MARGIN) - snap_container.add_child(snap_point) - - snaps.append(snap_point) - - %Rows.add_child(snap_container) - - var bg := Background.new() - bg.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN - bg.custom_minimum_size.x = 100 - bg.custom_minimum_size.y = 30 - bg.color = color - bg.shift_top = Constants.CONTROL_MARGIN - %Rows.add_child(bg) + arg_name_to_param_input_dict = StatementBlock.format_string(self, %RowHBox, definition.display_template, definition.defaults) diff --git a/addons/block_code/ui/blocks/control_block/control_block.tscn b/addons/block_code/ui/blocks/control_block/control_block.tscn index e45c85d6..947b8d25 100644 --- a/addons/block_code/ui/blocks/control_block/control_block.tscn +++ b/addons/block_code/ui/blocks/control_block/control_block.tscn @@ -1,17 +1,18 @@ -[gd_scene load_steps=3 format=3 uid="uid://bk2argmujy0kk"] +[gd_scene load_steps=6 format=3 uid="uid://bk2argmujy0kk"] [ext_resource type="Script" path="res://addons/block_code/ui/blocks/control_block/control_block.gd" id="1_2hbir"] +[ext_resource type="Script" path="res://addons/block_code/ui/blocks/utilities/background/gutter.gd" id="2_6o8pf"] +[ext_resource type="PackedScene" uid="uid://c7puyxpqcq6xo" path="res://addons/block_code/ui/blocks/utilities/drag_drop_area/drag_drop_area.tscn" id="2_lpu3c"] +[ext_resource type="Script" path="res://addons/block_code/ui/blocks/utilities/background/background.gd" id="2_tx0qr"] [ext_resource type="PackedScene" uid="uid://b1oge52xhjqnu" path="res://addons/block_code/ui/blocks/utilities/snap_point/snap_point.tscn" id="3_nhryi"] -[node name="ControlBlock" type="MarginContainer"] +[node name="ControlBlock" type="MarginContainer" node_paths=PackedStringArray("bottom_snap", "child_snap")] size_flags_horizontal = 0 focus_mode = 2 mouse_filter = 2 script = ExtResource("1_2hbir") -block_name = &"control_block" -label = "Control Block" -color = Color(0.59979, 0.536348, 0.876215, 1) -bottom_snap_path = NodePath("VBoxContainer/SnapPoint") +bottom_snap = NodePath("VBoxContainer/SnapPoint") +child_snap = NodePath("VBoxContainer/MarginContainer/Rows/SnapContainer/SnapPoint") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 @@ -24,9 +25,7 @@ mouse_filter = 2 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/MarginContainer"] layout_mode = 2 -mouse_filter = 2 -theme_override_constants/margin_top = 30 -theme_override_constants/margin_bottom = 30 +theme_override_constants/margin_top = 12 [node name="Rows" type="VBoxContainer" parent="VBoxContainer/MarginContainer"] unique_name_in_owner = true @@ -35,5 +34,65 @@ size_flags_horizontal = 0 mouse_filter = 2 theme_override_constants/separation = 0 +[node name="Row" type="MarginContainer" parent="VBoxContainer/MarginContainer/Rows"] +custom_minimum_size = Vector2(80, 30) +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="TopBackground" type="Control" parent="VBoxContainer/MarginContainer/Rows/Row"] +unique_name_in_owner = true +layout_mode = 2 +script = ExtResource("2_tx0qr") +color = Color(1, 1, 1, 1) +shift_bottom = 20.0 + +[node name="DragDropArea" parent="VBoxContainer/MarginContainer/Rows/Row" instance=ExtResource("2_lpu3c")] +layout_mode = 2 + +[node name="RowHBoxContainer" type="MarginContainer" parent="VBoxContainer/MarginContainer/Rows/Row"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 6 + +[node name="RowHBox" type="HBoxContainer" parent="VBoxContainer/MarginContainer/Rows/Row/RowHBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 0 + +[node name="SnapContainer" type="MarginContainer" parent="VBoxContainer/MarginContainer/Rows"] +unique_name_in_owner = true +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 2 + +[node name="SnapGutter" type="Control" parent="VBoxContainer/MarginContainer/Rows/SnapContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 +size_flags_horizontal = 0 +script = ExtResource("2_6o8pf") +color = Color(1, 1, 1, 1) + +[node name="SnapPoint" parent="VBoxContainer/MarginContainer/Rows/SnapContainer" instance=ExtResource("3_nhryi")] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/margin_left = 20 + +[node name="BottomBackground" type="Control" parent="VBoxContainer/MarginContainer/Rows"] +unique_name_in_owner = true +custom_minimum_size = Vector2(80, 30) +layout_mode = 2 +size_flags_horizontal = 0 +script = ExtResource("2_tx0qr") +color = Color(1, 1, 1, 1) +shift_top = 20.0 + [node name="SnapPoint" parent="VBoxContainer" instance=ExtResource("3_nhryi")] layout_mode = 2 + +[connection signal="mouse_down" from="VBoxContainer/MarginContainer/Rows/Row/DragDropArea" to="." method="_on_drag_drop_area_mouse_down"] diff --git a/addons/block_code/ui/blocks/entry_block/entry_block.gd b/addons/block_code/ui/blocks/entry_block/entry_block.gd index 0659d4bc..22a8b58d 100644 --- a/addons/block_code/ui/blocks/entry_block/entry_block.gd +++ b/addons/block_code/ui/blocks/entry_block/entry_block.gd @@ -7,8 +7,8 @@ extends StatementBlock func _ready(): - block_type = Types.BlockType.ENTRY super() + bottom_snap = null static func get_block_class(): @@ -17,19 +17,3 @@ static func get_block_class(): static func get_scene_path(): return "res://addons/block_code/ui/blocks/entry_block/entry_block.tscn" - - -func get_entry_statement() -> String: - var formatted_statement := statement - - for pair in param_name_input_pairs: - formatted_statement = formatted_statement.replace("{%s}" % pair[0], pair[1].get_string()) - - return formatted_statement - - -func get_serialized_props() -> Array: - var props := super() - if not BlocksCatalog.has_block(block_name): - props.append_array(serialize_props(["signal_name"])) - return props diff --git a/addons/block_code/ui/blocks/entry_block/entry_block.tscn b/addons/block_code/ui/blocks/entry_block/entry_block.tscn index 50c2f0b6..9318bf94 100644 --- a/addons/block_code/ui/blocks/entry_block/entry_block.tscn +++ b/addons/block_code/ui/blocks/entry_block/entry_block.tscn @@ -1,15 +1,59 @@ -[gd_scene load_steps=3 format=3 uid="uid://d2fibflv3ojys"] +[gd_scene load_steps=5 format=3 uid="uid://d2fibflv3ojys"] -[ext_resource type="PackedScene" uid="uid://c84vmg3odrtxt" path="res://addons/block_code/ui/blocks/statement_block/statement_block.tscn" id="1_byjbb"] [ext_resource type="Script" path="res://addons/block_code/ui/blocks/entry_block/entry_block.gd" id="2_3ik8h"] +[ext_resource type="Script" path="res://addons/block_code/ui/blocks/utilities/background/background.gd" id="2_yrw8l"] +[ext_resource type="PackedScene" uid="uid://c7puyxpqcq6xo" path="res://addons/block_code/ui/blocks/utilities/drag_drop_area/drag_drop_area.tscn" id="3_v0qw8"] +[ext_resource type="PackedScene" uid="uid://b1oge52xhjqnu" path="res://addons/block_code/ui/blocks/utilities/snap_point/snap_point.tscn" id="4_yj206"] -[node name="EntryBlock" instance=ExtResource("1_byjbb")] +[node name="EntryBlock" type="MarginContainer" node_paths=PackedStringArray("child_snap")] +size_flags_horizontal = 0 focus_mode = 2 +mouse_filter = 2 script = ExtResource("2_3ik8h") -signal_name = "" -block_name = &"entry_block" -label = "EntryBlock" -block_type = 1 +child_snap = NodePath("VBoxContainer/SnapPoint") -[node name="Background" parent="VBoxContainer/TopMarginContainer" index="0"] +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 0 + +[node name="TopMarginContainer" type="MarginContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 30) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 2 +theme_override_constants/margin_left = 0 +theme_override_constants/margin_top = 0 +theme_override_constants/margin_right = 0 +theme_override_constants/margin_bottom = 0 + +[node name="Background" type="Control" parent="VBoxContainer/TopMarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 1 +script = ExtResource("2_yrw8l") +color = Color(1, 1, 1, 1) show_top = false + +[node name="DragDropArea" parent="VBoxContainer/TopMarginContainer" instance=ExtResource("3_v0qw8")] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TopMarginContainer"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 6 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 6 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/TopMarginContainer/MarginContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 0 + +[node name="SnapPoint" parent="VBoxContainer" instance=ExtResource("4_yj206")] +layout_mode = 2 + +[connection signal="mouse_down" from="VBoxContainer/TopMarginContainer/DragDropArea" to="." method="_on_drag_drop_area_mouse_down"] diff --git a/addons/block_code/ui/blocks/parameter_block/parameter_block.gd b/addons/block_code/ui/blocks/parameter_block/parameter_block.gd index cd6bb599..2ed56aca 100644 --- a/addons/block_code/ui/blocks/parameter_block/parameter_block.gd +++ b/addons/block_code/ui/blocks/parameter_block/parameter_block.gd @@ -5,16 +5,11 @@ extends Block const Constants = preload("res://addons/block_code/ui/constants.gd") const Util = preload("res://addons/block_code/ui/util.gd") -@export var block_format: String = "" -@export var statement: String = "" -@export var variant_type: Variant.Type -@export var defaults: Dictionary = {} - @onready var _panel := $Panel @onready var _hbox := %HBoxContainer -var param_name_input_pairs: Array -var param_input_strings: Dictionary # Only loaded from serialized +var arg_name_to_param_input_dict: Dictionary +var args_to_add_after_format: Dictionary # Only used when loading var spawned_by: ParameterOutput var _panel_normal: StyleBox @@ -32,47 +27,24 @@ func _ready(): _panel_focus.bg_color = color _panel_focus.border_color = Constants.FOCUS_BORDER_COLOR - block_type = Types.BlockType.VALUE if not Util.node_is_part_of_edited_scene(self): _panel.add_theme_stylebox_override("panel", _panel_normal) format() - if param_input_strings: - for pair in param_name_input_pairs: - pair[1].set_raw_input(param_input_strings[pair[0]]) + for arg_name in arg_name_to_param_input_dict: + if arg_name in args_to_add_after_format: + var argument = args_to_add_after_format[arg_name] + if argument is Block: + arg_name_to_param_input_dict[arg_name].snap_point.add_child(argument) + else: + arg_name_to_param_input_dict[arg_name].set_raw_input(argument) func _on_drag_drop_area_mouse_down(): _drag_started() -func get_serialized_props() -> Array: - var props := super() - if not BlocksCatalog.has_block(block_name): - props.append_array(serialize_props(["block_format", "statement", "defaults", "variant_type"])) - - var _param_input_strings: Dictionary = {} - for pair in param_name_input_pairs: - _param_input_strings[pair[0]] = pair[1].get_raw_input() - - props.append(["param_input_strings", _param_input_strings]) - - return props - - -# Override this method to create custom parameter functionality -func get_parameter_string() -> String: - var formatted_statement := statement - - for pair in param_name_input_pairs: - formatted_statement = formatted_statement.replace("{%s}" % pair[0], pair[1].get_string()) - - formatted_statement = InstructionTree.IDHandler.make_unique(formatted_statement) - - return formatted_statement - - static func get_block_class(): return "ParameterBlock" @@ -82,7 +54,7 @@ static func get_scene_path(): func format(): - param_name_input_pairs = StatementBlock.format_string(self, %HBoxContainer, block_format, defaults) + arg_name_to_param_input_dict = StatementBlock.format_string(self, %HBoxContainer, definition.display_template, definition.defaults) func _on_focus_entered(): diff --git a/addons/block_code/ui/blocks/parameter_block/parameter_block.tscn b/addons/block_code/ui/blocks/parameter_block/parameter_block.tscn index 2e901cd9..ac46b80a 100644 --- a/addons/block_code/ui/blocks/parameter_block/parameter_block.tscn +++ b/addons/block_code/ui/blocks/parameter_block/parameter_block.tscn @@ -21,9 +21,6 @@ size_flags_horizontal = 0 focus_mode = 2 mouse_filter = 2 script = ExtResource("1_0hajy") -block_name = &"parameter_block" -label = "Param" -block_type = 3 [node name="Panel" type="Panel" parent="."] unique_name_in_owner = true diff --git a/addons/block_code/ui/blocks/statement_block/statement_block.gd b/addons/block_code/ui/blocks/statement_block/statement_block.gd index a39ce705..5340477d 100644 --- a/addons/block_code/ui/blocks/statement_block/statement_block.gd +++ b/addons/block_code/ui/blocks/statement_block/statement_block.gd @@ -2,51 +2,42 @@ class_name StatementBlock extends Block +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") const ParameterInput = preload("res://addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd") const ParameterInputScene = preload("res://addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.tscn") - -@export var block_format: String = "" -@export var statement: String = "" -@export var defaults: Dictionary = {} +const ParameterOutput = preload("res://addons/block_code/ui/blocks/utilities/parameter_output/parameter_output.gd") +const ParameterOutputScene = preload("res://addons/block_code/ui/blocks/utilities/parameter_output/parameter_output.tscn") +const Types = preload("res://addons/block_code/types/types.gd") @onready var _background := %Background @onready var _hbox := %HBoxContainer -var param_name_input_pairs: Array -var param_input_strings: Dictionary # Only loaded from serialized +var arg_name_to_param_input_dict: Dictionary +var args_to_add_after_format: Dictionary # Only used when loading func _ready(): super() - if block_type != Types.BlockType.STATEMENT: + if definition.type != Types.BlockType.STATEMENT: _background.show_top = false _background.color = color format() - if param_input_strings: - for pair in param_name_input_pairs: - pair[1].set_raw_input(param_input_strings[pair[0]]) + for arg_name in arg_name_to_param_input_dict: + if arg_name in args_to_add_after_format: + var argument = args_to_add_after_format[arg_name] + if argument is Block: + arg_name_to_param_input_dict[arg_name].snap_point.add_child(argument) + else: + arg_name_to_param_input_dict[arg_name].set_raw_input(argument) func _on_drag_drop_area_mouse_down(): _drag_started() -func get_serialized_props() -> Array: - var props := super() - if not BlocksCatalog.has_block(block_name): - props.append_array(serialize_props(["block_format", "statement", "defaults"])) - - var _param_input_strings: Dictionary = {} - for pair in param_name_input_pairs: - _param_input_strings[pair[0]] = pair[1].get_raw_input() - - props.append(["param_input_strings", _param_input_strings]) - return props - - static func get_block_class(): return "StatementBlock" @@ -55,38 +46,13 @@ static func get_scene_path(): return "res://addons/block_code/ui/blocks/statement_block/statement_block.tscn" -# Override this method to create custom block functionality -func get_instruction_node() -> InstructionTree.TreeNode: - var formatted_statement := statement - - for pair in param_name_input_pairs: - formatted_statement = formatted_statement.replace("{%s}" % pair[0], pair[1].get_string()) - - formatted_statement = InstructionTree.IDHandler.make_unique(formatted_statement) - - var statement_lines := formatted_statement.split("\n") - - var root: InstructionTree.TreeNode = InstructionTree.TreeNode.new(statement_lines[0]) - var node := root - - for i in range(1, statement_lines.size()): - node.next = InstructionTree.TreeNode.new(statement_lines[i]) - node = node.next - - if bottom_snap: - var snapped_block: Block = bottom_snap.get_snapped_block() - if snapped_block: - node.next = snapped_block.get_instruction_node() - - return root - - func format(): - param_name_input_pairs = format_string(self, %HBoxContainer, block_format, defaults) + arg_name_to_param_input_dict = format_string(self, %HBoxContainer, definition.display_template, definition.defaults) -static func format_string(parent_block: Block, attach_to: Node, string: String, _defaults: Dictionary) -> Array: - var _param_name_input_pairs = [] +static func format_string(parent_block: Block, attach_to: Node, string: String, _defaults: Dictionary) -> Dictionary: + BlocksCatalog.setup() + var _arg_name_to_param_input_dict = {} var regex = RegEx.new() regex.compile("\\[([^\\]]+)\\]|\\{([^}]+)\\}") # Capture things of format {test} or [test] var results := regex.search_all(string) @@ -122,15 +88,15 @@ static func format_string(parent_block: Block, attach_to: Node, string: String, var param_node: Node if copy_block: - var parameter_output: ParameterOutput = preload("res://addons/block_code/ui/blocks/utilities/parameter_output/parameter_output.tscn").instantiate() + var parameter_output: ParameterOutput = ParameterOutputScene.instantiate() parameter_output.name = "ParameterOutput%d" % start # Unique path - parameter_output.block_params = { - "block_format": param_name, - "statement": param_name, - "variant_type": param_type, - "color": parent_block.color, - "scope": parent_block.get_entry_statement() if parent_block is EntryBlock else "" - } + + var block_name = &"%s_%s" % [parent_block.definition.name, param_name] + var block_definition = BlocksCatalog.get_block(block_name) + if block_definition == null: + push_error("Could not locate block definition %s" % block_name) + + parameter_output.block_params = {"definition": block_definition, "color": parent_block.color} parameter_output.block = parent_block attach_to.add_child(parameter_output) else: @@ -144,10 +110,11 @@ static func format_string(parent_block: Block, attach_to: Node, string: String, parameter_input.modified.connect(func(): parent_block.modified.emit()) attach_to.add_child(parameter_input) - if param_default: + + if param_default != null: parameter_input.set_raw_input(param_default) - _param_name_input_pairs.append([param_name, parameter_input]) + _arg_name_to_param_input_dict[param_name] = parameter_input start = result.get_end() @@ -158,4 +125,4 @@ static func format_string(parent_block: Block, attach_to: Node, string: String, label.text = label_text attach_to.add_child(label) - return _param_name_input_pairs + return _arg_name_to_param_input_dict diff --git a/addons/block_code/ui/blocks/statement_block/statement_block.tscn b/addons/block_code/ui/blocks/statement_block/statement_block.tscn index f5507555..90bbdd9c 100644 --- a/addons/block_code/ui/blocks/statement_block/statement_block.tscn +++ b/addons/block_code/ui/blocks/statement_block/statement_block.tscn @@ -5,14 +5,12 @@ [ext_resource type="PackedScene" uid="uid://c7puyxpqcq6xo" path="res://addons/block_code/ui/blocks/utilities/drag_drop_area/drag_drop_area.tscn" id="2_owgdx"] [ext_resource type="PackedScene" uid="uid://b1oge52xhjqnu" path="res://addons/block_code/ui/blocks/utilities/snap_point/snap_point.tscn" id="3_5vaov"] -[node name="StatementBlock" type="MarginContainer"] +[node name="StatementBlock" type="MarginContainer" node_paths=PackedStringArray("bottom_snap")] size_flags_horizontal = 0 focus_mode = 2 mouse_filter = 2 script = ExtResource("1_6wvlf") -block_name = &"statement_block" -label = "StatementBlock" -bottom_snap_path = NodePath("VBoxContainer/SnapPoint") +bottom_snap = NodePath("VBoxContainer/SnapPoint") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 diff --git a/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd b/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd index 96abece8..fdf2cd66 100644 --- a/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd +++ b/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd @@ -1,6 +1,7 @@ @tool extends MarginContainer +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") const Types = preload("res://addons/block_code/types/types.gd") signal modified @@ -85,6 +86,8 @@ func get_raw_input(): return null if _line_edit.text == "" else int(_line_edit.text) TYPE_FLOAT: return null if _line_edit.text == "" else float(_line_edit.text) + TYPE_STRING_NAME: + return StringName(_line_edit.text) TYPE_NIL: return _line_edit.text _: @@ -116,37 +119,6 @@ func get_snapped_block() -> Block: return snap_point.get_snapped_block() -func get_string() -> String: - var snapped_block: ParameterBlock = get_snapped_block() as ParameterBlock - if snapped_block: - var generated_string = snapped_block.get_parameter_string() - if Types.can_cast(snapped_block.variant_type, variant_type): - return Types.cast(generated_string, snapped_block.variant_type, variant_type) - else: - push_warning("No cast from %s to %s; using '%s' verbatim" % [snapped_block, variant_type, generated_string]) - return generated_string - - var input = get_raw_input() - - if option: - return _option_input.get_item_text(_option_input.selected).to_snake_case() - - match variant_type: - TYPE_STRING: - return "'%s'" % input.replace("\\", "\\\\").replace("'", "\\'") - TYPE_VECTOR2: - return "Vector2%s" % str(input) - TYPE_COLOR: - return "Color%s" % str(input) - TYPE_OBJECT: - if input is OptionData: - var option_data := input as OptionData - return option_data.items[option_data.selected] - _: - return "%s" % input - return "" - - func _validate_and_submit_edit_text(line_edit: Node, type: Variant.Type): if _last_submitted_text[line_edit] == line_edit.text: return @@ -160,6 +132,7 @@ func _validate_and_submit_edit_text(line_edit: Node, type: Variant.Type): line_edit.text = _last_submitted_text[line_edit] return _last_submitted_text[line_edit] = line_edit.text + modified.emit() diff --git a/addons/block_code/ui/main_panel.gd b/addons/block_code/ui/main_panel.gd index 810f378e..64ea1f90 100644 --- a/addons/block_code/ui/main_panel.gd +++ b/addons/block_code/ui/main_panel.gd @@ -8,6 +8,7 @@ const BlockCodePlugin = preload("res://addons/block_code/block_code_plugin.gd") const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.gd") const Picker = preload("res://addons/block_code/ui/picker/picker.gd") const TitleBar = preload("res://addons/block_code/ui/title_bar/title_bar.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") @onready var _picker: Picker = %Picker @onready var _block_canvas: BlockCanvas = %BlockCanvas @@ -61,8 +62,7 @@ func _on_undo_redo_version_changed(): func _on_show_script_button_pressed(): - var block_script: BlockScriptSerialization = _current_block_code_node.block_script - var script: String = _block_canvas.generate_script_from_current_window(block_script) + var script: String = _block_canvas.generate_script_from_current_window() script_window_requested.emit(script) @@ -148,8 +148,12 @@ func save_script(): block_script = block_script.duplicate(true) undo_redo.add_do_property(_current_block_code_node, "block_script", block_script) - _block_canvas.rebuild_block_trees(undo_redo) - var generated_script = _block_canvas.generate_script_from_current_window(block_script) + undo_redo.add_undo_property(block_script, "block_serialization_trees", block_script.block_serialization_trees) + _block_canvas.rebuild_ast_list() + _block_canvas.rebuild_block_serialization_trees() + undo_redo.add_do_property(block_script, "block_serialization_trees", block_script.block_serialization_trees) + + var generated_script = _block_canvas.generate_script_from_current_window() if generated_script != block_script.generated_script: undo_redo.add_undo_property(block_script, "generated_script", block_script.generated_script) undo_redo.add_do_property(block_script, "generated_script", generated_script) @@ -178,10 +182,8 @@ func _input(event): func _print_generated_script(): - if _current_block_code_node == null: - return - var block_script: BlockScriptSerialization = _current_block_code_node.block_script - var script: String = _block_canvas.generate_script_from_current_window(block_script) + var script: String = _block_canvas.generate_script_from_current_window() + print(script) print("Debug script! (not saved)") @@ -261,7 +263,7 @@ func _set_selection(nodes: Array[Node]): EditorInterface.get_selection().add_node(node) -func _create_variable(variable: VariableResource): +func _create_variable(variable: VariableDefinition): if _current_block_code_node == null: print("No script loaded to add variable to.") return diff --git a/addons/block_code/ui/picker/categories/block_category.gd b/addons/block_code/ui/picker/categories/block_category.gd index aee65cb7..e076865d 100644 --- a/addons/block_code/ui/picker/categories/block_category.gd +++ b/addons/block_code/ui/picker/categories/block_category.gd @@ -1,12 +1,14 @@ extends RefCounted +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") + var name: String -var block_list: Array[Block] +var block_list: Array[BlockDefinition] var color: Color var order: int -func _init(p_name: String = "", p_color: Color = Color.WHITE, p_order: int = 0, p_block_list: Array[Block] = []): +func _init(p_name: String = "", p_color: Color = Color.WHITE, p_order: int = 0, p_block_list: Array[BlockDefinition] = []): name = p_name block_list = p_block_list color = p_color diff --git a/addons/block_code/ui/picker/categories/block_category_display.gd b/addons/block_code/ui/picker/categories/block_category_display.gd index b4c536e3..5145836a 100644 --- a/addons/block_code/ui/picker/categories/block_category_display.gd +++ b/addons/block_code/ui/picker/categories/block_category_display.gd @@ -1,7 +1,10 @@ @tool extends MarginContainer +signal block_picked(block: Block) + const BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd") +const Util = preload("res://addons/block_code/ui/util.gd") var category: BlockCategory @@ -12,5 +15,11 @@ var category: BlockCategory func _ready(): _label.text = category.name - for block: Block in category.block_list: + for block_definition in category.block_list: + var block: Block = Util.instantiate_block(block_definition) + + block.color = category.color + block.can_delete = false + block.drag_started.connect(func(block: Block): block_picked.emit(block)) + _blocks.add_child(block) diff --git a/addons/block_code/ui/picker/categories/category_factory.gd b/addons/block_code/ui/picker/categories/category_factory.gd index 934a333b..02eda12b 100644 --- a/addons/block_code/ui/picker/categories/category_factory.gd +++ b/addons/block_code/ui/picker/categories/category_factory.gd @@ -2,6 +2,8 @@ class_name CategoryFactory extends Object const BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd") +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") const Types = preload("res://addons/block_code/types/types.gd") const Util = preload("res://addons/block_code/ui/util.gd") const Constants = preload("res://addons/block_code/ui/constants.gd") @@ -14,7 +16,7 @@ static func _category_cmp(a: BlockCategory, b: BlockCategory) -> bool: return a.name.naturalcasecmp_to(b.name) < 0 -static func get_categories(blocks: Array[Block], extra_categories: Array[BlockCategory] = []) -> Array[BlockCategory]: +static func get_categories(blocks: Array[BlockDefinition], extra_categories: Array[BlockCategory] = []) -> Array[BlockCategory]: var cat_map: Dictionary = {} var extra_cat_map: Dictionary = {} @@ -44,67 +46,61 @@ static func get_categories(blocks: Array[Block], extra_categories: Array[BlockCa return cats -static func get_general_blocks() -> Array[Block]: - var block: Block - var block_list: Array[Block] = [] +static func get_general_blocks() -> Array[BlockDefinition]: + var block: BlockDefinition + var block_list: Array[BlockDefinition] = [] + + BlocksCatalog.setup() # Lifecycle for block_name in [&"ready", &"process", &"physics_process", &"queue_free"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Loops for block_name in [&"for", &"while", &"break", &"continue", &"await_scene_ready"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Logs - block = Util.instantiate_block_by_name(&"print") + block = BlocksCatalog.get_block(&"print") block_list.append(block) # Communication for block_name in [&"define_method", &"call_method_group", &"call_method_node"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) for block_name in [&"add_to_group", &"add_node_to_group", &"remove_from_group", &"remove_node_from_group", &"is_in_group", &"is_node_in_group"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Variables - block = Util.instantiate_block_by_name(&"vector2") + block = BlocksCatalog.get_block(&"vector2") block_list.append(block) # Math for block_name in [&"add", &"subtract", &"multiply", &"divide", &"pow", &"randf_range", &"randi_range", &"sin", &"cos", &"tan"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Logic for block_name in [&"if", &"else_if", &"else", &"compare", &"and", &"or", &"not"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Input - block = Util.instantiate_block_by_name(&"is_input_actioned") + block = BlocksCatalog.get_block(&"is_input_actioned") block_list.append(block) # Sounds for block_name in [&"load_sound", &"play_sound", &"pause_continue_sound", &"stop_sound"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) # Graphics for block_name in [&"viewport_width", &"viewport_height", &"viewport_center"]: - block = Util.instantiate_block_by_name(block_name) + block = BlocksCatalog.get_block(block_name) block_list.append(block) return block_list - - -static func get_inherited_blocks(_class_name: String) -> Array[Block]: - return Util.instantiate_blocks_for_class(_class_name) - - -static func get_variable_blocks(variables: Array[VariableResource]): - return Util.instantiate_variable_blocks(variables) diff --git a/addons/block_code/ui/picker/categories/variable_category/variable_category_display.gd b/addons/block_code/ui/picker/categories/variable_category/variable_category_display.gd index 6f27bf3b..6e049dc1 100644 --- a/addons/block_code/ui/picker/categories/variable_category/variable_category_display.gd +++ b/addons/block_code/ui/picker/categories/variable_category/variable_category_display.gd @@ -2,8 +2,9 @@ extends "res://addons/block_code/ui/picker/categories/block_category_display.gd" const Types = preload("res://addons/block_code/types/types.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") -signal variable_created(variable: VariableResource) +signal variable_created(variable: VariableDefinition) @onready var variable_blocks := %VariableBlocks @@ -13,4 +14,4 @@ func _ready(): func _on_create_variable(var_name, var_type): - variable_created.emit(VariableResource.new(var_name, Types.STRING_TO_VARIANT_TYPE[var_type])) + variable_created.emit(VariableDefinition.new(var_name, Types.STRING_TO_VARIANT_TYPE[var_type])) diff --git a/addons/block_code/ui/picker/picker.gd b/addons/block_code/ui/picker/picker.gd index 7d7b9648..d2fbd4c2 100644 --- a/addons/block_code/ui/picker/picker.gd +++ b/addons/block_code/ui/picker/picker.gd @@ -1,15 +1,18 @@ @tool extends MarginContainer +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") const BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd") const BlockCategoryButtonScene = preload("res://addons/block_code/ui/picker/categories/block_category_button.tscn") const BlockCategoryButton = preload("res://addons/block_code/ui/picker/categories/block_category_button.gd") const BlockCategoryDisplay = preload("res://addons/block_code/ui/picker/categories/block_category_display.gd") const CategoryFactory = preload("res://addons/block_code/ui/picker/categories/category_factory.gd") +const Util = preload("res://addons/block_code/ui/util.gd") const VariableCategoryDisplay = preload("res://addons/block_code/ui/picker/categories/variable_category/variable_category_display.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") signal block_picked(block: Block) -signal variable_created(variable: VariableResource) +signal variable_created(variable: VariableDefinition) @onready var _block_list := %BlockList @onready var _block_scroll := %BlockScroll @@ -25,19 +28,8 @@ func block_script_selected(block_script: BlockScriptSerialization): reset_picker() return - var categories_to_add: Array[BlockCategory] = [] - - for class_dict in ProjectSettings.get_global_class_list(): - if class_dict.class == block_script.script_inherits: - var script = load(class_dict.path) - if script.has_method("get_custom_categories"): - categories_to_add = script.get_custom_categories() - if script.has_method("setup_custom_blocks"): - script.setup_custom_blocks() - break - - var blocks_to_add: Array[Block] = [] - blocks_to_add.append_array(CategoryFactory.get_inherited_blocks(block_script.script_inherits)) + var blocks_to_add: Array[BlockDefinition] = block_script.get_definitions() + var categories_to_add: Array[BlockCategory] = block_script.get_categories() init_picker(blocks_to_add, categories_to_add) reload_variables(block_script.variables) @@ -51,7 +43,7 @@ func reset_picker(): c.queue_free() -func init_picker(extra_blocks: Array[Block] = [], extra_categories: Array[BlockCategory] = []): +func init_picker(extra_blocks: Array[BlockDefinition] = [], extra_categories: Array[BlockCategory] = []): reset_picker() var blocks := CategoryFactory.get_general_blocks() + extra_blocks @@ -75,23 +67,13 @@ func init_picker(extra_blocks: Array[Block] = [], extra_categories: Array[BlockC _variable_category_display = block_category_display block_category_display.category = category + block_category_display.block_picked.connect(func(block: Block): block_picked.emit(block)) _block_list.add_child(block_category_display) - for _block in category.block_list: - var block: Block = _block as Block - block.drag_started.connect(_block_picked) - - # Don't allow the block to be deleted while in the picker. - block.can_delete = false - _block_scroll.scroll_vertical = 0 -func _block_picked(block: Block): - block_picked.emit(block) - - func scroll_to(y: float): if scroll_tween: scroll_tween.kill() @@ -110,15 +92,15 @@ func set_collapsed(collapsed: bool): _widget_container.visible = not collapsed -func reload_variables(variables: Array[VariableResource]): +func reload_variables(variables: Array[VariableDefinition]): if _variable_category_display: for c in _variable_category_display.variable_blocks.get_children(): c.queue_free() var i := 1 - for block in CategoryFactory.get_variable_blocks(variables): + for block in Util.instantiate_variable_blocks(variables): _variable_category_display.variable_blocks.add_child(block) - block.drag_started.connect(_block_picked) + block.drag_started.connect(func(block: Block): block_picked.emit(block)) if i % 2 == 0: var spacer := Control.new() spacer.custom_minimum_size.y = 12 diff --git a/addons/block_code/ui/util.gd b/addons/block_code/ui/util.gd index 5371b4e8..076d1f84 100644 --- a/addons/block_code/ui/util.gd +++ b/addons/block_code/ui/util.gd @@ -4,6 +4,7 @@ const BlockDefinition = preload("res://addons/block_code/code_generation/block_d const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") const Types = preload("res://addons/block_code/types/types.gd") const Constants = preload("res://addons/block_code/ui/constants.gd") +const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd") const SCENE_PER_TYPE = { Types.BlockType.ENTRY: preload("res://addons/block_code/ui/blocks/entry_block/entry_block.tscn"), @@ -19,109 +20,26 @@ static func get_category_color(category: String) -> Color: static func instantiate_block(block_definition: BlockDefinition) -> Block: - var scene = SCENE_PER_TYPE[block_definition.type] - var b = scene.instantiate() - b.block_name = block_definition.name - if block_definition.type == Types.BlockType.CONTROL: - b.block_formats = [block_definition.display_template] - b.statements = [block_definition.code_template] - else: - b.block_format = block_definition.display_template - b.statement = block_definition.code_template - if block_definition.type == Types.BlockType.VALUE: - b.variant_type = block_definition.variant_type - elif block_definition.type == Types.BlockType.ENTRY: - if block_definition.signal_name != "": - b.signal_name = block_definition.signal_name - b.defaults = block_definition.defaults - b.tooltip_text = block_definition.description - b.category = block_definition.category - b.color = get_category_color(block_definition.category) - - return b - - -static func instantiate_block_by_name(block_name: StringName) -> Block: - BlocksCatalog.setup() - var block_definition: BlockDefinition = BlocksCatalog.get_block(block_name) if block_definition == null: - push_error("The block %s is not in the catalog yet!" % block_name) - return - return instantiate_block(block_definition) - - -static func _get_builtin_parents(_class_name: String) -> Array[String]: - var parents: Array[String] = [] - var current = _class_name - - while current != "": - parents.append(current) - current = ClassDB.get_parent_class(current) - - return parents - - -static func _get_custom_parent_class_name(_custom_class_name: String) -> String: - for class_dict in ProjectSettings.get_global_class_list(): - if class_dict.class != _custom_class_name: - continue - var script = load(class_dict.path) - var builtin_class = script.get_instance_base_type() - return builtin_class - return "Node" - - -static func _get_parents(_class_name: String) -> Array[String]: - if ClassDB.class_exists(_class_name): - return _get_builtin_parents(_class_name) - var parents: Array[String] = [_class_name] - var _parent_class_name = _get_custom_parent_class_name(_class_name) - parents.append_array(_get_builtin_parents(_parent_class_name)) - return parents - - -static func instantiate_blocks_for_class(_class_name: String) -> Array[Block]: - BlocksCatalog.setup() - - var blocks: Array[Block] = [] - for subclass in _get_parents(_class_name): - for block_definition in BlocksCatalog.get_blocks_by_class(subclass): - var b = instantiate_block(block_definition) - blocks.append(b) - - return blocks - - -static func get_variable_block_definitions(variables: Array[VariableResource]) -> Array[BlockDefinition]: - var block_definitions: Array[BlockDefinition] = [] - for variable: VariableResource in variables: - var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type] - - var b = BlockDefinition.new() - b.name = "get_var_%s" % variable.var_name - b.type = Types.BlockType.VALUE - b.variant_type = variable.var_type - b.display_template = variable.var_name - b.code_template = variable.var_name - block_definitions.append(b) + push_error("Cannot construct block from null block definition.") + return null - b = BlockDefinition.new() - b.name = "set_var_%s" % variable.var_name - b.type = Types.BlockType.STATEMENT - b.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string] - b.code_template = "%s = {value}" % [variable.var_name] - block_definitions.append(b) + var scene = SCENE_PER_TYPE.get(block_definition.type) + if scene == null: + push_error("Cannot instantiate Block from type %s" % block_definition.type) + return null - return block_definitions + var block = scene.instantiate() + block.definition = block_definition + return block -static func instantiate_variable_blocks(variables: Array[VariableResource]) -> Array[Block]: +static func instantiate_variable_blocks(variables: Array[VariableDefinition]) -> Array[Block]: var blocks: Array[Block] = [] - for block_definition in get_variable_block_definitions(variables): - var b = instantiate_block(block_definition) - # HACK: Color the blocks since they are outside of the normal picker system - b.color = Constants.BUILTIN_CATEGORIES_PROPS["Variables"].color - blocks.append(b) + for block_definition in BlocksCatalog.get_variable_block_definitions(variables): + var block = instantiate_block(block_definition) + block.color = get_category_color(block_definition.category) + blocks.append(block) return blocks diff --git a/tests/test_category_factory.gd b/tests/test_category_factory.gd index 186d97b4..99dbfd11 100644 --- a/tests/test_category_factory.gd +++ b/tests/test_category_factory.gd @@ -1,14 +1,9 @@ extends GutTest ## Tests for CategoryFactory +const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") const BlockCategory = preload("res://addons/block_code/ui/picker/categories/block_category.gd") - - -func free_block_list(blocks: Array[Block]): - var block: Block = blocks.pop_back() - while block != null: - block.free() - block = blocks.pop_back() +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") func get_category_names(categories: Array[BlockCategory]) -> Array[String]: @@ -19,16 +14,14 @@ func get_category_names(categories: Array[BlockCategory]) -> Array[String]: func get_class_category_names(_class_name: String) -> Array[String]: - var blocks: Array[Block] = CategoryFactory.get_inherited_blocks(_class_name) + var blocks: Array[BlockDefinition] = BlocksCatalog.get_inherited_blocks(_class_name) var names: Array[String] = get_category_names(CategoryFactory.get_categories(blocks)) - free_block_list(blocks) return names func test_general_category_names(): - var blocks: Array[Block] = CategoryFactory.get_general_blocks() + var blocks: Array[BlockDefinition] = CategoryFactory.get_general_blocks() var names: Array[String] = get_category_names(CategoryFactory.get_categories(blocks)) - free_block_list(blocks) assert_eq( names, [ @@ -62,9 +55,8 @@ func test_inherited_category_names(params = use_parameters(class_category_names) func test_unique_block_names(): - var blocks: Array[Block] = CategoryFactory.get_general_blocks() + var blocks: Array[BlockDefinition] = CategoryFactory.get_general_blocks() var block_names: Dictionary for block in blocks: - assert_does_not_have(block_names, block.block_name, "Block name %s is duplicated" % block.block_name) - block_names[block.block_name] = block - free_block_list(blocks) + assert_does_not_have(block_names, block.name, "Block name %s is duplicated" % block.name) + block_names[block.name] = block diff --git a/tests/test_code_generation.gd b/tests/test_code_generation.gd new file mode 100644 index 00000000..7e61602e --- /dev/null +++ b/tests/test_code_generation.gd @@ -0,0 +1,240 @@ +extends GutTest +## Tests for Script Generation + +const Types = preload("res://addons/block_code/types/types.gd") +const ScriptGenerator = preload("res://addons/block_code/code_generation/script_generator.gd") +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 BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd") +const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd") + +var general_blocks: Dictionary + + +func build_block_map(block_map: Dictionary, blocks: Array[BlockDefinition]): + assert_eq(block_map, {}) + for block in blocks: + assert_does_not_have(block_map, block.name, "Block name %s is duplicated" % block.name) + block_map[block.name] = block + + +func free_block_map(block_map: Dictionary): + block_map.clear() + assert_eq(block_map, {}) + + +func before_each(): + build_block_map(general_blocks, CategoryFactory.get_general_blocks()) + + +func after_each(): + free_block_map(general_blocks) + + +func block_resource_from_code_template(code_template: String) -> BlockDefinition: + var block_resource = BlockDefinition.new() + block_resource.code_template = code_template + return block_resource + + +func ast_node_from_code_template(code_template: String) -> BlockAST.ASTNode: + var node = BlockAST.ASTNode.new() + node.data = block_resource_from_code_template(code_template) + return node + + +func test_single_node_text(): + var node = ast_node_from_code_template("blah") + var text: String = node.get_code(0) + assert_eq(text, "blah\n") + + +func test_root_depth_text(): + var node = ast_node_from_code_template("blah") + var text: String + for depth in range(5): + text = node.get_code(depth) + assert_eq(text, "\t".repeat(depth) + "blah\n") + + +func test_child_node_text(): + var parent = ast_node_from_code_template("parent") + var child = ast_node_from_code_template("child") + var grandchild = ast_node_from_code_template("grandchild") + parent.children.append(child) + child.children.append(grandchild) + var text: String = parent.get_code(0) + assert_eq(text, "parent\n\tchild\n\t\tgrandchild\n") + + +func test_sibling_node_text(): + var parent = ast_node_from_code_template("parent") + var brother = ast_node_from_code_template("brother") + var sister = ast_node_from_code_template("sister") + parent.children.append(brother) + parent.children.append(sister) + var text: String = parent.get_code(0) + assert_eq(text, "parent\n\tbrother\n\tsister\n") + + +## Test recursive node first, depth first text generation. +func test_tree_node_text(): + var root = ast_node_from_code_template("root") + var parent = ast_node_from_code_template("parent") + var child1 = ast_node_from_code_template("child1") + var child2 = ast_node_from_code_template("child2") + var grandchild = ast_node_from_code_template("grandchild") + var sibling = ast_node_from_code_template("sibling") + var nephew = ast_node_from_code_template("nephew") + + root.children.append(parent) + parent.children.append(child1) + parent.children.append(child2) + child1.children.append(grandchild) + root.children.append(sibling) + sibling.children.append(nephew) + + var text: String = root.get_code(0) + assert_eq(text, "root\n\tparent\n\t\tchild1\n\t\t\tgrandchild\n\t\tchild2\n\tsibling\n\t\tnephew\n") + + +func test_script_no_nodes(): + var bsd := BlockScriptSerialization.new("Foo") + var script := ScriptGenerator.generate_script(ASTList.new(), bsd) + assert_eq( + script, + ( + """\ + extends Foo + + + """ + . dedent() + ) + ) + + +func test_script_no_entry_blocks(): + var bsd := BlockScriptSerialization.new("Foo") + var ast := BlockAST.new() + ast.root = BlockAST.ASTNode.new() + ast.root.data = BlockDefinition.new() + ast.root.data.type = Types.BlockType.STATEMENT + var ast_list = ASTList.new() + ast_list.append(ast, Vector2(0, 0)) + var script := ScriptGenerator.generate_script(ast_list, bsd) + assert_eq( + script, + ( + """\ + extends Foo + + + """ + . dedent() + ) + ) + + +func test_basic_script(): + var ready_block: BlockDefinition = general_blocks[&"ready"] + var print_block: BlockDefinition = general_blocks[&"print"] + + var ast := BlockAST.new() + ast.root = BlockAST.ASTNode.new() + ast.root.data = ready_block + ast.root.children.append(BlockAST.ASTNode.new()) + ast.root.children[0].data = print_block + ast.root.children[0].arguments["text"] = "Hello world!" + var ast_list = ASTList.new() + ast_list.append(ast, Vector2(0, 0)) + + var bsd := BlockScriptSerialization.new("Node2D") + var script := ScriptGenerator.generate_script(ast_list, bsd) + assert_eq( + script, + ( + """\ + extends Node2D + + + func _ready(): + print('Hello world!') + + """ + . dedent() + ) + ) + + +func test_multiple_entry_script(): + var ready_block: BlockDefinition = general_blocks[&"ready"] + var print_block: BlockDefinition = general_blocks[&"print"] + + var ast := BlockAST.new() + ast.root = BlockAST.ASTNode.new() + ast.root.data = ready_block + ast.root.children.append(BlockAST.ASTNode.new()) + ast.root.children[0].data = print_block + ast.root.children[0].arguments["text"] = "Hello world!" + var ast_list = ASTList.new() + ast_list.append(ast, Vector2(0, 0)) + ast_list.append(ast, Vector2(0, 0)) + + var bsd := BlockScriptSerialization.new("Node2D") + var script := ScriptGenerator.generate_script(ast_list, bsd) + assert_eq( + script, + ( + """\ + extends Node2D + + + func _ready(): + print('Hello world!') + print('Hello world!') + + """ + . dedent() + ) + ) + + +func test_signal_script(): + var area2d_blocks: Dictionary + build_block_map(area2d_blocks, BlocksCatalog.get_inherited_blocks("Area2D")) + + var entered_block: BlockDefinition = area2d_blocks[&"area2d_on_entered"] + var print_block: BlockDefinition = general_blocks[&"print"] + + var ast := BlockAST.new() + ast.root = BlockAST.ASTNode.new() + ast.root.data = entered_block + ast.root.children.append(BlockAST.ASTNode.new()) + ast.root.children[0].data = print_block + ast.root.children[0].arguments["text"] = "Body entered!" + var ast_list = ASTList.new() + ast_list.append(ast, Vector2(0, 0)) + + var bsd := BlockScriptSerialization.new("Area2D") + var script := ScriptGenerator.generate_script(ast_list, bsd) + assert_eq( + script, + ( + """\ + extends Area2D + + + func _init(): + body_entered.connect(_on_body_entered) + + func _on_body_entered(body: Node2D): + + print('Body entered!') + + """ + . dedent() + ) + ) + + free_block_map(area2d_blocks) diff --git a/tests/test_instruction_tree.gd b/tests/test_instruction_tree.gd deleted file mode 100644 index 81e17fab..00000000 --- a/tests/test_instruction_tree.gd +++ /dev/null @@ -1,223 +0,0 @@ -extends GutTest -## Tests for InstructionTree - -const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd") -const CategoryFactory = preload("res://addons/block_code/ui/picker/categories/category_factory.gd") -const InstructionTree = preload("res://addons/block_code/instruction_tree/instruction_tree.gd") - -var general_blocks: Dictionary - - -func build_block_map(block_map: Dictionary, blocks: Array[Block]): - assert_eq(block_map, {}) - for block in blocks: - assert_does_not_have(block_map, block.block_name, "Block name %s is duplicated" % block.block_name) - block_map[block.block_name] = block - - -func free_block_map(block_map: Dictionary): - for block in block_map.values(): - block.free() - block_map.clear() - assert_eq(block_map, {}) - - -func dup_node(node: Node) -> Node: - return node.duplicate(DUPLICATE_USE_INSTANTIATION) - - -func before_each(): - build_block_map(general_blocks, CategoryFactory.get_general_blocks()) - - -func after_each(): - free_block_map(general_blocks) - - -func test_single_node_text(): - var node = InstructionTree.TreeNode.new("blah") - var text: String = InstructionTree.generate_text(node, 0) - assert_eq(text, "blah\n") - - -func test_root_depth_text(): - var node = InstructionTree.TreeNode.new("blah") - var text: String - for depth in range(5): - text = InstructionTree.generate_text(node, depth) - assert_eq(text, "\t".repeat(depth) + "blah\n") - - -func test_child_node_text(): - var parent = InstructionTree.TreeNode.new("parent") - var child = InstructionTree.TreeNode.new("child") - var grandchild = InstructionTree.TreeNode.new("grandchild") - parent.add_child(child) - child.add_child(grandchild) - var text: String = InstructionTree.generate_text(parent, 0) - assert_eq(text, "parent\n\tchild\n\t\tgrandchild\n") - - -func test_sibling_node_text(): - var node = InstructionTree.TreeNode.new("node") - var brother = InstructionTree.TreeNode.new("brother") - var sister = InstructionTree.TreeNode.new("sister") - node.next = brother - brother.next = sister - var text: String = InstructionTree.generate_text(node, 0) - assert_eq(text, "node\nbrother\nsister\n") - - -## Test recursive node first, depth first text generation. -func test_tree_node_text(): - var root = InstructionTree.TreeNode.new("root") - var child1 = InstructionTree.TreeNode.new("child1") - var child2 = InstructionTree.TreeNode.new("child2") - var grandchild = InstructionTree.TreeNode.new("grandchild") - var sibling = InstructionTree.TreeNode.new("sibling") - var nephew = InstructionTree.TreeNode.new("nephew") - - root.add_child(child1) - root.add_child(child2) - child1.add_child(grandchild) - root.next = sibling - sibling.add_child(nephew) - - var text: String = InstructionTree.generate_text(root, 0) - assert_eq(text, "root\n\tchild1\n\t\tgrandchild\n\tchild2\nsibling\n\tnephew\n") - - -func test_script_no_nodes(): - var block_script := BlockScriptSerialization.new("Foo") - var text_script := BlockTreeUtil.generate_script_from_nodes([], block_script) - assert_eq( - text_script, - ( - """\ - extends Foo - - - """ - . dedent() - ) - ) - - -func test_script_no_entry_blocks(): - var block_script := BlockScriptSerialization.new("Foo") - var nodes: Array[Node] = [Node.new(), Node2D.new(), Control.new()] - var text_script := BlockTreeUtil.generate_script_from_nodes(nodes, block_script) - assert_eq( - text_script, - ( - """\ - extends Foo - - - """ - . dedent() - ) - ) - for node in nodes: - node.free() - - -func test_basic_script(): - var ready_block: Block = dup_node(general_blocks[&"ready"]) - - var print_block: Block = dup_node(general_blocks["print"]) - # XXX: It seems like this should substitute {text} in the statement, - # but it doesn't. I can't make sense of StatementBlock. - # print_block.param_input_strings = {"text": "this is a test"} - # print_block._ready() - - # XXX: Why does insert_snapped_block add_child but not set snapped_block? - ready_block.bottom_snap.insert_snapped_block(print_block) - ready_block.bottom_snap.snapped_block = print_block - assert_true(ready_block.bottom_snap.has_snapped_block()) - assert_eq(ready_block.bottom_snap.get_snapped_block(), print_block) - - var block_script := BlockScriptSerialization.new("Node2D") - var text_script := BlockTreeUtil.generate_script_from_nodes([ready_block], block_script) - assert_eq( - text_script, - ( - """\ - extends Node2D - - - func _ready(): - print({text}) - - """ - . dedent() - ) - ) - - ready_block.free() - - -func test_multiple_entry_script(): - var ready_block: Block = dup_node(general_blocks[&"ready"]) - var print_block: Block = dup_node(general_blocks[&"print"]) - ready_block.bottom_snap.insert_snapped_block(print_block) - ready_block.bottom_snap.snapped_block = print_block - - var ready_block_2: Block = dup_node(general_blocks[&"ready"]) - var print_block_2: Block = dup_node(general_blocks[&"print"]) - ready_block_2.bottom_snap.insert_snapped_block(print_block_2) - ready_block_2.bottom_snap.snapped_block = print_block_2 - - var block_script := BlockScriptSerialization.new("Node2D") - var text_script := BlockTreeUtil.generate_script_from_nodes([ready_block, ready_block_2], block_script) - assert_eq( - text_script, - ( - """\ - extends Node2D - - - func _ready(): - print({text}) - - print({text}) - - """ - . dedent() - ) - ) - - ready_block.free() - ready_block_2.free() - - -func test_signal_script(): - var area2d_blocks: Dictionary - build_block_map(area2d_blocks, CategoryFactory.get_inherited_blocks("Area2D")) - var entered_block: Block = dup_node(area2d_blocks["area2d_on_entered"]) - var print_block: Block = dup_node(general_blocks["print"]) - entered_block.bottom_snap.insert_snapped_block(print_block) - entered_block.bottom_snap.snapped_block = print_block - - var block_script := BlockScriptSerialization.new("Area2D") - var text_script = BlockTreeUtil.generate_script_from_nodes([entered_block], block_script) - assert_eq( - text_script, - ( - """\ - extends Area2D - - - func _on_body_entered(body: Node2D): - - print({text}) - - func _init(): - body_entered.connect(_on_body_entered) - """ - . dedent() - ) - ) - - entered_block.free() - free_block_map(area2d_blocks)