diff --git a/addons/block_code/drag_manager/drag_manager.gd b/addons/block_code/drag_manager/drag_manager.gd index fdc21cf9..9c6aa9c8 100644 --- a/addons/block_code/drag_manager/drag_manager.gd +++ b/addons/block_code/drag_manager/drag_manager.gd @@ -8,6 +8,8 @@ signal block_modified @export var picker_path: NodePath @export var block_canvas_path: NodePath +const Constants = preload("res://addons/block_code/ui/constants.gd") + var drag_offset: Vector2 var dragging: Block = null @@ -39,38 +41,41 @@ func _process(_delta): var closest_snap_point: SnapPoint = null var closest_dist: float = INF var snap_points: Array[Node] = get_tree().get_nodes_in_group("snap_point") - for n in snap_points: - if n is SnapPoint: - var snap_point: SnapPoint = n as SnapPoint - if snap_point.block == null: - push_error("Warning: a snap point does not reference it's parent block.") - continue - if snap_point.block.on_canvas: - if Types.can_cast(dragging.block_type, snap_point.block_type): - var snap_global_pos: Vector2 = snap_point.get_global_rect().position - var temp_dist: float = dragging_global_pos.distance_to(snap_global_pos) - if temp_dist < closest_dist: - # Check if any parent node is this node - var is_child: bool = false - var parent = snap_point - while parent is SnapPoint: - if parent.block == dragging: - is_child = true - - parent = parent.block.get_parent() - - if not is_child: - closest_dist = temp_dist - closest_snap_point = snap_point - - if closest_dist > 80.0: - closest_snap_point = null + for snap_point in snap_points: + if not snap_point is SnapPoint: + push_error('Warning: node %s in group "snap_point" is not of class SnapPoint.' % snap_point) + continue + if snap_point.block == null: + push_error("Warning: snap point %s does not reference it's parent block." % snap_point) + continue + if not snap_point.block.on_canvas: + # We only snap to blocks on the canvas: + continue + if dragging.block_type != snap_point.block_type: + # We only snap to the same block type: + continue + if dragging.block_type == Types.BlockType.VALUE and not Types.can_cast(dragging.variant_type, snap_point.variant_type): + # We only snap Value blocks to snaps that can cast to same variant: + continue + var snap_global_pos: Vector2 = snap_point.get_global_rect().position + var temp_dist: float = dragging_global_pos.distance_to(snap_global_pos) + if temp_dist <= Constants.MINIMUM_SNAP_DISTANCE and temp_dist < closest_dist: + # Check if any parent node is this node + var is_child: bool = false + var parent = snap_point + while parent is SnapPoint: + if parent.block == dragging: + is_child = true + + parent = parent.block.get_parent() + + if not is_child: + closest_dist = temp_dist + closest_snap_point = snap_point if closest_snap_point != previewing_snap_point: _update_preview(closest_snap_point) - # TODO: make sure dragging.block_type is the same as snap_point type - func _update_preview(snap_point: SnapPoint): previewing_snap_point = snap_point diff --git a/addons/block_code/types/types.gd b/addons/block_code/types/types.gd index 8f8b03e6..75162777 100644 --- a/addons/block_code/types/types.gd +++ b/addons/block_code/types/types.gd @@ -3,23 +3,38 @@ extends Node enum BlockType { NONE, - EXECUTE, ENTRY, - # Parameters - STRING, - INT, - FLOAT, - VECTOR2, - BOOL, - COLOR, - NODE + EXECUTE, + VALUE, +} + +const VARIANT_TYPE_TO_STRING: Dictionary = { + TYPE_STRING: "STRING", + TYPE_INT: "INT", + TYPE_FLOAT: "FLOAT", + TYPE_BOOL: "BOOL", + TYPE_VECTOR2: "VECTOR2", + TYPE_COLOR: "COLOR", + TYPE_NODE_PATH: "NODE_PATH", + TYPE_NIL: "NIL", +} + +const STRING_TO_VARIANT_TYPE: Dictionary = { + "STRING": TYPE_STRING, + "INT": TYPE_INT, + "FLOAT": TYPE_FLOAT, + "BOOL": TYPE_BOOL, + "VECTOR2": TYPE_VECTOR2, + "COLOR": TYPE_COLOR, + "NODE_PATH": TYPE_NODE_PATH, + "NIL": TYPE_NIL, } const cast_relationships = [ - [BlockType.INT, BlockType.FLOAT, "float(%s)"], - [BlockType.FLOAT, BlockType.INT, "int(%s)"], - [BlockType.INT, BlockType.STRING, "str(%s)"], - [BlockType.FLOAT, BlockType.STRING, "str(%s)"], + [TYPE_INT, TYPE_FLOAT, "float(%s)"], + [TYPE_FLOAT, TYPE_INT, "int(%s)"], + [TYPE_INT, TYPE_STRING, "str(%s)"], + [TYPE_FLOAT, TYPE_STRING, "str(%s)"], ] # Directed graph, edges are CastGraphEdge @@ -27,10 +42,10 @@ static var cast_graph: Dictionary class CastGraphEdge: - var to: BlockType + var to: Variant.Type var cast_format: String - func _init(p_to: BlockType, p_cast_format: String): + func _init(p_to: Variant.Type, p_cast_format: String): to = p_to cast_format = p_cast_format @@ -56,7 +71,7 @@ static var dist: Dictionary const INT_MAX: int = 1000000000 -static func dijkstra(source: BlockType): +static func dijkstra(source: Variant.Type): prev = {} dist = {} @@ -86,7 +101,7 @@ static func dijkstra(source: BlockType): queue.update_priority(v, alt) -static func can_cast(type: BlockType, parent_type: BlockType) -> bool: +static func can_cast(type: Variant.Type, parent_type: Variant.Type) -> bool: if type == parent_type: return true @@ -96,7 +111,7 @@ static func can_cast(type: BlockType, parent_type: BlockType) -> bool: return false -static func cast(val: String, type: BlockType, parent_type: BlockType): +static func cast(val: String, type: Variant.Type, parent_type: Variant.Type): if type == parent_type: return val 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 fca02b87..fc8b1d91 100644 --- a/addons/block_code/ui/blocks/entry_block/entry_block.gd +++ b/addons/block_code/ui/blocks/entry_block/entry_block.gd @@ -4,6 +4,7 @@ extends StatementBlock func _ready(): + block_type = Types.BlockType.ENTRY super() 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 5e22e55b..e1351721 100644 --- a/addons/block_code/ui/blocks/entry_block/entry_block.tscn +++ b/addons/block_code/ui/blocks/entry_block/entry_block.tscn @@ -7,4 +7,7 @@ script = ExtResource("2_3ik8h") block_name = "entry_block" label = "EntryBlock" -block_type = 2 +block_type = 1 + +[node name="Background" parent="VBoxContainer/TopMarginContainer" index="0"] +show_top = false 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 fedae5fb..76b2be75 100644 --- a/addons/block_code/ui/blocks/parameter_block/parameter_block.gd +++ b/addons/block_code/ui/blocks/parameter_block/parameter_block.gd @@ -4,6 +4,7 @@ extends Block @export var block_format: String = "" @export var statement: String = "" +@export var variant_type: Variant.Type @onready var _panel := $Panel @onready var _hbox := %HBoxContainer @@ -15,6 +16,7 @@ var param_input_strings: Dictionary # Only loaded from serialized func _ready(): super() + block_type = Types.BlockType.VALUE var new_panel = _panel.get_theme_stylebox("panel").duplicate() new_panel.bg_color = color new_panel.border_color = color.darkened(0.2) 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 1cf7525a..3f7dff8b 100644 --- a/addons/block_code/ui/blocks/statement_block/statement_block.gd +++ b/addons/block_code/ui/blocks/statement_block/statement_block.gd @@ -100,12 +100,12 @@ static func format_string(parent_block: Block, attach_to: Node, string: String) var split := param.split(": ") var param_name := split[0] var param_type_str := split[1] - var param_type := Types.BlockType.get(param_type_str) + var param_type: Variant.Type = Types.STRING_TO_VARIANT_TYPE[param_type_str] var param_input: ParameterInput = preload("res://addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.tscn").instantiate() param_input.name = "ParameterInput%d" % start # Unique path param_input.placeholder = param_name - param_input.block_type = param_type + param_input.variant_type = param_type param_input.block = parent_block param_input.text_modified.connect(func(): parent_block.modified.emit()) attach_to.add_child(param_input) @@ -115,7 +115,7 @@ static func format_string(parent_block: Block, attach_to: Node, string: String) var new_block: Block = preload("res://addons/block_code/ui/blocks/parameter_block/parameter_block.tscn").instantiate() new_block.block_format = param_name new_block.statement = param_name - new_block.block_type = param_type + new_block.variant_type = param_type new_block.color = parent_block.color param_input.block_type = Types.BlockType.NONE param_input.snap_point.block_type = Types.BlockType.NONE # Necessary because already called ready 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 1d5fac1d..8f846c76 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 @@ -9,7 +9,8 @@ signal text_modified @export var block_path: NodePath -@export var block_type: Types.BlockType = Types.BlockType.STRING +@export var variant_type: Variant.Type = TYPE_STRING +@export var block_type: Types.BlockType = Types.BlockType.VALUE var block: Block @@ -41,6 +42,7 @@ func _ready(): block = get_node_or_null(block_path) snap_point.block = block snap_point.block_type = block_type + snap_point.variant_type = variant_type # Do something with block_type to restrict input @@ -50,16 +52,16 @@ func get_snapped_block() -> Block: func get_string() -> String: - var snapped_block: Block = get_snapped_block() + var snapped_block: ParameterBlock = get_snapped_block() as ParameterBlock if snapped_block: var generated_string = snapped_block.get_parameter_string() - return Types.cast(generated_string, snapped_block.block_type, block_type) + return Types.cast(generated_string, snapped_block.variant_type, variant_type) var text: String = get_plain_text() - if block_type == Types.BlockType.STRING: + if variant_type == TYPE_STRING: text = "'%s'" % text.replace("\\", "\\\\").replace("'", "\\'") - if block_type == Types.BlockType.VECTOR2: + elif variant_type == TYPE_VECTOR2: text = "Vector2(%s)" % text return text diff --git a/addons/block_code/ui/blocks/utilities/snap_point/snap_point.gd b/addons/block_code/ui/blocks/utilities/snap_point/snap_point.gd index 42a01e8a..3b9e9b72 100644 --- a/addons/block_code/ui/blocks/utilities/snap_point/snap_point.gd +++ b/addons/block_code/ui/blocks/utilities/snap_point/snap_point.gd @@ -6,6 +6,9 @@ extends MarginContainer @export var block_type: Types.BlockType = Types.BlockType.EXECUTE +## When block_type is [enum Types.BlockType.VALUE], the type of the value that can be used at this snap point. +@export var variant_type: Variant.Type + var block: Block diff --git a/addons/block_code/ui/constants.gd b/addons/block_code/ui/constants.gd index 1d7e98a9..d36d45aa 100644 --- a/addons/block_code/ui/constants.gd +++ b/addons/block_code/ui/constants.gd @@ -6,3 +6,4 @@ const KNOB_H = 5.0 const KNOB_Z = 5.0 const CONTROL_MARGIN = 20.0 const OUTLINE_WIDTH = 3.0 +const MINIMUM_SNAP_DISTANCE = 80.0 diff --git a/addons/block_code/ui/picker/categories/category_factory.gd b/addons/block_code/ui/picker/categories/category_factory.gd index 08161af7..28dfb9aa 100644 --- a/addons/block_code/ui/picker/categories/category_factory.gd +++ b/addons/block_code/ui/picker/categories/category_factory.gd @@ -84,7 +84,7 @@ static func get_general_categories() -> Array[BlockCategory]: test_list.append(b) b = BLOCKS["entry_block"].instantiate() - b.block_format = "On body enter [body: NODE]" + b.block_format = "On body enter [body: NODE_PATH]" b.statement = "func _on_body_enter(body):" test_list.append(b) @@ -94,9 +94,9 @@ static func get_general_categories() -> Array[BlockCategory]: var signal_list: Array[Block] = [] b = BLOCKS["entry_block"].instantiate() - # HACK: make signals work with new entry nodes. NONE instead of STRING type allows + # HACK: make signals work with new entry nodes. NIL instead of STRING type allows # plain text input for function name. Should revamp signals later - b.block_format = "On signal {signal: NONE}" + b.block_format = "On signal {signal: NIL}" b.statement = "func signal_{signal}():" signal_list.append(b) @@ -111,8 +111,8 @@ static func get_general_categories() -> Array[BlockCategory]: signal_list.append(b) b = BLOCKS["statement_block"].instantiate() - b.block_format = "Add {node: NODE} to group {group: STRING}" - b.statement = "{node}.add_to_group({group})" + b.block_format = "Add {node: NODE_PATH} to group {group: STRING}" + b.statement = "get_node({node}).add_to_group({group})" signal_list.append(b) b = BLOCKS["statement_block"].instantiate() @@ -121,20 +121,20 @@ static func get_general_categories() -> Array[BlockCategory]: signal_list.append(b) b = BLOCKS["statement_block"].instantiate() - b.block_format = "Remove {node: NODE} from group {group: STRING}" - b.statement = "{node}.remove_from_group({group})" + b.block_format = "Remove {node: NODE_PATH} from group {group: STRING}" + b.statement = "get_node({node}).remove_from_group({group})" signal_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.BOOL + b.variant_type = TYPE_BOOL b.block_format = "Is in group {group: STRING}" b.statement = "is_in_group({group})" signal_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.BOOL - b.block_format = "Is {node: NODE} in group {group: STRING}" - b.statement = "{node}.is_in_group({group})" + b.variant_type = TYPE_BOOL + b.block_format = "Is {node: NODE_PATH} in group {group: STRING}" + b.statement = "get_node({node}).is_in_group({group})" signal_list.append(b) var signal_category: BlockCategory = BlockCategory.new("Signal", signal_list, Color("f0c300")) @@ -158,7 +158,7 @@ static func get_general_categories() -> Array[BlockCategory]: variable_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "Get Int {var: STRING}" b.statement = "VAR_DICT[{var}]" variable_list.append(b) @@ -174,31 +174,31 @@ static func get_general_categories() -> Array[BlockCategory]: var math_list: Array[Block] = [] b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "{a: INT} + {b: INT}" b.statement = "({a} + {b})" math_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "{a: INT} - {b: INT}" b.statement = "({a} - {b})" math_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "{a: INT} * {b: INT}" b.statement = "({a} * {b})" math_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "{a: INT} / {b: INT}" b.statement = "({a} / {b})" math_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.INT + b.variant_type = TYPE_INT b.block_format = "{base: INT} ^ {exp: INT}" b.statement = "(pow({base}, {exp}))" math_list.append(b) @@ -211,20 +211,20 @@ static func get_general_categories() -> Array[BlockCategory]: for op in ["==", ">", "<", ">=", "<=", "!="]: b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.BOOL + b.variant_type = TYPE_BOOL b.block_format = "{int1: INT} %s {int2: INT}" % op b.statement = "({int1} %s {int2})" % op logic_list.append(b) for op in ["and", "or"]: b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.BOOL + b.variant_type = TYPE_BOOL b.block_format = "{bool1: BOOL} %s {bool2: BOOL}" % op b.statement = "({bool1} %s {bool2})" % op logic_list.append(b) b = BLOCKS["parameter_block"].instantiate() - b.block_type = Types.BlockType.BOOL + b.variant_type = TYPE_BOOL b.block_format = "Not {bool: BOOL}" b.statement = "(!{bool})" logic_list.append(b) @@ -264,31 +264,13 @@ static func add_to_categories(main: Array[BlockCategory], addition: Array[BlockC return main -static func built_in_type_to_block_type(type: Variant.Type): - match type: - TYPE_BOOL: - return Types.BlockType.BOOL - TYPE_INT: - return Types.BlockType.INT - TYPE_FLOAT: - return Types.BlockType.FLOAT - TYPE_STRING: - return Types.BlockType.STRING - TYPE_VECTOR2: - return Types.BlockType.VECTOR2 - TYPE_COLOR: - return Types.BlockType.COLOR - - return null - - static func property_to_blocklist(property: Dictionary) -> Array[Block]: var block_list: Array[Block] = [] - var block_type = built_in_type_to_block_type(property.type) + var block_type = property.type if block_type: - var type_string: String = Types.BlockType.find_key(block_type) + var type_string: String = Types.VARIANT_TYPE_TO_STRING[block_type] var b = BLOCKS["statement_block"].instantiate() b.block_format = "Set %s to {value: %s}" % [property.name.capitalize(), type_string] @@ -376,19 +358,19 @@ static func _get_input_blocks() -> Array[Block]: for action: StringName in InputMap.get_actions(): var block: Block = BLOCKS["parameter_block"].instantiate() - block.block_type = Types.BlockType.BOOL + block.variant_type = TYPE_BOOL block.block_format = "Is action %s pressed" % action block.statement = 'Input.is_action_pressed("%s")' % action block_list.append(block) block = BLOCKS["parameter_block"].instantiate() - block.block_type = Types.BlockType.BOOL + block.variant_type = TYPE_BOOL block.block_format = "Is action %s just pressed" % action block.statement = 'Input.is_action_just_pressed("%s")' % action block_list.append(block) block = BLOCKS["parameter_block"].instantiate() - block.block_type = Types.BlockType.BOOL + block.variant_type = TYPE_BOOL block.block_format = "Is action %s just released" % action block.statement = 'Input.is_action_just_released("%s")' % action block_list.append(block) diff --git a/test_game/test_game.tscn b/test_game/test_game.tscn index cea07b82..30440c27 100644 --- a/test_game/test_game.tscn +++ b/test_game/test_game.tscn @@ -241,7 +241,7 @@ path_child_pairs = [] [sub_resource type="Resource" id="Resource_7f05b"] script = ExtResource("3_dpt5n") block_class = &"ParameterBlock" -serialized_props = [["block_name", "parameter_block"], ["label", "Param"], ["color", Color(0.941176, 0.764706, 0, 1)], ["block_type", 6], ["position", Vector2(0, 0)], ["block_format", "Is {node: NODE} in group {group: STRING}"], ["statement", "{node}.is_in_group({group})"], ["param_input_strings", { +serialized_props = [["block_name", "parameter_block"], ["label", "Param"], ["color", Color(0.941176, 0.764706, 0, 1)], ["block_type", 6], ["position", Vector2(0, 0)], ["block_format", "Is {node: NODE_PATH} in group {group: STRING}"], ["statement", "get_node({node}).is_in_group({group})"], ["param_input_strings", { "group": "Enemy", "node": "" }]] @@ -278,7 +278,7 @@ path_child_pairs = [[NodePath("VBoxContainer/MarginContainer/Rows/Row0/RowHBoxCo [sub_resource type="Resource" id="Resource_um7l6"] script = ExtResource("3_dpt5n") block_class = &"EntryBlock" -serialized_props = [["block_name", "entry_block"], ["label", "EntryBlock"], ["color", Color(0.6, 0.537255, 0.87451, 1)], ["block_type", 2], ["position", Vector2(472, 469)], ["block_format", "On body enter [body: NODE]"], ["statement", "func _on_body_enter(body):"], ["param_input_strings", { +serialized_props = [["block_name", "entry_block"], ["label", "EntryBlock"], ["color", Color(0.6, 0.537255, 0.87451, 1)], ["block_type", 2], ["position", Vector2(472, 469)], ["block_format", "On body enter [body: NODE_PATH]"], ["statement", "func _on_body_enter(body):"], ["param_input_strings", { "body": "" }]] @@ -302,7 +302,7 @@ path_child_pairs = [] [sub_resource type="Resource" id="Resource_g4l32"] script = ExtResource("3_dpt5n") block_class = &"EntryBlock" -serialized_props = [["block_name", "entry_block"], ["label", "EntryBlock"], ["color", Color(0.941176, 0.764706, 0, 1)], ["block_type", 2], ["position", Vector2(537, 268)], ["block_format", "On signal {signal: NONE}"], ["statement", "func signal_{signal}():"], ["param_input_strings", { +serialized_props = [["block_name", "entry_block"], ["label", "EntryBlock"], ["color", Color(0.941176, 0.764706, 0, 1)], ["block_type", 2], ["position", Vector2(537, 268)], ["block_format", "On signal {signal: NIL}"], ["statement", "func signal_{signal}():"], ["param_input_strings", { "signal": "will_hi" }]]