From 6b462706846eda990255a0b15246e85d35973b0d Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Wed, 9 Oct 2024 15:01:11 +0200 Subject: [PATCH 01/88] Sync block coding plugin commit d16f29 --- addons/block_code/README.md | 43 +- addons/block_code/block_code_plugin.gd | 46 +- .../communication/add_node_to_group.tres | 17 +- .../blocks/communication/add_to_group.tres | 16 +- .../communication/area2d_on_entered.tres | 4 +- .../communication/area2d_on_exited.tres | 4 +- .../communication/call_method_group.tres | 22 +- .../communication/call_method_node.tres | 3 +- .../blocks/communication/define_method.tres | 3 +- .../blocks/communication/get_node.gd | 33 + .../blocks/communication/get_node.tres | 27 + .../block_code/blocks/communication/groups.gd | 71 ++ .../blocks/communication/is_in_group.tres | 18 +- .../communication/is_node_in_group.tres | 18 +- .../communication/remove_from_group.tres | 22 +- .../communication/remove_node_from_group.tres | 24 +- .../communication/rigidbody2d_on_entered.tres | 4 +- .../communication/rigidbody2d_on_exited.tres | 4 +- .../graphics/animationplayer_is_playing.tres | 2 +- .../graphics/animationplayer_pause.tres | 2 +- .../blocks/graphics/animationplayer_play.gd | 28 + .../blocks/graphics/animationplayer_play.tres | 18 +- .../blocks/graphics/animationplayer_stop.tres | 2 +- .../blocks/graphics/viewport_center.tres | 5 +- .../blocks/graphics/viewport_height.tres | 5 +- .../blocks/graphics/viewport_width.tres | 5 +- .../input/characterbody2d_is_on_floor.tres | 17 + .../blocks/input/characterbody2d_move.tres | 4 +- .../blocks/input/is_input_actioned.gd | 35 + .../blocks/input/is_input_actioned.tres | 27 + .../blocks/lifecycle/physics_process.tres | 16 - .../block_code/blocks/lifecycle/process.tres | 3 +- .../blocks/lifecycle/queue_free.tres | 8 +- addons/block_code/blocks/lifecycle/ready.tres | 3 +- addons/block_code/blocks/log/breakpoint.tres | 17 + addons/block_code/blocks/log/print.tres | 2 +- addons/block_code/blocks/logic/compare.tres | 6 +- addons/block_code/blocks/logic/else.tres | 8 +- addons/block_code/blocks/logic/else_if.tres | 8 +- addons/block_code/blocks/logic/if.tres | 8 +- addons/block_code/blocks/logic/not.tres | 3 +- .../blocks/loops/await_scene_ready.tres | 3 +- addons/block_code/blocks/loops/break.tres | 8 +- addons/block_code/blocks/loops/continue.tres | 8 +- addons/block_code/blocks/loops/for.tres | 3 +- addons/block_code/blocks/loops/while.tres | 3 +- .../block_code/blocks/math/randf_range.tres | 2 +- .../block_code/blocks/math/randi_range.tres | 2 +- .../blocks/math/vector_from_angle.tres | 17 + .../blocks/math/vector_multiply.tres | 20 + .../characterbody2d_move_and_slide.tres | 2 +- .../blocks/sounds/audiostreamplayer_play.tres | 2 +- .../blocks/sounds/audiostreamplayer_stop.tres | 2 +- .../block_code/blocks/sounds/load_sound.tres | 2 +- .../blocks/sounds/pause_continue_sound.tres | 8 +- .../block_code/blocks/sounds/play_sound.tres | 2 +- .../block_code/blocks/sounds/stop_sound.tres | 2 +- .../rigidbody2d_physics_position.tres | 2 +- .../block_code/blocks/variables/vector2.tres | 4 +- addons/block_code/code_generation/ast_list.gd | 37 + .../block_code/code_generation/block_ast.gd | 139 +++ .../code_generation/block_definition.gd | 74 +- .../code_generation/block_extension.gd | 50 + .../code_generation/blocks_catalog.gd | 180 ++-- .../option_data.gd | 1 - .../code_generation/script_generator.gd | 63 ++ .../variable_definition.gd} | 1 - addons/block_code/drag_manager/drag.gd | 12 +- .../block_code/drag_manager/drag_manager.gd | 28 +- .../examples/pong_game/pong_game.tscn | 970 +++++++----------- addons/block_code/examples/spawner/ball.tscn | 29 + .../block_code/examples/spawner/spawner.tscn | 269 +++++ .../examples/spawner/volatile_ball.tscn | 135 +++ .../instruction_tree/instruction_tree.gd | 58 -- .../block_script_serialization.gd | 261 ++++- .../serialization/block_serialization.gd | 17 +- .../serialization/block_serialization_tree.gd | 24 + .../block_serialized_properties.gd | 12 - .../serialization/default_block_script.tres | 34 +- .../value_block_serialization.gd | 9 + .../simple_character/simple_character.gd | 67 +- .../simple_scoring/simple_scoring.gd | 10 +- .../simple_spawner/simple_spawner.gd | 160 +++ addons/block_code/types/types.gd | 2 + .../ui/block_canvas/block_canvas.gd | 227 +++- .../ui/block_canvas/block_canvas.tscn | 2 + addons/block_code/ui/block_editor_context.gd | 33 + addons/block_code/ui/block_tree_util.gd | 70 +- addons/block_code/ui/blocks/block/block.gd | 216 ++-- .../ui/blocks/control_block/control_block.gd | 160 +-- .../blocks/control_block/control_block.tscn | 78 +- .../ui/blocks/entry_block/entry_block.gd | 18 +- .../ui/blocks/entry_block/entry_block.tscn | 60 +- .../blocks/parameter_block/parameter_block.gd | 51 +- .../parameter_block/parameter_block.tscn | 27 +- .../blocks/statement_block/statement_block.gd | 143 +-- .../statement_block/statement_block.tscn | 22 +- .../drag_drop_area/drag_drop_area.gd | 74 +- .../drag_drop_area/drag_drop_area.tscn | 7 +- .../parameter_input/parameter_input.gd | 174 +++- .../parameter_input/parameter_input.tscn | 74 +- .../parameter_output/parameter_output.gd | 33 +- .../parameter_output/parameter_output.tscn | 5 +- .../blocks/utilities/snap_point/snap_point.gd | 10 + .../template_editor/template_editor.gd | 144 +++ .../template_editor/template_editor.tscn | 17 + addons/block_code/ui/constants.gd | 11 + addons/block_code/ui/main_panel.gd | 114 +- .../ui/picker/categories/block_category.gd | 11 +- .../categories/block_category_button.gd | 2 +- .../categories/block_category_display.gd | 72 +- .../categories/block_category_display.tscn | 4 +- .../ui/picker/categories/category_factory.gd | 111 +- .../create_variable_button.gd | 8 +- .../create_variable_button.tscn | 2 + .../create_variable_dialog.gd | 42 +- .../create_variable_dialog.tscn | 11 +- .../variable_category_display.gd | 14 +- .../variable_category_display.tscn | 17 +- addons/block_code/ui/picker/picker.gd | 164 +-- addons/block_code/ui/title_bar/title_bar.gd | 23 +- addons/block_code/ui/util.gd | 160 +-- 122 files changed, 3659 insertions(+), 2097 deletions(-) create mode 100644 addons/block_code/blocks/communication/get_node.gd create mode 100644 addons/block_code/blocks/communication/get_node.tres create mode 100644 addons/block_code/blocks/communication/groups.gd create mode 100644 addons/block_code/blocks/graphics/animationplayer_play.gd create mode 100644 addons/block_code/blocks/input/characterbody2d_is_on_floor.tres create mode 100644 addons/block_code/blocks/input/is_input_actioned.gd create mode 100644 addons/block_code/blocks/input/is_input_actioned.tres delete mode 100644 addons/block_code/blocks/lifecycle/physics_process.tres create mode 100644 addons/block_code/blocks/log/breakpoint.tres create mode 100644 addons/block_code/blocks/math/vector_from_angle.tres create mode 100644 addons/block_code/blocks/math/vector_multiply.tres create mode 100644 addons/block_code/code_generation/ast_list.gd create mode 100644 addons/block_code/code_generation/block_ast.gd create mode 100644 addons/block_code/code_generation/block_extension.gd rename addons/block_code/{ui/block_canvas => code_generation}/option_data.gd (88%) create mode 100644 addons/block_code/code_generation/script_generator.gd rename addons/block_code/{ui/block_canvas/variable_resource.gd => code_generation/variable_definition.gd} (87%) create mode 100644 addons/block_code/examples/spawner/ball.tscn create mode 100644 addons/block_code/examples/spawner/spawner.tscn create mode 100644 addons/block_code/examples/spawner/volatile_ball.tscn delete mode 100644 addons/block_code/instruction_tree/instruction_tree.gd create mode 100644 addons/block_code/serialization/block_serialization_tree.gd delete mode 100644 addons/block_code/serialization/block_serialized_properties.gd create mode 100644 addons/block_code/serialization/value_block_serialization.gd create mode 100644 addons/block_code/simple_spawner/simple_spawner.gd create mode 100644 addons/block_code/ui/block_editor_context.gd create mode 100644 addons/block_code/ui/blocks/utilities/template_editor/template_editor.gd create mode 100644 addons/block_code/ui/blocks/utilities/template_editor/template_editor.tscn diff --git a/addons/block_code/README.md b/addons/block_code/README.md index 9fdf477..d349988 100644 --- a/addons/block_code/README.md +++ b/addons/block_code/README.md @@ -11,14 +11,12 @@ Our aim is to reduce the learning curve faced by learners who are on the early p With this project, we aim to reduce the height of the mountain that such learners have to climb. Specifically, we aim to eliminate the requirement of learners having to simultaneously learn to code while building their first games. Instead of writing GDScript to implement games, this plugin enables learners use block coding. Tools like [Scratch](https://scratch.mit.edu/), [Blockly](https://developers.google.com/blockly), and [MakeCode](https://www.microsoft.com/en-us/makecode) have demonstrated that block coding can be much more accessible and intuitive to beginners than textual programming—we are bringing those concepts into Godot to help learners become familiar with some aspects of Godot itself while simplifying the creation of their first games. -### Constraints - -In order to be learner-friendly, we have to implement blocks at a suitable level of abstraction. For example, in GDScript you would typically move a sprite around the screen by examining input events and adjusting a sprite's movement vector accordingly—but we do not wish to express this level of detail in blocks. Instead, we lean much more towards the kinds of blocks you can find in MakeCode Arcade, such as having a single block for "move mySprite with buttons". - -Expressing an appropriate layer of abstraction is perhaps the most challenging aspect of this project, and will likely place limits upon what can be achieved with this tool. We do not aim to express the full power of Godot & GDScript with this block coding plugin, but rather, our objective is to provide a gentler introduction to Godot for learners, such that they can get familiar with other aspects of the Godot Editor and learn programming concepts while creating basic games. We envision that learners would use block coding as a stepping stone and then later progress onto learning GDScript. +In order to be learner-friendly, we implement blocks at a suitable level of abstraction. For example, we have blocks that allow the user to trivially connect keyboard input to the movement of a particular game element, and to make the score show up on-screen. That abstraction does place limits on what can be achieved with this tool, while still allowing us to provide a gentler introduction to Godot for learners, such that they can get familiar with other aspects of the Godot Editor and learn programming concepts while creating basic games. We envision that learners would use block coding as a stepping stone and then later progress onto learning GDScript. That said, we are in no way opposed to having this project grow to be able to create more complex games, as long as it does not negatively affect the experience for early stage learners. +See our [pedagogy and audience documentation](docs/PEDAGOGY.md) for more info. + ## Getting Started 1. Install the plugin through the Godot AssetLib searching for the name @@ -32,11 +30,11 @@ That said, we are in no way opposed to having this project grow to be able to cr 2. Make sure to enable the plugin in **Project** → **Project Settings** → **Plugins**. -3. You're ready to get started! Open a scene, and add a **BlockCode** child node to any node in the scene using the **Add Child Node** menu dialog. +3. You're ready to get started! Open a scene, select a node, and observe that there's a **Block Code** section within the lower central pane of the Godot editor, where you usually find debugging, animation and shader functionality. Click **Block Code** and then use the **Add Block Code** button to create a block canvas. -4. The **Block Code** editor will open in a new tab. Drag blocks from the picker and snap them together to create a script. You can switch to other Block Code scripts by selecting the respective BlockCode node in the scene tree. +4. Drag blocks from the picker and snap them together to create a script. You can switch to other Block Code scripts by selecting the respective node from the scene tree. -5. **Run** the scene to see your Block Code scripts in action. Block Code scripts are attached to the BlockCode node's parent, and are saved to the scene. +5. **Run** the scene to see your Block Code scripts in action. Block Code scripts are saved within the scene. If you clone the plugin's git repository and open it in Godot, you will be presented with a block-built Pong game as an example. @@ -50,6 +48,20 @@ We will now seek feedback from learners, educators and game makers, as well as r - Should this be a plugin or an extension? - Should blocks generate GDScript or be dynamically executed? +There is no language or data format stability implemented or expected in these early stages. If you upgrade the block coding plugin within an existing project, expect any existing block scripts to stop working and need reimplementing from scratch. For now, you probably want to avoid updating the plugin within your project if it's meeting your needs, or only doing that very sporadically. We will consider offering stability guarantees in future stages of development. + +## General user guidance + +Block scripts run against the node where you created them. The "Queue Free" block is going to free that node, not any other. + +The selection of available blocks varies based on the node type. For example, create a block script on an `Area2D` and you will notice that you have an `On body entered` signal handling block available. Create a node script on an `AnimationPlayer` node and you will observe blocks for starting and stopping animations. + +If you wish to switch context to another node, you need to define a function in that other node, and then call it. Once execution jumps into that function, blocks will now act against that other node, and you'll have access to type-specific blocks belonging to that other node. You'll need do this kind of thing if you want to trigger the freeing of another node, or trigger an animation to start playing. This is both strong in conveying the concepts of objects and encapsulation, while also a bit tedious - we may revisit in future! + +We have some high level blocks for simplifying common game elements. Add a SimpleCharacter node to get a game element that can be connected to keyboard input with just one type-specific block. Add a SimpleScoring node to display a score on-screen, accompanied by simple blocks for adjusting that score. + +Lean into animations! Godot's animations functionality goes beyond just simple animations of graphics. You can do so much by combining block coding with Godot's powerful animations editor. + ## Feedback Please share feedback in the [Godot Forum Block Coding thread](https://forum.godotengine.org/t/block-coding-high-level-block-based-visual-programming/68941). @@ -90,3 +102,18 @@ There are several other GUT command line options for running specific tests. For example, `-gtest=path/to/test_script_1.gd,path/to/test_script_2.gd` can be used to run specific test scripts. A specific test function can be specified with `-gunit_test_name=test_to_run`. + +### Using the Development Version of the Plugin + +1. If your project already has the BlockCode plugin installed: + 1. Ensure you have committed your project to Git, including the `addons/block_code` directory. + At this stage of development, **block code programs written for an older plugin version will + likely not work with a newer version of the plugin**, so it is essential that you take a + snapshot of your project before changing the plugin version. + 2. Under *Project* → *Project Settings…* → *Plugins*, disable the BlockCode plugin + 3. In the *FileSystem* sidebar, delete the `res://addons/block_code` directory +2. Download + [a development snapshot](https://github.com/endlessm/godot-block-coding/archive/main.zip) +3. Under *AssetLib*, click *Import…*, and browse to the `main.zip` file you just downloaded +4. Check the *☑ Ignore assert root* option, and click *Install* +5. Under *Project* → *Project Settings…* → *Plugins*, enable the BlockCode plugin diff --git a/addons/block_code/block_code_plugin.gd b/addons/block_code/block_code_plugin.gd index f7e0ac8..0067b7f 100644 --- a/addons/block_code/block_code_plugin.gd +++ b/addons/block_code/block_code_plugin.gd @@ -1,5 +1,6 @@ @tool extends EditorPlugin + const MainPanelScene := preload("res://addons/block_code/ui/main_panel.tscn") const MainPanel = preload("res://addons/block_code/ui/main_panel.gd") const Types = preload("res://addons/block_code/types/types.gd") @@ -12,7 +13,6 @@ const BlockInspectorPlugin := preload("res://addons/block_code/inspector_plugin/ var block_inspector_plugin: BlockInspectorPlugin var editor_inspector: EditorInspector -var editor_selection: EditorSelection var _selected_block_code: BlockCode @@ -24,8 +24,6 @@ const DISABLED_CLASSES := [ "ParameterBlock", "StatementBlock", "SnapPoint", - "BlockSerialization", - "BlockSerializedProperties", "BlockScriptSerialization", "CategoryFactory", ] @@ -35,7 +33,6 @@ func _enter_tree(): Types.init_cast_graph() editor_inspector = EditorInterface.get_inspector() - editor_selection = EditorInterface.get_selection() main_panel = MainPanelScene.instantiate() main_panel.script_window_requested.connect(script_window_requested) @@ -95,43 +92,20 @@ func _exit_tree(): func _ready(): - connect("scene_changed", _on_scene_changed) editor_inspector.connect("edited_object_changed", _on_editor_inspector_edited_object_changed) - _on_scene_changed(EditorInterface.get_edited_scene_root()) _on_editor_inspector_edited_object_changed() -func _on_scene_changed(scene_root: Node): - main_panel.switch_scene(scene_root) - - func _on_editor_inspector_edited_object_changed(): var edited_object = editor_inspector.get_edited_object() - #var edited_node = edited_object as Node - var selected_nodes = editor_selection.get_selected_nodes() - - if edited_object is BlockCode: + var block_code_node = edited_object as BlockCode + if block_code_node: + # If a block code node was explicitly selected, activate the + # Block Code panel. make_bottom_panel_item_visible(main_panel) - - if edited_object is BlockCode and selected_nodes.size() == 1 and edited_object.owner and edited_object != _selected_block_code: - # If a BlockCode node is being edited, and it was explicitly selected - # (as opposed to edited in the Inspector alone), select its parent node - # as well. This provides a clearer indication of what is being edited. - # Changing the selection will cause edited_object_changed to fire again, - # so we will return early to avoid duplicate work. - var parent_node = edited_object.get_parent() - if parent_node: - editor_selection.add_node.call_deferred(parent_node) - return - - if edited_object and edited_object.get_class() == "MultiNodeEdit": - # If multiple nodes are shown in the inspector, we will find the first - # BlockCode node in the list of selected nodes and use that. This - # occurs when the user selects multiple items in the Scene panel, or - # when we select the parent of a BlockCode node. - edited_object = selected_nodes.filter(func(node): return node is BlockCode).pop_front() - - var block_code_node = list_block_code_nodes_for_node(edited_object as Node).pop_front() + else: + # Find the first block code child. + block_code_node = list_block_code_nodes_for_node(edited_object as Node).pop_front() select_block_code_node(block_code_node) @@ -145,7 +119,7 @@ func select_block_code_node(block_code: BlockCode): if not is_block_code_editable(block_code): block_code = null - if _selected_block_code: + if is_instance_valid(_selected_block_code): _selected_block_code.tree_entered.disconnect(_on_selected_block_code_changed) _selected_block_code.tree_exited.disconnect(_on_selected_block_code_changed) _selected_block_code.property_list_changed.disconnect(_on_selected_block_code_changed) @@ -153,7 +127,7 @@ func select_block_code_node(block_code: BlockCode): _selected_block_code = block_code - if _selected_block_code: + if is_instance_valid(_selected_block_code): _selected_block_code.tree_entered.connect(_on_selected_block_code_changed) _selected_block_code.tree_exited.connect(_on_selected_block_code_changed) _selected_block_code.property_list_changed.connect(_on_selected_block_code_changed) diff --git a/addons/block_code/blocks/communication/add_node_to_group.tres b/addons/block_code/blocks/communication/add_node_to_group.tres index b4ea7d6..2e386d2 100644 --- a/addons/block_code/blocks/communication/add_node_to_group.tres +++ b/addons/block_code/blocks/communication/add_node_to_group.tres @@ -1,16 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bpvefei72nh3a"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://bpvefei72nh3a"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_5qal7"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_auf06"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="1_p83c7"] + +[sub_resource type="Resource" id="Resource_sus0f"] +script = ExtResource("1_auf06") +selected = 0 +items = [] [resource] script = ExtResource("1_5qal7") name = &"add_node_to_group" +target_node_class = "" description = "Add the node into the group" category = "Communication | Groups" type = 2 variant_type = 0 -display_template = "Add {node: OBJECT} to group {group: STRING}" +display_template = "add {node: OBJECT} to group {group: STRING}" code_template = "{node}.add_to_group({group})" -defaults = {} +defaults = { +"group": SubResource("Resource_sus0f") +} signal_name = "" scope = "" +extension_script = ExtResource("1_p83c7") diff --git a/addons/block_code/blocks/communication/add_to_group.tres b/addons/block_code/blocks/communication/add_to_group.tres index d4ccb2b..04e14a6 100644 --- a/addons/block_code/blocks/communication/add_to_group.tres +++ b/addons/block_code/blocks/communication/add_to_group.tres @@ -1,6 +1,13 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bvrmau8atjx1x"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://bvrmau8atjx1x"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_aom4j"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_bcm71"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="2_42ixf"] + +[sub_resource type="Resource" id="Resource_fk0wa"] +script = ExtResource("1_aom4j") +selected = 0 +items = [] [resource] script = ExtResource("1_bcm71") @@ -10,8 +17,11 @@ description = "Add this node into the group" category = "Communication | Groups" type = 2 variant_type = 0 -display_template = "Add to group {group: STRING}" +display_template = "add to group {group: STRING}" code_template = "add_to_group({group})" -defaults = {} +defaults = { +"group": SubResource("Resource_fk0wa") +} signal_name = "" scope = "" +extension_script = ExtResource("2_42ixf") diff --git a/addons/block_code/blocks/communication/area2d_on_entered.tres b/addons/block_code/blocks/communication/area2d_on_entered.tres index 370331e..7b35896 100644 --- a/addons/block_code/blocks/communication/area2d_on_entered.tres +++ b/addons/block_code/blocks/communication/area2d_on_entered.tres @@ -10,8 +10,8 @@ description = "" category = "Communication | Methods" type = 1 variant_type = 0 -display_template = "On [body: OBJECT] entered" -code_template = "func _on_body_entered(body: Node2D): +display_template = "when this node collides with [something: OBJECT]" +code_template = "func _on_body_entered(something: Node2D): " defaults = {} signal_name = "body_entered" diff --git a/addons/block_code/blocks/communication/area2d_on_exited.tres b/addons/block_code/blocks/communication/area2d_on_exited.tres index ba3d0e5..7e959b1 100644 --- a/addons/block_code/blocks/communication/area2d_on_exited.tres +++ b/addons/block_code/blocks/communication/area2d_on_exited.tres @@ -10,8 +10,8 @@ description = "" category = "Communication | Methods" type = 1 variant_type = 0 -display_template = "On [body: OBJECT] exited" -code_template = "func _on_body_exited(body: Node2D): +display_template = "when this node stops colliding with [something: OBJECT]" +code_template = "func _on_body_exited(something: Node2D): " defaults = {} signal_name = "body_exited" diff --git a/addons/block_code/blocks/communication/call_method_group.tres b/addons/block_code/blocks/communication/call_method_group.tres index 5df2fe2..06515f5 100644 --- a/addons/block_code/blocks/communication/call_method_group.tres +++ b/addons/block_code/blocks/communication/call_method_group.tres @@ -1,15 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c15vtdfihdxb8"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://c15vtdfihdxb8"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_3nuts"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_mlm68"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="1_of577"] + +[sub_resource type="Resource" id="Resource_f4ctg"] +script = ExtResource("1_3nuts") +selected = 0 +items = [] [resource] script = ExtResource("1_mlm68") name = &"call_method_group" +target_node_class = "" +description = "Calls the method/function on each member of the given group" +category = "Communication | Methods" type = 2 variant_type = 0 -display_template = "Call method {method_name: STRING} in group {group: STRING}" +display_template = "call method {method_name: STRING} in group {group: STRING}" code_template = "get_tree().call_group({group}, {method_name})" -description = "Calls the method/function on each member of the given group" -category = "Communication | Methods" -defaults = {} +defaults = { +"group": SubResource("Resource_f4ctg") +} signal_name = "" +scope = "" +extension_script = ExtResource("1_of577") diff --git a/addons/block_code/blocks/communication/call_method_node.tres b/addons/block_code/blocks/communication/call_method_node.tres index ba78855..b677cdb 100644 --- a/addons/block_code/blocks/communication/call_method_node.tres +++ b/addons/block_code/blocks/communication/call_method_node.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_pg363") name = &"call_method_node" +target_node_class = "" description = "Calls the method/function of the given node" category = "Communication | Methods" type = 2 variant_type = 0 -display_template = "Call method {method_name: STRING} in node {node: OBJECT}" +display_template = "call method {method_name: STRING} in node {node: OBJECT}" code_template = "{node}.call({method_name})" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/communication/define_method.tres b/addons/block_code/blocks/communication/define_method.tres index 4278151..6ae8ca0 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/communication/get_node.gd b/addons/block_code/blocks/communication/get_node.gd new file mode 100644 index 0000000..61638da --- /dev/null +++ b/addons/block_code/blocks/communication/get_node.gd @@ -0,0 +1,33 @@ +@tool +extends BlockExtension + +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") +const Util = preload("res://addons/block_code/ui/util.gd") + + +func _find_paths(paths: Array[NodePath], node: Node, path_root: Node, block_parent: Node): + # Add any non-BlockCode nodes that aren't the parent of the current + # BlockCode node. + if not node is BlockCode: + var node_path: NodePath = Util.node_scene_path(node, block_parent, path_root) + if not node_path in [^"", ^"."]: + paths.append(node_path) + + for child in node.get_children(): + _find_paths(paths, child, path_root, block_parent) + + +func get_defaults_for_node(context_node: Node) -> Dictionary: + # The default paths are only needed in the editor. + if not Engine.is_editor_hint(): + return {} + + var scene_root: Node = EditorInterface.get_edited_scene_root() + var path_root: Node = scene_root.get_parent() + var paths: Array[NodePath] + _find_paths(paths, scene_root, path_root, context_node) + + if not paths: + return {} + + return {"path": OptionData.new(paths)} diff --git a/addons/block_code/blocks/communication/get_node.tres b/addons/block_code/blocks/communication/get_node.tres new file mode 100644 index 0000000..22f7f5f --- /dev/null +++ b/addons/block_code/blocks/communication/get_node.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://canpdkahokjqs"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_bk47y"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_d60g7"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/get_node.gd" id="1_we5wl"] + +[sub_resource type="Resource" id="Resource_esr4a"] +script = ExtResource("1_bk47y") +selected = 0 +items = [] + +[resource] +script = ExtResource("1_d60g7") +name = &"get_node" +target_node_class = "" +description = "Get the node at the given path" +category = "Communication | Nodes" +type = 3 +variant_type = 24 +display_template = "{path: NIL}" +code_template = "get_node(\"{path}\")" +defaults = { +"path": SubResource("Resource_esr4a") +} +signal_name = "" +scope = "" +extension_script = ExtResource("1_we5wl") diff --git a/addons/block_code/blocks/communication/groups.gd b/addons/block_code/blocks/communication/groups.gd new file mode 100644 index 0000000..8faf4aa --- /dev/null +++ b/addons/block_code/blocks/communication/groups.gd @@ -0,0 +1,71 @@ +@tool +## Common BlockExtension for scene group options. +extends BlockExtension + +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") +const Util = preload("res://addons/block_code/ui/util.gd") + + +# Global groups are just project settings in the global_group group. +# ProjectSettings doesn't offer an API to get the project groups or to get all +# settings. Fortunately, settings are simply implemented as properties on the +# ProjectSettings object. +static func _add_global_groups(groups: Dictionary): + for prop in ProjectSettings.get_property_list(): + var name: String = prop["name"] + var parts := name.split("/", false, 1) + if parts[0] == "global_group": + groups[parts[1]] = null + + +# Add all the groups in a node and its children, ignoring internal _ prefixed +# groups. +static func _add_node_groups(groups: Dictionary, node: Node): + for group in node.get_groups(): + if not group.begins_with("_"): + groups[String(group)] = null + + for child in node.get_children(): + _add_node_groups(groups, child) + + +func _get_edited_scene_groups() -> Array[String]: + var groups: Dictionary + _add_global_groups(groups) + + var root := context_node.get_tree().edited_scene_root + _add_node_groups(groups, root) + + var sorted_groups: Array[String] + sorted_groups.assign(groups.keys()) + sorted_groups.sort() + return sorted_groups + + +func _init(): + # FIXME: Only global group changes are monitored. Scene local groups should + # also be monitored, but godot does not have any reasonable API to do that. + ProjectSettings.settings_changed.connect(_on_project_settings_changed) + + +func _context_node_changed(): + # If the context node changed, the scene local groups need to be updated. + changed.emit() + + +func get_defaults() -> Dictionary: + if not context_node: + return {} + + # The default groups are only needed in the editor. + if not Util.node_is_part_of_edited_scene(context_node): + return {} + + var groups: Array[String] = _get_edited_scene_groups() + return {"group": OptionData.new(groups)} + + +func _on_project_settings_changed(): + # FIXME: The global groups should be cached and compared so that the + # defaults are only changed when the global groups actually change. + changed.emit() diff --git a/addons/block_code/blocks/communication/is_in_group.tres b/addons/block_code/blocks/communication/is_in_group.tres index 0b38920..afd089d 100644 --- a/addons/block_code/blocks/communication/is_in_group.tres +++ b/addons/block_code/blocks/communication/is_in_group.tres @@ -1,15 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://q4cnstftvsiu"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://q4cnstftvsiu"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_cla3i"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_tjyq5"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="2_o165d"] + +[sub_resource type="Resource" id="Resource_d0v0d"] +script = ExtResource("1_cla3i") +selected = 0 +items = [] [resource] script = ExtResource("1_tjyq5") name = &"is_in_group" +target_node_class = "" description = "Is this node in the group" category = "Communication | Groups" type = 3 variant_type = 1 -display_template = "Is in group {group: STRING}" +display_template = "is in group {group: STRING}" code_template = "is_in_group({group})" -defaults = {} +defaults = { +"group": SubResource("Resource_d0v0d") +} signal_name = "" +scope = "" +extension_script = ExtResource("2_o165d") diff --git a/addons/block_code/blocks/communication/is_node_in_group.tres b/addons/block_code/blocks/communication/is_node_in_group.tres index 3639b33..d56b3f7 100644 --- a/addons/block_code/blocks/communication/is_node_in_group.tres +++ b/addons/block_code/blocks/communication/is_node_in_group.tres @@ -1,15 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bbtdxeey74x67"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://bbtdxeey74x67"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_5krrs"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="1_r4prw"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_y4j0k"] + +[sub_resource type="Resource" id="Resource_o38ym"] +script = ExtResource("1_y4j0k") +selected = 0 +items = [] [resource] script = ExtResource("1_5krrs") name = &"is_node_in_group" +target_node_class = "" description = "Is the node in the group" category = "Communication | Groups" type = 3 variant_type = 1 -display_template = "Is {node: OBJECT} in group {group: STRING}" +display_template = "{node: OBJECT} is in group {group: STRING}" code_template = "{node}.is_in_group({group})" -defaults = {} +defaults = { +"group": SubResource("Resource_o38ym") +} signal_name = "" +scope = "" +extension_script = ExtResource("1_r4prw") diff --git a/addons/block_code/blocks/communication/remove_from_group.tres b/addons/block_code/blocks/communication/remove_from_group.tres index 7e20f65..b25fe8a 100644 --- a/addons/block_code/blocks/communication/remove_from_group.tres +++ b/addons/block_code/blocks/communication/remove_from_group.tres @@ -1,15 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dgenw5wyqorvq"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://dgenw5wyqorvq"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_cdwef"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="1_i50fw"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_mnxp7"] + +[sub_resource type="Resource" id="Resource_45b71"] +script = ExtResource("1_mnxp7") +selected = 0 +items = [] [resource] script = ExtResource("1_cdwef") name = &"remove_from_group" +target_node_class = "" +description = "Remove this node from the group" +category = "Communication | Groups" type = 2 variant_type = 0 -display_template = "Remove from group {group: STRING}" +display_template = "remove from group {group: STRING}" code_template = "remove_from_group({group})" -description = "Remove this node from the group" -category = "Communication | Groups" -defaults = {} +defaults = { +"group": SubResource("Resource_45b71") +} signal_name = "" +scope = "" +extension_script = ExtResource("1_i50fw") diff --git a/addons/block_code/blocks/communication/remove_node_from_group.tres b/addons/block_code/blocks/communication/remove_node_from_group.tres index 9b132be..defa925 100644 --- a/addons/block_code/blocks/communication/remove_node_from_group.tres +++ b/addons/block_code/blocks/communication/remove_node_from_group.tres @@ -1,15 +1,27 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b2dwk77hnri8y"] +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://b2dwk77hnri8y"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_clwhe"] +[ext_resource type="Script" path="res://addons/block_code/blocks/communication/groups.gd" id="1_h3lhb"] [ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_pec24"] +[sub_resource type="Resource" id="Resource_03rge"] +script = ExtResource("1_clwhe") +selected = 0 +items = [] + [resource] script = ExtResource("1_pec24") name = &"remove_node_from_group" -type = 2 -variant_type = 0 -display_template = "Remove {node: OBJECT} from group {group: STRING}" -code_template = "{node}.remove_from_group({group})" +target_node_class = "" description = "Remove the node from the group" category = "Communication | Groups" -defaults = {} +type = 2 +variant_type = 0 +display_template = "remove {node: OBJECT} from group {group: NIL}" +code_template = "{node}.remove_from_group(\\\"{group}\\\")" +defaults = { +"group": SubResource("Resource_03rge") +} signal_name = "" +scope = "" +extension_script = ExtResource("1_h3lhb") diff --git a/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres b/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres index 5be0ca5..b9859c3 100644 --- a/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres +++ b/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres @@ -10,8 +10,8 @@ description = "" category = "Communication | Methods" type = 1 variant_type = 0 -display_template = "On [body: OBJECT] entered" -code_template = "func _on_body_entered(body: Node2D): +display_template = "when this node collides with [something: OBJECT]" +code_template = "func _on_body_entered(something: Node2D): " defaults = {} signal_name = "body_entered" diff --git a/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres b/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres index f16eb08..7d8cf41 100644 --- a/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres +++ b/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres @@ -10,8 +10,8 @@ description = "" category = "Communication | Methods" type = 1 variant_type = 0 -display_template = "On [body: OBJECT] exited" -code_template = "func _on_body_exited(body: Node2D): +display_template = "when this node stops colliding with [something: OBJECT]" +code_template = "func _on_body_exited(something: Node2D): " defaults = {} signal_name = "body_exited" diff --git a/addons/block_code/blocks/graphics/animationplayer_is_playing.tres b/addons/block_code/blocks/graphics/animationplayer_is_playing.tres index 1e3f657..15e9bdf 100644 --- a/addons/block_code/blocks/graphics/animationplayer_is_playing.tres +++ b/addons/block_code/blocks/graphics/animationplayer_is_playing.tres @@ -10,7 +10,7 @@ description = "Check if an animation is currently playing." category = "Graphics | Animation" type = 3 variant_type = 1 -display_template = "Is playing" +display_template = "is playing" code_template = "is_playing()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/animationplayer_pause.tres b/addons/block_code/blocks/graphics/animationplayer_pause.tres index 15f8090..894f779 100644 --- a/addons/block_code/blocks/graphics/animationplayer_pause.tres +++ b/addons/block_code/blocks/graphics/animationplayer_pause.tres @@ -10,7 +10,7 @@ description = "Pause the currently playing animation." category = "Graphics | Animation" type = 2 variant_type = 0 -display_template = "Pause" +display_template = "pause" code_template = "pause()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/animationplayer_play.gd b/addons/block_code/blocks/graphics/animationplayer_play.gd new file mode 100644 index 0000000..3d3389b --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_play.gd @@ -0,0 +1,28 @@ +@tool +extends BlockExtension + +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") + + +func get_defaults() -> Dictionary: + var animation_player = context_node as AnimationPlayer + + if not animation_player: + return {} + + var animation_list = animation_player.get_animation_list() + + return {"animation": OptionData.new(animation_list)} + + +func _context_node_changed(): + var animation_player = context_node as AnimationPlayer + + if not animation_player: + return + + animation_player.animation_list_changed.connect(_on_animation_list_changed) + + +func _on_animation_list_changed(): + _emit_changed() diff --git a/addons/block_code/blocks/graphics/animationplayer_play.tres b/addons/block_code/blocks/graphics/animationplayer_play.tres index 43bbce1..38149cc 100644 --- a/addons/block_code/blocks/graphics/animationplayer_play.tres +++ b/addons/block_code/blocks/graphics/animationplayer_play.tres @@ -1,12 +1,18 @@ -[gd_resource type="Resource" load_steps=4 format=3 uid="uid://c5e1byehtxwc0"] +[gd_resource type="Resource" load_steps=6 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"] +[ext_resource type="Script" path="res://addons/block_code/blocks/graphics/animationplayer_play.gd" id="2_7ymgi"] + +[sub_resource type="Resource" id="Resource_qpxn2"] +script = ExtResource("1_xu43h") +selected = 0 +items = [] [sub_resource type="Resource" id="Resource_vnp2w"] script = ExtResource("1_xu43h") selected = 0 -items = ["ahead", "backwards"] +items = ["forward", "backwards"] [resource] script = ExtResource("1_emeuv") @@ -16,14 +22,16 @@ description = "Play the animation." category = "Graphics | Animation" type = 2 variant_type = 0 -display_template = "Play {animation: STRING} {direction: OPTION}" -code_template = "if \"{direction}\" == \"ahead\": +display_template = "play {animation: STRING} {direction: NIL}" +code_template = "if {direction} == \"forward\": play({animation}) else: play_backwards({animation}) " defaults = { +"animation": SubResource("Resource_qpxn2"), "direction": SubResource("Resource_vnp2w") } signal_name = "" scope = "" +extension_script = ExtResource("2_7ymgi") diff --git a/addons/block_code/blocks/graphics/animationplayer_stop.tres b/addons/block_code/blocks/graphics/animationplayer_stop.tres index 35369a8..72e62c8 100644 --- a/addons/block_code/blocks/graphics/animationplayer_stop.tres +++ b/addons/block_code/blocks/graphics/animationplayer_stop.tres @@ -10,7 +10,7 @@ description = "Stop the currently playing animation." category = "Graphics | Animation" type = 2 variant_type = 0 -display_template = "Stop" +display_template = "stop" code_template = "stop()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/viewport_center.tres b/addons/block_code/blocks/graphics/viewport_center.tres index 3779ce2..fe0a557 100644 --- a/addons/block_code/blocks/graphics/viewport_center.tres +++ b/addons/block_code/blocks/graphics/viewport_center.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_rc1so") name = &"viewport_center" -description = "" +target_node_class = "" +description = "Coordinates of the middle of the viewable screen when playing." category = "Graphics | Viewport" type = 3 variant_type = 5 -display_template = "Viewport Center" +display_template = "viewport center" code_template = "(func (): var transform: Transform2D = get_viewport_transform(); var scale: Vector2 = transform.get_scale(); return -transform.origin / scale + get_viewport_rect().size / scale / 2).call()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/viewport_height.tres b/addons/block_code/blocks/graphics/viewport_height.tres index 789de6d..70ba7c0 100644 --- a/addons/block_code/blocks/graphics/viewport_height.tres +++ b/addons/block_code/blocks/graphics/viewport_height.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_1debb") name = &"viewport_height" -description = "" +target_node_class = "" +description = "How tall the viewable screen is when playing." category = "Graphics | Viewport" type = 3 variant_type = 3 -display_template = "Viewport Height" +display_template = "viewport height" code_template = "(func (): var transform: Transform2D = get_viewport_transform(); var scale: Vector2 = transform.get_scale(); return -transform.origin.y / scale.y + get_viewport_rect().size.y / scale.y).call()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/graphics/viewport_width.tres b/addons/block_code/blocks/graphics/viewport_width.tres index ceb3073..6600a23 100644 --- a/addons/block_code/blocks/graphics/viewport_width.tres +++ b/addons/block_code/blocks/graphics/viewport_width.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_ll4rh") name = &"viewport_width" -description = "" +target_node_class = "" +description = "How wide the viewable screen is when playing." category = "Graphics | Viewport" type = 3 variant_type = 3 -display_template = "Viewport Width" +display_template = "viewport width" code_template = "(func (): var transform: Transform2D = get_viewport_transform(); var scale: Vector2 = transform.get_scale(); return -transform.origin.x / scale.x + get_viewport_rect().size.x / scale.x).call()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/input/characterbody2d_is_on_floor.tres b/addons/block_code/blocks/input/characterbody2d_is_on_floor.tres new file mode 100644 index 0000000..9dd9dd5 --- /dev/null +++ b/addons/block_code/blocks/input/characterbody2d_is_on_floor.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cbpicqif1ddro"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_btxic"] + +[resource] +script = ExtResource("1_btxic") +name = &"characterbody2d_is_on_floor" +target_node_class = "CharacterBody2D" +description = "True if the character is on the floor." +category = "Physics | Velocity" +type = 3 +variant_type = 1 +display_template = "is on floor" +code_template = "is_on_floor()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/input/characterbody2d_move.tres b/addons/block_code/blocks/input/characterbody2d_move.tres index 0761461..f5dc9a1 100644 --- a/addons/block_code/blocks/input/characterbody2d_move.tres +++ b/addons/block_code/blocks/input/characterbody2d_move.tres @@ -6,11 +6,11 @@ script = ExtResource("1_btxic") name = &"characterbody2d_move" target_node_class = "CharacterBody2D" -description = "" +description = "Move the character up, down, left, and right with the keyboard using the given keys. The speed of movement can be adjusted separately for x (left and right) and y (up and down)." category = "Input" type = 2 variant_type = 0 -display_template = "Move with keys {up: STRING} {down: STRING} {left: STRING} {right: STRING} with speed {speed: VECTOR2}" +display_template = "move with keys {up: STRING} {down: STRING} {left: STRING} {right: STRING} at speed {speed: VECTOR2}" code_template = "var dir = Vector2() dir.x += float(Input.is_key_pressed(OS.find_keycode_from_string({right}))) dir.x -= float(Input.is_key_pressed(OS.find_keycode_from_string({left}))) diff --git a/addons/block_code/blocks/input/is_input_actioned.gd b/addons/block_code/blocks/input/is_input_actioned.gd new file mode 100644 index 0000000..cc28a01 --- /dev/null +++ b/addons/block_code/blocks/input/is_input_actioned.gd @@ -0,0 +1,35 @@ +@tool +extends BlockExtension + +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") + + +func get_defaults() -> Dictionary: + var inputmap_actions = _get_inputmap_actions() + return {"action_name": OptionData.new(inputmap_actions)} + + +static func _get_inputmap_actions() -> Array[StringName]: + var inputmap_actions: Array[StringName] + + var editor_input_actions: Dictionary = {} + var editor_input_action_deadzones: Dictionary = {} + if Engine.is_editor_hint(): + var actions := InputMap.get_actions() + for action in actions: + if action.begins_with("spatial_editor"): + var events := InputMap.action_get_events(action) + editor_input_actions[action] = events + editor_input_action_deadzones[action] = InputMap.action_get_deadzone(action) + + InputMap.load_from_project_settings() + + inputmap_actions = InputMap.get_actions() + + if Engine.is_editor_hint(): + for action in editor_input_actions.keys(): + InputMap.add_action(action, editor_input_action_deadzones[action]) + for event in editor_input_actions[action]: + InputMap.action_add_event(action, event) + + return inputmap_actions diff --git a/addons/block_code/blocks/input/is_input_actioned.tres b/addons/block_code/blocks/input/is_input_actioned.tres new file mode 100644 index 0000000..5f7545f --- /dev/null +++ b/addons/block_code/blocks/input/is_input_actioned.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" load_steps=5 format=3 uid="uid://86j17le5e58u"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/option_data.gd" id="1_d8i05"] +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rprh7"] +[ext_resource type="Script" path="res://addons/block_code/blocks/input/is_input_actioned.gd" id="2_h11b7"] + +[sub_resource type="Resource" id="Resource_ai5in"] +script = ExtResource("1_d8i05") +selected = 0 +items = ["pressed", "just_pressed", "just_released"] + +[resource] +script = ExtResource("1_rprh7") +name = &"is_input_actioned" +target_node_class = "" +description = "True if the specified input action has been pressed or released." +category = "Input" +type = 3 +variant_type = 1 +display_template = "action {action_name: STRING_NAME} is {action: NIL}" +code_template = "Input.is_action_{{action}}('{{action_name}}')" +defaults = { +"action": SubResource("Resource_ai5in") +} +signal_name = "" +scope = "" +extension_script = ExtResource("2_h11b7") diff --git a/addons/block_code/blocks/lifecycle/physics_process.tres b/addons/block_code/blocks/lifecycle/physics_process.tres deleted file mode 100644 index a797f4b..0000000 --- a/addons/block_code/blocks/lifecycle/physics_process.tres +++ /dev/null @@ -1,16 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bxl7n4tkf1mvd"] - -[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_s0hq0"] - -[resource] -script = ExtResource("1_s0hq0") -name = &"physics_process" -description = "Attached blocks will be executed during the \"physics\" processing step of the main loop" -category = "Lifecycle" -type = 1 -variant_type = 0 -display_template = "On Physics Process" -code_template = "func _physics_process(delta):" -defaults = {} -signal_name = "" -scope = "" diff --git a/addons/block_code/blocks/lifecycle/process.tres b/addons/block_code/blocks/lifecycle/process.tres index 7a43cea..67f1a8e 100644 --- a/addons/block_code/blocks/lifecycle/process.tres +++ b/addons/block_code/blocks/lifecycle/process.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_pmina") name = &"process" +target_node_class = "" description = "Attached blocks will be executed during the processing step of the main loop" category = "Lifecycle" type = 1 variant_type = 0 -display_template = "On Process" +display_template = "every frame" code_template = "func _process(delta):" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/lifecycle/queue_free.tres b/addons/block_code/blocks/lifecycle/queue_free.tres index 2c65bdf..6dbe810 100644 --- a/addons/block_code/blocks/lifecycle/queue_free.tres +++ b/addons/block_code/blocks/lifecycle/queue_free.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_75fle") name = &"queue_free" +target_node_class = "" +description = "Queues this node to be deleted at the end of the current frame" +category = "Lifecycle" type = 2 variant_type = 0 -display_template = "Queue Free" +display_template = "remove" code_template = "queue_free()" -description = "Queues this node to be deleted at the end of the current frame" -category = "Lifecycle" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/lifecycle/ready.tres b/addons/block_code/blocks/lifecycle/ready.tres index 664fba2..55a2cf2 100644 --- a/addons/block_code/blocks/lifecycle/ready.tres +++ b/addons/block_code/blocks/lifecycle/ready.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_vk0xk") name = &"ready" +target_node_class = "" description = "Attached blocks will be executed once when the node is \"ready\"" category = "Lifecycle" type = 1 variant_type = 0 -display_template = "On Ready" +display_template = "when starting" code_template = "func _ready():" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/log/breakpoint.tres b/addons/block_code/blocks/log/breakpoint.tres new file mode 100644 index 0000000..d0a8a85 --- /dev/null +++ b/addons/block_code/blocks/log/breakpoint.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bddy0d4xdv20c"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_xva04"] + +[resource] +script = ExtResource("1_xva04") +name = &"breakpoint" +target_node_class = "" +description = "Pause execution and show the current line of code in the debugger." +category = "Log" +type = 2 +variant_type = 0 +display_template = "breakpoint" +code_template = "breakpoint" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/log/print.tres b/addons/block_code/blocks/log/print.tres index 128c491..bc6c82f 100644 --- a/addons/block_code/blocks/log/print.tres +++ b/addons/block_code/blocks/log/print.tres @@ -10,7 +10,7 @@ description = "Print the text to output" category = "Log" type = 2 variant_type = 0 -display_template = "Print {text: STRING}" +display_template = "log text {text: STRING}" code_template = "print({text})" defaults = { "text": "Hello" diff --git a/addons/block_code/blocks/logic/compare.tres b/addons/block_code/blocks/logic/compare.tres index 8e124cf..d4b4dc4 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"] @@ -16,8 +16,8 @@ description = "" category = "Logic | Comparison" type = 3 variant_type = 1 -display_template = "{float1: FLOAT} {op: OPTION} {float2: FLOAT}" -code_template = "{float1} {op} {float2}" +display_template = "{float1: FLOAT} {op: NIL} {float2: FLOAT}" +code_template = "{float1} {{op}} {float2}" defaults = { "float1": 1.0, "float2": 1.0, diff --git a/addons/block_code/blocks/logic/else.tres b/addons/block_code/blocks/logic/else.tres index 4877389..3d55ede 100644 --- a/addons/block_code/blocks/logic/else.tres +++ b/addons/block_code/blocks/logic/else.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_x816c") name = &"else" +target_node_class = "" +description = "" +category = "Logic | Conditionals" type = 4 variant_type = 0 -display_template = "Else" +display_template = "else" code_template = "else:" -description = "" -category = "Logic | Conditionals" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/else_if.tres b/addons/block_code/blocks/logic/else_if.tres index d3d4dae..becb43c 100644 --- a/addons/block_code/blocks/logic/else_if.tres +++ b/addons/block_code/blocks/logic/else_if.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_kgjks") name = &"else_if" +target_node_class = "" +description = "" +category = "Logic | Conditionals" type = 4 variant_type = 0 -display_template = "Else if {condition: BOOL}" +display_template = "else if {condition: BOOL}" code_template = "elif {condition}:" -description = "" -category = "Logic | Conditionals" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/if.tres b/addons/block_code/blocks/logic/if.tres index 5ba37c3..a44dd6c 100644 --- a/addons/block_code/blocks/logic/if.tres +++ b/addons/block_code/blocks/logic/if.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_c6ly3") name = &"if" +target_node_class = "" +description = "" +category = "Logic | Conditionals" type = 4 variant_type = 0 -display_template = "If {condition: BOOL}" +display_template = "if {condition: BOOL}" code_template = "if {condition}:" -description = "" -category = "Logic | Conditionals" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/not.tres b/addons/block_code/blocks/logic/not.tres index 5839cab..7be70ba 100644 --- a/addons/block_code/blocks/logic/not.tres +++ b/addons/block_code/blocks/logic/not.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_6igv6") name = &"not" +target_node_class = "" description = "" category = "Logic | Boolean" type = 3 variant_type = 1 -display_template = "Not {bool: BOOL}" +display_template = "not {bool: BOOL}" code_template = "not {bool}" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/loops/await_scene_ready.tres b/addons/block_code/blocks/loops/await_scene_ready.tres index 18f7e08..e418f92 100644 --- a/addons/block_code/blocks/loops/await_scene_ready.tres +++ b/addons/block_code/blocks/loops/await_scene_ready.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_qy2t4") name = &"await_scene_ready" +target_node_class = "" description = "" category = "Loops" type = 2 variant_type = 0 -display_template = "Await scene ready" +display_template = "wait for the scene to be ready" code_template = "if not get_tree().root.is_node_ready(): await get_tree().root.ready" defaults = {} diff --git a/addons/block_code/blocks/loops/break.tres b/addons/block_code/blocks/loops/break.tres index afcbb27..5ebf481 100644 --- a/addons/block_code/blocks/loops/break.tres +++ b/addons/block_code/blocks/loops/break.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_4rhsl") name = &"break" +target_node_class = "" +description = "" +category = "Loops" type = 2 variant_type = 0 -display_template = "Break" +display_template = "break" code_template = "break" -description = "" -category = "Loops" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/loops/continue.tres b/addons/block_code/blocks/loops/continue.tres index a269783..ee8d5af 100644 --- a/addons/block_code/blocks/loops/continue.tres +++ b/addons/block_code/blocks/loops/continue.tres @@ -5,11 +5,13 @@ [resource] script = ExtResource("1_junev") name = &"continue" +target_node_class = "" +description = "" +category = "Loops" type = 2 variant_type = 0 -display_template = "Continue" +display_template = "continue" code_template = "continue" -description = "" -category = "Loops" defaults = {} signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/loops/for.tres b/addons/block_code/blocks/loops/for.tres index 1505118..656f67d 100644 --- a/addons/block_code/blocks/loops/for.tres +++ b/addons/block_code/blocks/loops/for.tres @@ -5,11 +5,12 @@ [resource] script = ExtResource("1_u01bi") name = &"for" +target_node_class = "" description = "Run the connected blocks [i]number[/i] times" category = "Loops" type = 4 variant_type = 0 -display_template = "Repeat {number: INT}" +display_template = "repeat {number: INT}" code_template = "for __i in {number}:" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/loops/while.tres b/addons/block_code/blocks/loops/while.tres index 91e2d4f..8b68cbf 100644 --- a/addons/block_code/blocks/loops/while.tres +++ b/addons/block_code/blocks/loops/while.tres @@ -5,13 +5,14 @@ [resource] script = ExtResource("1_fxxh0") name = &"while" +target_node_class = "" description = "Run the connected blocks as long as [i]condition[/i] is true. Hint: snap a [b]Comparison[/b] block into the condition." category = "Loops" type = 4 variant_type = 0 -display_template = "While {condition: BOOL}" +display_template = "while {condition: BOOL}" code_template = "while {condition}:" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/math/randf_range.tres b/addons/block_code/blocks/math/randf_range.tres index ea47e98..a25e6b5 100644 --- a/addons/block_code/blocks/math/randf_range.tres +++ b/addons/block_code/blocks/math/randf_range.tres @@ -10,7 +10,7 @@ description = "Generate a random floating point number between [i]from[/i] and [ category = "Math" type = 3 variant_type = 3 -display_template = "Random floating point number between {from: FLOAT} and {to: FLOAT}" +display_template = "random floating point number between {from: FLOAT} and {to: FLOAT}" code_template = "randf_range({from}, {to})" defaults = { "from": -1.0, diff --git a/addons/block_code/blocks/math/randi_range.tres b/addons/block_code/blocks/math/randi_range.tres index 6636b4d..f9c8f96 100644 --- a/addons/block_code/blocks/math/randi_range.tres +++ b/addons/block_code/blocks/math/randi_range.tres @@ -10,7 +10,7 @@ description = "Generate a random signed 32-bits integer number between [i]from[/ category = "Math" type = 3 variant_type = 2 -display_template = "Random integer number between {from: INT} and {to: INT}" +display_template = "random integer number between {from: INT} and {to: INT}" code_template = "randi_range({from}, {to})" defaults = { "from": 0, diff --git a/addons/block_code/blocks/math/vector_from_angle.tres b/addons/block_code/blocks/math/vector_from_angle.tres new file mode 100644 index 0000000..fce91df --- /dev/null +++ b/addons/block_code/blocks/math/vector_from_angle.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c7a6wnxegkfd5"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_1p1ve"] + +[resource] +script = ExtResource("1_1p1ve") +name = &"from_angle" +target_node_class = "" +description = "Creates a unit Vector2 rotated to the given angle in radians." +category = "Math" +type = 3 +variant_type = 5 +display_template = "vector from {angle: FLOAT}" +code_template = "Vector2.from_angle({angle})" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/vector_multiply.tres b/addons/block_code/blocks/math/vector_multiply.tres new file mode 100644 index 0000000..8c82607 --- /dev/null +++ b/addons/block_code/blocks/math/vector_multiply.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bff7cwmpisihj"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_52jwf"] + +[resource] +script = ExtResource("1_52jwf") +name = &"vector_multiply" +target_node_class = "" +description = "Multiplies a vector with a number. Use this, for example, to get a point some distance away along an angle." +category = "Math" +type = 3 +variant_type = 5 +display_template = "multiply {vector: VECTOR2} by {number: FLOAT}" +code_template = "{vector} * {number}" +defaults = { +"number": 1.0, +"vector": Vector2(1, 1) +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/physics/characterbody2d_move_and_slide.tres b/addons/block_code/blocks/physics/characterbody2d_move_and_slide.tres index 89448e2..1e6a212 100644 --- a/addons/block_code/blocks/physics/characterbody2d_move_and_slide.tres +++ b/addons/block_code/blocks/physics/characterbody2d_move_and_slide.tres @@ -10,7 +10,7 @@ description = "" category = "Physics | Velocity" type = 2 variant_type = 0 -display_template = "Move and slide" +display_template = "move and slide" code_template = "move_and_slide()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/sounds/audiostreamplayer_play.tres b/addons/block_code/blocks/sounds/audiostreamplayer_play.tres index 1e57483..92fab52 100644 --- a/addons/block_code/blocks/sounds/audiostreamplayer_play.tres +++ b/addons/block_code/blocks/sounds/audiostreamplayer_play.tres @@ -10,7 +10,7 @@ description = "Play the audio stream" category = "Sounds" type = 2 variant_type = 0 -display_template = "Play" +display_template = "play" code_template = "play()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres b/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres index d95f6a3..c0ea7c8 100644 --- a/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres +++ b/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres @@ -10,7 +10,7 @@ description = "Stop the audio stream" category = "Sounds" type = 2 variant_type = 0 -display_template = "Stop" +display_template = "stop" code_template = "stop()" defaults = {} signal_name = "" diff --git a/addons/block_code/blocks/sounds/load_sound.tres b/addons/block_code/blocks/sounds/load_sound.tres index 918ba27..8f03666 100644 --- a/addons/block_code/blocks/sounds/load_sound.tres +++ b/addons/block_code/blocks/sounds/load_sound.tres @@ -10,7 +10,7 @@ description = "Load a resource file as the audio stream" category = "Sounds" type = 2 variant_type = 0 -display_template = "Load file {file_path: STRING} as sound {name: STRING}" +display_template = "load file {file_path: STRING} as sound {name: STRING}" code_template = "var __sound = AudioStreamPlayer.new() __sound.name = {name} __sound.set_stream(load({file_path})) diff --git a/addons/block_code/blocks/sounds/pause_continue_sound.tres b/addons/block_code/blocks/sounds/pause_continue_sound.tres index 074e035..17487bb 100644 --- a/addons/block_code/blocks/sounds/pause_continue_sound.tres +++ b/addons/block_code/blocks/sounds/pause_continue_sound.tres @@ -1,12 +1,12 @@ [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"] script = ExtResource("1_ilhdq") selected = 0 -items = ["Pause", "Continue"] +items = ["pause", "continue"] [resource] script = ExtResource("1_q04gm") @@ -16,9 +16,9 @@ description = "Pause/Continue the audio stream" category = "Sounds" type = 2 variant_type = 0 -display_template = "{pause: OPTION} the sound {name: STRING}" +display_template = "{pause: NIL} the sound {name: STRING}" code_template = "var __sound_node = get_node({name}) -if \"{pause}\" == \"pause\": +if {pause} == \"pause\": __sound_node.stream_paused = true else: __sound_node.stream_paused = false diff --git a/addons/block_code/blocks/sounds/play_sound.tres b/addons/block_code/blocks/sounds/play_sound.tres index ac910d9..7284076 100644 --- a/addons/block_code/blocks/sounds/play_sound.tres +++ b/addons/block_code/blocks/sounds/play_sound.tres @@ -10,7 +10,7 @@ description = "Play the audio stream with volume and pitch" category = "Sounds" type = 2 variant_type = 0 -display_template = "Play the sound {name: STRING} with Volume dB {db: FLOAT} and Pitch Scale {pitch: FLOAT}" +display_template = "play the sound {name: STRING} with volume {db: FLOAT} dB and pitch scale {pitch: FLOAT}" code_template = "var __sound_node = get_node({name}) __sound_node.volume_db = {db} __sound_node.pitch_scale = {pitch} diff --git a/addons/block_code/blocks/sounds/stop_sound.tres b/addons/block_code/blocks/sounds/stop_sound.tres index 8233b73..91532df 100644 --- a/addons/block_code/blocks/sounds/stop_sound.tres +++ b/addons/block_code/blocks/sounds/stop_sound.tres @@ -10,7 +10,7 @@ description = "Stop the audio stream" category = "Sounds" type = 2 variant_type = 0 -display_template = "Stop the sound {name: STRING}" +display_template = "stop the sound {name: STRING}" code_template = "var __sound_node = get_node({name}) __sound_node.stop() " diff --git a/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres b/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres index 8059830..1003993 100644 --- a/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres +++ b/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres @@ -10,7 +10,7 @@ description = "" category = "Transform | Position" type = 2 variant_type = 0 -display_template = "Set Physics Position {position: VECTOR2}" +display_template = "set physics position {position: VECTOR2}" code_template = "PhysicsServer2D.body_set_state( get_rid(), PhysicsServer2D.BODY_STATE_TRANSFORM, diff --git a/addons/block_code/blocks/variables/vector2.tres b/addons/block_code/blocks/variables/vector2.tres index cbdcf2e..c11e43b 100644 --- a/addons/block_code/blocks/variables/vector2.tres +++ b/addons/block_code/blocks/variables/vector2.tres @@ -7,10 +7,10 @@ script = ExtResource("1_ilw3v") name = &"vector2" target_node_class = "" description = "" -category = "Variables" +category = "Math" type = 3 variant_type = 5 -display_template = "Vector2 x: {x: FLOAT} y: {y: FLOAT}" +display_template = "vector2 x: {x: FLOAT} y: {y: FLOAT}" code_template = "Vector2({x}, {y})" defaults = { "x": 0.0, 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 0000000..aafb6ca --- /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 0000000..a90657e --- /dev/null +++ b/addons/block_code/code_generation/block_ast.gd @@ -0,0 +1,139 @@ +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 = BlockAST.format_code_template(data.code_template, arguments) + 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 = BlockAST.format_code_template(data.code_template, arguments) + 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 format_code_template(code_template: String, arguments: Dictionary) -> String: + for argument_name in arguments: + # Use parentheses to be safe + var argument_value: Variant = arguments[argument_name] + var code_string: String + var raw_string: String + + if argument_value is OptionData: + # Temporary hack: previously, the value was stored as an OptionData + # object with a list of items and a "selected" property. If we are + # using an older block script where that is the case, convert the + # value to the value of its selected item. + # See also, ParameterInput._update_option_input. + argument_value = argument_value.items[argument_value.selected] + + if argument_value is ASTValueNode: + code_string = argument_value.get_code() + raw_string = code_string + else: + code_string = BlockAST.raw_input_to_code_string(argument_value) + raw_string = str(argument_value) + + code_template = code_template.replace("{{%s}}" % argument_name, raw_string) + code_template = code_template.replace("{%s}" % argument_name, code_string) + + return code_template + + +static func raw_input_to_code_string(input) -> String: + match typeof(input): + TYPE_STRING: + return "'%s'" % input.c_escape() + TYPE_VECTOR2: + return "Vector2%s" % str(input) + TYPE_COLOR: + return "Color%s" % str(input) + _: + return "%s" % input + + return "" diff --git a/addons/block_code/code_generation/block_definition.gd b/addons/block_code/code_generation/block_definition.gd index 9f3bbb9..26ac9e7 100644 --- a/addons/block_code/code_generation/block_definition.gd +++ b/addons/block_code/code_generation/block_definition.gd @@ -1,9 +1,10 @@ @tool - extends Resource const Types = preload("res://addons/block_code/types/types.gd") +const FORMAT_STRING_PATTERN = "\\[(?[^\\]]+)\\]|\\{(?[^}]+)\\}|(?