diff --git a/.github/ISSUE_TEMPLATE/public.yml b/.github/ISSUE_TEMPLATE/public.yml index fd95efb4..4e749039 100644 --- a/.github/ISSUE_TEMPLATE/public.yml +++ b/.github/ISSUE_TEMPLATE/public.yml @@ -25,12 +25,26 @@ body: validations: required: true + - type: input + id: v + attributes: + label: Block Coding Plugin Version + description: What version of the plugin are you using? You can check in **Project** → **Project Settings** → **Plugins**. + placeholder: v0.0.0 + + - type: input + id: godot + attributes: + label: Godot Engine Version + description: What version of Godot are you using? You can copy it by clicking the version in **Help** → **About Godot…**. + placeholder: v0.0.0.stable + - type: input id: os attributes: label: Operating System description: What specific OS and version are you seeing the problem on? - placeholder: e.g. Windows 11, Fedora Silverblue 39, Ubuntu 22.04, etc. + placeholder: Windows 11, Fedora Silverblue 39, Ubuntu 22.04, etc. - type: textarea id: logs diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 5ed986b8..f8057221 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -25,6 +25,9 @@ jobs: tests: name: Tests + strategy: + matrix: + godot-version: [4.2.2, 4.3.0] runs-on: ubuntu-latest steps: - name: Checkout @@ -32,7 +35,7 @@ jobs: - name: Setup Godot uses: chickensoft-games/setup-godot@v2.1.1 with: - version: 4.2.2 + version: ${{ matrix.godot-version }} use-dotnet: false - name: Initialize Godot run: | diff --git a/.github/workflows/godot-asset-library.yaml b/.github/workflows/godot-asset-library.yaml index 69c291b7..e57363a1 100644 --- a/.github/workflows/godot-asset-library.yaml +++ b/.github/workflows/godot-asset-library.yaml @@ -13,7 +13,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Push to Godot Asset Library - uses: wjt/godot-asset-lib-action@9cce3792b504bec69eb06b852b008d095e372f56 + uses: deep-entertainment/godot-asset-lib-action@v0.6.0 with: username: ${{ secrets.GODOT_ASSET_LIBRARY_USERNAME }} password: ${{ secrets.GODOT_ASSET_LIBRARY_PASSWORD }} diff --git a/addons/block_code/README.md b/addons/block_code/README.md index 9fdf4771..d349988f 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_node/block_code.gd b/addons/block_code/block_code_node/block_code.gd index eaafb088..97f81e7d 100644 --- a/addons/block_code/block_code_node/block_code.gd +++ b/addons/block_code/block_code_node/block_code.gd @@ -3,7 +3,7 @@ class_name BlockCode extends Node -@export var block_script: BlockScriptData = null +@export var block_script: BlockScriptSerialization = null func _ready(): @@ -25,10 +25,10 @@ func _enter_tree(): # Create script if block_script == null: - var new_bsd: BlockScriptData = load("res://addons/block_code/ui/bsd_templates/default_bsd.tres").duplicate(true) - new_bsd.script_inherits = _get_custom_or_native_class(get_parent()) - new_bsd.generated_script = new_bsd.generated_script.replace("INHERIT_DEFAULT", new_bsd.script_inherits) - block_script = new_bsd + var new_block_script: BlockScriptSerialization = load("res://addons/block_code/serialization/default_block_script.tres").duplicate(true) + new_block_script.script_inherits = _get_custom_or_native_class(get_parent()) + new_block_script.generated_script = new_block_script.generated_script.replace("INHERIT_DEFAULT", new_block_script.script_inherits) + block_script = new_block_script func _update_parent_script(): diff --git a/addons/block_code/block_code_plugin.gd b/addons/block_code/block_code_plugin.gd index 8f297cf0..96b9b7ab 100644 --- a/addons/block_code/block_code_plugin.gd +++ b/addons/block_code/block_code_plugin.gd @@ -1,8 +1,11 @@ @tool -class_name BlockCodePlugin extends EditorPlugin -const MainPanel := preload("res://addons/block_code/ui/main_panel.tscn") +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") +const ScriptWindow := preload("res://addons/block_code/ui/script_window/script_window.tscn") + static var main_panel: MainPanel static var block_code_button: Button @@ -17,32 +20,13 @@ var _selected_block_code: BlockCode var old_feature_profile: String = "" const DISABLED_CLASSES := [ - "BlockScriptData", - "DragManager", - "InstructionTree", - "Types", "Block", "ControlBlock", "ParameterBlock", "StatementBlock", - "DragDropArea", "SnapPoint", - "SerializedBlockTreeNodeArray", - "SerializedBlockTreeNode", - "SerializedBlock", - "PackedSceneTreeNodeArray", - "PackedSceneTreeNode", - "BlockCanvas", + "BlockScriptSerialization", "CategoryFactory", - "BlockCategoryDisplay", - "BlockCategory", - "Picker", - "TitleBar", - "MainPanel", - "BlockCodePlugin", - "BlockCategoryButton", - "CreateVariableButton", - "VariableCategoryDisplay" ] @@ -52,7 +36,8 @@ func _enter_tree(): editor_inspector = EditorInterface.get_inspector() editor_selection = EditorInterface.get_selection() - main_panel = MainPanel.instantiate() + main_panel = MainPanelScene.instantiate() + main_panel.script_window_requested.connect(script_window_requested) main_panel.undo_redo = get_undo_redo() block_code_button = add_control_to_bottom_panel(main_panel, _get_plugin_name()) block_inspector_plugin = BlockInspectorPlugin.new() @@ -75,6 +60,18 @@ func _enter_tree(): EditorInterface.set_current_feature_profile("block_code") +func script_window_requested(script: String): + var script_window = ScriptWindow.instantiate() + script_window.script_content = script + + EditorInterface.get_base_control().add_child(script_window) + + await script_window.close_requested + + script_window.queue_free() + script_window = null + + func _exit_tree(): remove_inspector_plugin(block_inspector_plugin) @@ -97,16 +94,10 @@ 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 @@ -147,7 +138,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) @@ -155,7 +146,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/block_script_data/block_script_data.gd b/addons/block_code/block_script_data/block_script_data.gd deleted file mode 100644 index 3502ca73..00000000 --- a/addons/block_code/block_script_data/block_script_data.gd +++ /dev/null @@ -1,16 +0,0 @@ -class_name BlockScriptData -extends Resource - -@export var script_inherits: String -@export var block_trees: SerializedBlockTreeNodeArray -@export var variables: Array[VariableResource] -@export var generated_script: String -@export var version: int - - -func _init(p_script_inherits: String = "", p_block_trees: SerializedBlockTreeNodeArray = null, p_variables: Array[VariableResource] = [], p_generated_script: String = "", p_version = 0): - script_inherits = p_script_inherits - block_trees = p_block_trees - generated_script = p_generated_script - variables = p_variables - version = p_version diff --git a/addons/block_code/blocks/communication/add_node_to_group.tres b/addons/block_code/blocks/communication/add_node_to_group.tres new file mode 100644 index 00000000..b4ea7d66 --- /dev/null +++ b/addons/block_code/blocks/communication/add_node_to_group.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bpvefei72nh3a"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_5qal7"] + +[resource] +script = ExtResource("1_5qal7") +name = &"add_node_to_group" +description = "Add the node into the group" +category = "Communication | Groups" +type = 2 +variant_type = 0 +display_template = "Add {node: OBJECT} to group {group: STRING}" +code_template = "{node}.add_to_group({group})" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/communication/add_to_group.tres b/addons/block_code/blocks/communication/add_to_group.tres new file mode 100644 index 00000000..d4ccb2bc --- /dev/null +++ b/addons/block_code/blocks/communication/add_to_group.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bvrmau8atjx1x"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_bcm71"] + +[resource] +script = ExtResource("1_bcm71") +name = &"add_to_group" +target_node_class = "" +description = "Add this node into the group" +category = "Communication | Groups" +type = 2 +variant_type = 0 +display_template = "Add to group {group: STRING}" +code_template = "add_to_group({group})" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/communication/area2d_on_entered.tres b/addons/block_code/blocks/communication/area2d_on_entered.tres new file mode 100644 index 00000000..370331e6 --- /dev/null +++ b/addons/block_code/blocks/communication/area2d_on_entered.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://de4k7t7uqws1j"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_xotf5"] + +[resource] +script = ExtResource("1_xotf5") +name = &"area2d_on_entered" +target_node_class = "Area2D" +description = "" +category = "Communication | Methods" +type = 1 +variant_type = 0 +display_template = "On [body: OBJECT] entered" +code_template = "func _on_body_entered(body: Node2D): +" +defaults = {} +signal_name = "body_entered" +scope = "" diff --git a/addons/block_code/blocks/communication/area2d_on_exited.tres b/addons/block_code/blocks/communication/area2d_on_exited.tres new file mode 100644 index 00000000..ba3d0e53 --- /dev/null +++ b/addons/block_code/blocks/communication/area2d_on_exited.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b36nq4mau6lu6"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_21qth"] + +[resource] +script = ExtResource("1_21qth") +name = &"area2d_on_exited" +target_node_class = "Area2D" +description = "" +category = "Communication | Methods" +type = 1 +variant_type = 0 +display_template = "On [body: OBJECT] exited" +code_template = "func _on_body_exited(body: Node2D): +" +defaults = {} +signal_name = "body_exited" +scope = "" diff --git a/addons/block_code/blocks/communication/call_method_group.tres b/addons/block_code/blocks/communication/call_method_group.tres new file mode 100644 index 00000000..5df2fe2f --- /dev/null +++ b/addons/block_code/blocks/communication/call_method_group.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c15vtdfihdxb8"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_mlm68"] + +[resource] +script = ExtResource("1_mlm68") +name = &"call_method_group" +type = 2 +variant_type = 0 +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 = {} +signal_name = "" diff --git a/addons/block_code/blocks/communication/call_method_node.tres b/addons/block_code/blocks/communication/call_method_node.tres new file mode 100644 index 00000000..ba78855c --- /dev/null +++ b/addons/block_code/blocks/communication/call_method_node.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c04j5flmimjvf"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_pg363"] + +[resource] +script = ExtResource("1_pg363") +name = &"call_method_node" +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}" +code_template = "{node}.call({method_name})" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/communication/define_method.tres b/addons/block_code/blocks/communication/define_method.tres new file mode 100644 index 00000000..f3bd8926 --- /dev/null +++ b/addons/block_code/blocks/communication/define_method.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://7r2b2griss3i"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_6e473"] + +[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: STRING_NAME}" +code_template = "func {method_name}():" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/communication/is_in_group.tres b/addons/block_code/blocks/communication/is_in_group.tres new file mode 100644 index 00000000..0b38920a --- /dev/null +++ b/addons/block_code/blocks/communication/is_in_group.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://q4cnstftvsiu"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_tjyq5"] + +[resource] +script = ExtResource("1_tjyq5") +name = &"is_in_group" +description = "Is this node in the group" +category = "Communication | Groups" +type = 3 +variant_type = 1 +display_template = "Is in group {group: STRING}" +code_template = "is_in_group({group})" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/communication/is_node_in_group.tres b/addons/block_code/blocks/communication/is_node_in_group.tres new file mode 100644 index 00000000..3639b336 --- /dev/null +++ b/addons/block_code/blocks/communication/is_node_in_group.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bbtdxeey74x67"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_5krrs"] + +[resource] +script = ExtResource("1_5krrs") +name = &"is_node_in_group" +description = "Is the node in the group" +category = "Communication | Groups" +type = 3 +variant_type = 1 +display_template = "Is {node: OBJECT} in group {group: STRING}" +code_template = "{node}.is_in_group({group})" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/communication/remove_from_group.tres b/addons/block_code/blocks/communication/remove_from_group.tres new file mode 100644 index 00000000..7e20f653 --- /dev/null +++ b/addons/block_code/blocks/communication/remove_from_group.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dgenw5wyqorvq"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_cdwef"] + +[resource] +script = ExtResource("1_cdwef") +name = &"remove_from_group" +type = 2 +variant_type = 0 +display_template = "Remove from group {group: STRING}" +code_template = "remove_from_group({group})" +description = "Remove this node from the group" +category = "Communication | Groups" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/communication/remove_node_from_group.tres b/addons/block_code/blocks/communication/remove_node_from_group.tres new file mode 100644 index 00000000..9b132bed --- /dev/null +++ b/addons/block_code/blocks/communication/remove_node_from_group.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b2dwk77hnri8y"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_pec24"] + +[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})" +description = "Remove the node from the group" +category = "Communication | Groups" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres b/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres new file mode 100644 index 00000000..5be0ca5c --- /dev/null +++ b/addons/block_code/blocks/communication/rigidbody2d_on_entered.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dl1xd1jit2mlp"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_v2421"] + +[resource] +script = ExtResource("1_v2421") +name = &"rigidbody2d_on_entered" +target_node_class = "RigidBody2D" +description = "" +category = "Communication | Methods" +type = 1 +variant_type = 0 +display_template = "On [body: OBJECT] entered" +code_template = "func _on_body_entered(body: Node2D): +" +defaults = {} +signal_name = "body_entered" +scope = "" diff --git a/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres b/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres new file mode 100644 index 00000000..f16eb085 --- /dev/null +++ b/addons/block_code/blocks/communication/rigidbody2d_on_exited.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c15ymi1kxb570"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_sahiu"] + +[resource] +script = ExtResource("1_sahiu") +name = &"rigidbody2d_on_exited" +target_node_class = "RigidBody2D" +description = "" +category = "Communication | Methods" +type = 1 +variant_type = 0 +display_template = "On [body: OBJECT] exited" +code_template = "func _on_body_exited(body: Node2D): +" +defaults = {} +signal_name = "body_exited" +scope = "" diff --git a/addons/block_code/blocks/graphics/animationplayer_is_playing.tres b/addons/block_code/blocks/graphics/animationplayer_is_playing.tres new file mode 100644 index 00000000..1e3f6578 --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_is_playing.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://mg4y3o0rsqd5"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_xr50b"] + +[resource] +script = ExtResource("1_xr50b") +name = &"animationplayer_is_playing" +target_node_class = "AnimationPlayer" +description = "Check if an animation is currently playing." +category = "Graphics | Animation" +type = 3 +variant_type = 1 +display_template = "Is playing" +code_template = "is_playing()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/graphics/animationplayer_pause.tres b/addons/block_code/blocks/graphics/animationplayer_pause.tres new file mode 100644 index 00000000..15f80903 --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_pause.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://clopo7gmje5a"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_2enqv"] + +[resource] +script = ExtResource("1_2enqv") +name = &"animationplayer_pause" +target_node_class = "AnimationPlayer" +description = "Pause the currently playing animation." +category = "Graphics | Animation" +type = 2 +variant_type = 0 +display_template = "Pause" +code_template = "pause()" +defaults = {} +signal_name = "" +scope = "" 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 00000000..63c603a2 --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_play.gd @@ -0,0 +1,15 @@ +@tool +extends BlockExtension + +const OptionData = preload("res://addons/block_code/code_generation/option_data.gd") + + +func get_defaults_for_node(context_node: Node) -> 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)} diff --git a/addons/block_code/blocks/graphics/animationplayer_play.tres b/addons/block_code/blocks/graphics/animationplayer_play.tres new file mode 100644 index 00000000..750111de --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_play.tres @@ -0,0 +1,37 @@ +[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/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"] + +[resource] +script = ExtResource("1_emeuv") +name = &"animationplayer_play" +target_node_class = "AnimationPlayer" +description = "Play the animation." +category = "Graphics | Animation" +type = 2 +variant_type = 0 +display_template = "Play {animation: STRING} {direction: NIL}" +code_template = "if {direction} == \"ahead\": + 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 new file mode 100644 index 00000000..35369a87 --- /dev/null +++ b/addons/block_code/blocks/graphics/animationplayer_stop.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b4v00oxoxbfet"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_wp8gr"] + +[resource] +script = ExtResource("1_wp8gr") +name = &"animationplayer_stop" +target_node_class = "AnimationPlayer" +description = "Stop the currently playing animation." +category = "Graphics | Animation" +type = 2 +variant_type = 0 +display_template = "Stop" +code_template = "stop()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/graphics/viewport_center.tres b/addons/block_code/blocks/graphics/viewport_center.tres new file mode 100644 index 00000000..e3b65041 --- /dev/null +++ b/addons/block_code/blocks/graphics/viewport_center.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://1536itmdu8yo"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rc1so"] + +[resource] +script = ExtResource("1_rc1so") +name = &"viewport_center" +description = "Coordinates of the middle of the viewable screen when playing." +category = "Graphics | Viewport" +type = 3 +variant_type = 5 +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 = "" +scope = "" diff --git a/addons/block_code/blocks/graphics/viewport_height.tres b/addons/block_code/blocks/graphics/viewport_height.tres new file mode 100644 index 00000000..50dcf640 --- /dev/null +++ b/addons/block_code/blocks/graphics/viewport_height.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bdm4yr68mdf4d"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_1debb"] + +[resource] +script = ExtResource("1_1debb") +name = &"viewport_height" +description = "How tall the viewable screen is when playing." +category = "Graphics | Viewport" +type = 3 +variant_type = 3 +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 = "" +scope = "" diff --git a/addons/block_code/blocks/graphics/viewport_width.tres b/addons/block_code/blocks/graphics/viewport_width.tres new file mode 100644 index 00000000..39ba35aa --- /dev/null +++ b/addons/block_code/blocks/graphics/viewport_width.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bt78ajp56ga24"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_ll4rh"] + +[resource] +script = ExtResource("1_ll4rh") +name = &"viewport_width" +description = "How wide the viewable screen is when playing." +category = "Graphics | Viewport" +type = 3 +variant_type = 3 +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 = "" +scope = "" 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 00000000..f6f3b807 --- /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 new file mode 100644 index 00000000..688ff5af --- /dev/null +++ b/addons/block_code/blocks/input/characterbody2d_move.tres @@ -0,0 +1,31 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cu3ru61vg6bx5"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_btxic"] + +[resource] +script = ExtResource("1_btxic") +name = &"characterbody2d_move" +target_node_class = "CharacterBody2D" +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}" +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}))) +dir.y += float(Input.is_key_pressed(OS.find_keycode_from_string({down}))) +dir.y -= float(Input.is_key_pressed(OS.find_keycode_from_string({up}))) +dir = dir.normalized() +velocity = dir*{speed} +move_and_slide() +" +defaults = { +"down": "S", +"left": "A", +"right": "D", +"speed": Vector2(100, 100), +"up": "W" +} +signal_name = "" +scope = "" 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 00000000..15ad821d --- /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_for_node(_context_node: Node) -> 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 00000000..636a3f90 --- /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 = "Is action {action_name: STRING_NAME} {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 new file mode 100644 index 00000000..a797f4b6 --- /dev/null +++ b/addons/block_code/blocks/lifecycle/physics_process.tres @@ -0,0 +1,16 @@ +[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 new file mode 100644 index 00000000..7a43cea3 --- /dev/null +++ b/addons/block_code/blocks/lifecycle/process.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://chioedvp50013"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_pmina"] + +[resource] +script = ExtResource("1_pmina") +name = &"process" +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" +code_template = "func _process(delta):" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/lifecycle/queue_free.tres b/addons/block_code/blocks/lifecycle/queue_free.tres new file mode 100644 index 00000000..2c65bdf4 --- /dev/null +++ b/addons/block_code/blocks/lifecycle/queue_free.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://4hj5b3xaiuy8"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_75fle"] + +[resource] +script = ExtResource("1_75fle") +name = &"queue_free" +type = 2 +variant_type = 0 +display_template = "Queue Free" +code_template = "queue_free()" +description = "Queues this node to be deleted at the end of the current frame" +category = "Lifecycle" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/lifecycle/ready.tres b/addons/block_code/blocks/lifecycle/ready.tres new file mode 100644 index 00000000..664fba2f --- /dev/null +++ b/addons/block_code/blocks/lifecycle/ready.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dgwfoepoejlom"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_vk0xk"] + +[resource] +script = ExtResource("1_vk0xk") +name = &"ready" +description = "Attached blocks will be executed once when the node is \"ready\"" +category = "Lifecycle" +type = 1 +variant_type = 0 +display_template = "On Ready" +code_template = "func _ready():" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/log/breakpoint.tres b/addons/block_code/blocks/log/breakpoint.tres new file mode 100644 index 00000000..a7020222 --- /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/concat.tres b/addons/block_code/blocks/log/concat.tres new file mode 100644 index 00000000..744bd5a7 --- /dev/null +++ b/addons/block_code/blocks/log/concat.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cb6ux0amdhhlw"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_lstbo"] + +[resource] +script = ExtResource("1_lstbo") +name = &"concat" +description = "" +category = "Log" +type = 3 +variant_type = 4 +display_template = "{string1: STRING} + {string2: STRING}" +code_template = "{string1} + {string2}" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/log/print.tres b/addons/block_code/blocks/log/print.tres new file mode 100644 index 00000000..128c491a --- /dev/null +++ b/addons/block_code/blocks/log/print.tres @@ -0,0 +1,19 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://barxsapb8tl0r"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_0lih2"] + +[resource] +script = ExtResource("1_0lih2") +name = &"print" +target_node_class = "" +description = "Print the text to output" +category = "Log" +type = 2 +variant_type = 0 +display_template = "Print {text: STRING}" +code_template = "print({text})" +defaults = { +"text": "Hello" +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/and.tres b/addons/block_code/blocks/logic/and.tres new file mode 100644 index 00000000..c333138f --- /dev/null +++ b/addons/block_code/blocks/logic/and.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://d0g11cp3ff81i"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="2_lxlcw"] + +[resource] +script = ExtResource("2_lxlcw") +name = &"and" +description = "" +category = "Logic | Boolean" +type = 3 +variant_type = 1 +display_template = "{bool1: BOOL} and {bool2: BOOL}" +code_template = "{bool1} and {bool2}" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/compare.tres b/addons/block_code/blocks/logic/compare.tres new file mode 100644 index 00000000..d4b4dc4f --- /dev/null +++ b/addons/block_code/blocks/logic/compare.tres @@ -0,0 +1,27 @@ +[gd_resource type="Resource" load_steps=4 format=3 uid="uid://pr5wnn3ltkbo"] + +[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"] +script = ExtResource("1_hcv2h") +selected = 0 +items = ["==", ">", "<", ">=", "<=", "!="] + +[resource] +script = ExtResource("1_wp40r") +name = &"compare" +target_node_class = "" +description = "" +category = "Logic | Comparison" +type = 3 +variant_type = 1 +display_template = "{float1: FLOAT} {op: NIL} {float2: FLOAT}" +code_template = "{float1} {{op}} {float2}" +defaults = { +"float1": 1.0, +"float2": 1.0, +"op": SubResource("Resource_ie4sg") +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/else.tres b/addons/block_code/blocks/logic/else.tres new file mode 100644 index 00000000..4877389d --- /dev/null +++ b/addons/block_code/blocks/logic/else.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dpgx8j3veifgl"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_x816c"] + +[resource] +script = ExtResource("1_x816c") +name = &"else" +type = 4 +variant_type = 0 +display_template = "Else" +code_template = "else:" +description = "" +category = "Logic | Conditionals" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/logic/else_if.tres b/addons/block_code/blocks/logic/else_if.tres new file mode 100644 index 00000000..d3d4dae0 --- /dev/null +++ b/addons/block_code/blocks/logic/else_if.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://by53vmmn3wtny"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_kgjks"] + +[resource] +script = ExtResource("1_kgjks") +name = &"else_if" +type = 4 +variant_type = 0 +display_template = "Else if {condition: BOOL}" +code_template = "elif {condition}:" +description = "" +category = "Logic | Conditionals" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/logic/if.tres b/addons/block_code/blocks/logic/if.tres new file mode 100644 index 00000000..5ba37c37 --- /dev/null +++ b/addons/block_code/blocks/logic/if.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cxvoo3jassq8c"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_c6ly3"] + +[resource] +script = ExtResource("1_c6ly3") +name = &"if" +type = 4 +variant_type = 0 +display_template = "If {condition: BOOL}" +code_template = "if {condition}:" +description = "" +category = "Logic | Conditionals" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/logic/not.tres b/addons/block_code/blocks/logic/not.tres new file mode 100644 index 00000000..5839cabb --- /dev/null +++ b/addons/block_code/blocks/logic/not.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://d6asv53q6ok8"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_6igv6"] + +[resource] +script = ExtResource("1_6igv6") +name = &"not" +description = "" +category = "Logic | Boolean" +type = 3 +variant_type = 1 +display_template = "Not {bool: BOOL}" +code_template = "not {bool}" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/logic/or.tres b/addons/block_code/blocks/logic/or.tres new file mode 100644 index 00000000..c7b0f909 --- /dev/null +++ b/addons/block_code/blocks/logic/or.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cyu2tntoqf85m"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_oets3"] + +[resource] +script = ExtResource("1_oets3") +name = &"or" +description = "" +category = "Logic | Boolean" +type = 3 +variant_type = 1 +display_template = "{bool1: BOOL} or {bool2: BOOL}" +code_template = "{bool1} or {bool2}" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/loops/await_scene_ready.tres b/addons/block_code/blocks/loops/await_scene_ready.tres new file mode 100644 index 00000000..18f7e085 --- /dev/null +++ b/addons/block_code/blocks/loops/await_scene_ready.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bkdmiqavhqrph"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_qy2t4"] + +[resource] +script = ExtResource("1_qy2t4") +name = &"await_scene_ready" +description = "" +category = "Loops" +type = 2 +variant_type = 0 +display_template = "Await scene ready" +code_template = "if not get_tree().root.is_node_ready(): + await get_tree().root.ready" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/loops/break.tres b/addons/block_code/blocks/loops/break.tres new file mode 100644 index 00000000..afcbb27d --- /dev/null +++ b/addons/block_code/blocks/loops/break.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dwteydig4c6hi"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_4rhsl"] + +[resource] +script = ExtResource("1_4rhsl") +name = &"break" +type = 2 +variant_type = 0 +display_template = "Break" +code_template = "break" +description = "" +category = "Loops" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/loops/continue.tres b/addons/block_code/blocks/loops/continue.tres new file mode 100644 index 00000000..a2697831 --- /dev/null +++ b/addons/block_code/blocks/loops/continue.tres @@ -0,0 +1,15 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://srm0bee85n0d"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_junev"] + +[resource] +script = ExtResource("1_junev") +name = &"continue" +type = 2 +variant_type = 0 +display_template = "Continue" +code_template = "continue" +description = "" +category = "Loops" +defaults = {} +signal_name = "" diff --git a/addons/block_code/blocks/loops/for.tres b/addons/block_code/blocks/loops/for.tres new file mode 100644 index 00000000..1505118d --- /dev/null +++ b/addons/block_code/blocks/loops/for.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://0g4njflvemaa"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_u01bi"] + +[resource] +script = ExtResource("1_u01bi") +name = &"for" +description = "Run the connected blocks [i]number[/i] times" +category = "Loops" +type = 4 +variant_type = 0 +display_template = "Repeat {number: INT}" +code_template = "for __i in {number}:" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/loops/while.tres b/addons/block_code/blocks/loops/while.tres new file mode 100644 index 00000000..91e2d4fa --- /dev/null +++ b/addons/block_code/blocks/loops/while.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://ccnjk5s5qb2xe"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_fxxh0"] + +[resource] +script = ExtResource("1_fxxh0") +name = &"while" +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}" +code_template = "while {condition}:" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/add.tres b/addons/block_code/blocks/math/add.tres new file mode 100644 index 00000000..336e1afc --- /dev/null +++ b/addons/block_code/blocks/math/add.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://coy3o7q0x0y60"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rks7c"] + +[resource] +script = ExtResource("1_rks7c") +name = &"add" +target_node_class = "" +description = "" +category = "Math" +type = 3 +variant_type = 3 +display_template = "{a: FLOAT} + {b: FLOAT}" +code_template = "{a} + {b}" +defaults = { +"a": 1.0, +"b": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/cos.tres b/addons/block_code/blocks/math/cos.tres new file mode 100644 index 00000000..1f0bb4a8 --- /dev/null +++ b/addons/block_code/blocks/math/cos.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://c6g6ljp46lfrj"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_lxbvo"] + +[resource] +script = ExtResource("1_lxbvo") +name = &"cos" +description = "Calculate the cosine of [i]angle[/i]" +category = "Math" +type = 3 +variant_type = 3 +display_template = "cos {angle: FLOAT}" +code_template = "cos(deg_to_rad({angle}))" +defaults = { +"angle": 0.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/divide.tres b/addons/block_code/blocks/math/divide.tres new file mode 100644 index 00000000..0d6f00a5 --- /dev/null +++ b/addons/block_code/blocks/math/divide.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dwk6c70c4ta0"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rhh7v"] + +[resource] +script = ExtResource("1_rhh7v") +name = &"divide" +target_node_class = "" +description = "" +category = "Math" +type = 3 +variant_type = 3 +display_template = "{a: FLOAT} / {b: FLOAT}" +code_template = "{a} / {b}" +defaults = { +"a": 1.0, +"b": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/multiply.tres b/addons/block_code/blocks/math/multiply.tres new file mode 100644 index 00000000..1e29b3bb --- /dev/null +++ b/addons/block_code/blocks/math/multiply.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://yipjitb3p66q"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_c5vny"] + +[resource] +script = ExtResource("1_c5vny") +name = &"multiply" +target_node_class = "" +description = "" +category = "Math" +type = 3 +variant_type = 3 +display_template = "{a: FLOAT} * {b: FLOAT}" +code_template = "{a} * {b}" +defaults = { +"a": 1.0, +"b": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/pow.tres b/addons/block_code/blocks/math/pow.tres new file mode 100644 index 00000000..a21f3eff --- /dev/null +++ b/addons/block_code/blocks/math/pow.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bib11ow5t44to"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_cx5g5"] + +[resource] +script = ExtResource("1_cx5g5") +name = &"pow" +target_node_class = "" +description = "" +category = "Math" +type = 3 +variant_type = 3 +display_template = "{base: FLOAT} ^ {exp: FLOAT}" +code_template = "pow({base}, {exp})" +defaults = { +"base": 1.0, +"exp": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/randf_range.tres b/addons/block_code/blocks/math/randf_range.tres new file mode 100644 index 00000000..ea47e98e --- /dev/null +++ b/addons/block_code/blocks/math/randf_range.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://u35glf576fue"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_03jps"] + +[resource] +script = ExtResource("1_03jps") +name = &"randf_range" +target_node_class = "" +description = "Generate a random floating point number between [i]from[/i] and [i]to[/i] inclusively" +category = "Math" +type = 3 +variant_type = 3 +display_template = "Random floating point number between {from: FLOAT} and {to: FLOAT}" +code_template = "randf_range({from}, {to})" +defaults = { +"from": -1.0, +"to": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/randi_range.tres b/addons/block_code/blocks/math/randi_range.tres new file mode 100644 index 00000000..6636b4d4 --- /dev/null +++ b/addons/block_code/blocks/math/randi_range.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b3b1dyarh2hmo"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_hk574"] + +[resource] +script = ExtResource("1_hk574") +name = &"randi_range" +target_node_class = "" +description = "Generate a random signed 32-bits integer number between [i]from[/i] and [i]to[/i] inclusively. [i]from[/i] and [i]to[/i] can be a negative or positive number" +category = "Math" +type = 3 +variant_type = 2 +display_template = "Random integer number between {from: INT} and {to: INT}" +code_template = "randi_range({from}, {to})" +defaults = { +"from": 0, +"to": 100 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/sin.tres b/addons/block_code/blocks/math/sin.tres new file mode 100644 index 00000000..fca5fca4 --- /dev/null +++ b/addons/block_code/blocks/math/sin.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b1r7f06rfci6o"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_dlr47"] + +[resource] +script = ExtResource("1_dlr47") +name = &"sin" +description = "Calculate the sine of [i]angle[/i]" +category = "Math" +type = 3 +variant_type = 3 +display_template = "sin {angle: FLOAT}" +code_template = "sin(deg_to_rad({angle}))" +defaults = { +"angle": 0.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/subtract.tres b/addons/block_code/blocks/math/subtract.tres new file mode 100644 index 00000000..1284f408 --- /dev/null +++ b/addons/block_code/blocks/math/subtract.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dkt135xfcklya"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_n0dmm"] + +[resource] +script = ExtResource("1_n0dmm") +name = &"subtract" +target_node_class = "" +description = "" +category = "Math" +type = 3 +variant_type = 3 +display_template = "{a: FLOAT} - {b: FLOAT}" +code_template = "{a} - {b}" +defaults = { +"a": 1.0, +"b": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/math/tan.tres b/addons/block_code/blocks/math/tan.tres new file mode 100644 index 00000000..2c6b5cd6 --- /dev/null +++ b/addons/block_code/blocks/math/tan.tres @@ -0,0 +1,18 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://blpo01pjjheqb"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_e2566"] + +[resource] +script = ExtResource("1_e2566") +name = &"tan" +description = "Calculate the tangent of [i]angle[/i]" +category = "Math" +type = 3 +variant_type = 3 +display_template = "tan {angle: FLOAT}" +code_template = "tan(deg_to_rad({angle}))" +defaults = { +"angle": 0.0 +} +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 new file mode 100644 index 00000000..89448e21 --- /dev/null +++ b/addons/block_code/blocks/physics/characterbody2d_move_and_slide.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://cp6ak6wea8ogh"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_e3r2r"] + +[resource] +script = ExtResource("1_e3r2r") +name = &"characterbody2d_move_and_slide" +target_node_class = "CharacterBody2D" +description = "" +category = "Physics | Velocity" +type = 2 +variant_type = 0 +display_template = "Move and slide" +code_template = "move_and_slide()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/audiostreamplayer_play.tres b/addons/block_code/blocks/sounds/audiostreamplayer_play.tres new file mode 100644 index 00000000..1e574832 --- /dev/null +++ b/addons/block_code/blocks/sounds/audiostreamplayer_play.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bxjjml7u3rokv"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_vyl5w"] + +[resource] +script = ExtResource("1_vyl5w") +name = &"audiostreamplayer_play" +target_node_class = "AudioStreamPlayer" +description = "Play the audio stream" +category = "Sounds" +type = 2 +variant_type = 0 +display_template = "Play" +code_template = "play()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres b/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres new file mode 100644 index 00000000..d95f6a31 --- /dev/null +++ b/addons/block_code/blocks/sounds/audiostreamplayer_stop.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://ib16grbtduab"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_db4g2"] + +[resource] +script = ExtResource("1_db4g2") +name = &"audiostreamplayer_stop" +target_node_class = "AudioStreamPlayer" +description = "Stop the audio stream" +category = "Sounds" +type = 2 +variant_type = 0 +display_template = "Stop" +code_template = "stop()" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/load_sound.tres b/addons/block_code/blocks/sounds/load_sound.tres new file mode 100644 index 00000000..918ba273 --- /dev/null +++ b/addons/block_code/blocks/sounds/load_sound.tres @@ -0,0 +1,21 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://coefocdmytg0j"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_4w4si"] + +[resource] +script = ExtResource("1_4w4si") +name = &"load_sound" +target_node_class = "" +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}" +code_template = "var __sound = AudioStreamPlayer.new() +__sound.name = {name} +__sound.set_stream(load({file_path})) +add_child(__sound) +" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/pause_continue_sound.tres b/addons/block_code/blocks/sounds/pause_continue_sound.tres new file mode 100644 index 00000000..17487bbc --- /dev/null +++ b/addons/block_code/blocks/sounds/pause_continue_sound.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" load_steps=4 format=3 uid="uid://wpdspamg3f6g"] + +[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"] + +[resource] +script = ExtResource("1_q04gm") +name = &"pause_continue_sound" +target_node_class = "" +description = "Pause/Continue the audio stream" +category = "Sounds" +type = 2 +variant_type = 0 +display_template = "{pause: NIL} the sound {name: STRING}" +code_template = "var __sound_node = get_node({name}) +if {pause} == \"pause\": + __sound_node.stream_paused = true +else: + __sound_node.stream_paused = false +" +defaults = { +"pause": SubResource("Resource_lalgp") +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/play_sound.tres b/addons/block_code/blocks/sounds/play_sound.tres new file mode 100644 index 00000000..ac910d95 --- /dev/null +++ b/addons/block_code/blocks/sounds/play_sound.tres @@ -0,0 +1,24 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dt022ilveapt5"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_llfp1"] + +[resource] +script = ExtResource("1_llfp1") +name = &"play_sound" +target_node_class = "" +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}" +code_template = "var __sound_node = get_node({name}) +__sound_node.volume_db = {db} +__sound_node.pitch_scale = {pitch} +__sound_node.play() +" +defaults = { +"db": 0.0, +"pitch": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/sounds/stop_sound.tres b/addons/block_code/blocks/sounds/stop_sound.tres new file mode 100644 index 00000000..8233b734 --- /dev/null +++ b/addons/block_code/blocks/sounds/stop_sound.tres @@ -0,0 +1,22 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://csg40u5awp1sy"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_rfujh"] + +[resource] +script = ExtResource("1_rfujh") +name = &"stop_sound" +target_node_class = "" +description = "Stop the audio stream" +category = "Sounds" +type = 2 +variant_type = 0 +display_template = "Stop the sound {name: STRING}" +code_template = "var __sound_node = get_node({name}) +__sound_node.stop() +" +defaults = { +"db": 0.0, +"pitch": 1.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres b/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres new file mode 100644 index 00000000..8059830d --- /dev/null +++ b/addons/block_code/blocks/transform/rigidbody2d_physics_position.tres @@ -0,0 +1,22 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://ses5486g56q"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_72i54"] + +[resource] +script = ExtResource("1_72i54") +name = &"rigidbody2d_physics_position" +target_node_class = "RigidBody2D" +description = "" +category = "Transform | Position" +type = 2 +variant_type = 0 +display_template = "Set Physics Position {position: VECTOR2}" +code_template = "PhysicsServer2D.body_set_state( + get_rid(), + PhysicsServer2D.BODY_STATE_TRANSFORM, + Transform2D.IDENTITY.translated({position}) +) +" +defaults = {} +signal_name = "" +scope = "" diff --git a/addons/block_code/blocks/variables/vector2.tres b/addons/block_code/blocks/variables/vector2.tres new file mode 100644 index 00000000..cbdcf2eb --- /dev/null +++ b/addons/block_code/blocks/variables/vector2.tres @@ -0,0 +1,20 @@ +[gd_resource type="Resource" load_steps=2 format=3 uid="uid://ddj24k1fp0s82"] + +[ext_resource type="Script" path="res://addons/block_code/code_generation/block_definition.gd" id="1_ilw3v"] + +[resource] +script = ExtResource("1_ilw3v") +name = &"vector2" +target_node_class = "" +description = "" +category = "Variables" +type = 3 +variant_type = 5 +display_template = "Vector2 x: {x: FLOAT} y: {y: FLOAT}" +code_template = "Vector2({x}, {y})" +defaults = { +"x": 0.0, +"y": 0.0 +} +signal_name = "" +scope = "" diff --git a/addons/block_code/code_generation/ast_list.gd b/addons/block_code/code_generation/ast_list.gd new file mode 100644 index 00000000..aafb6caa --- /dev/null +++ b/addons/block_code/code_generation/ast_list.gd @@ -0,0 +1,37 @@ +extends RefCounted + +const Types = preload("res://addons/block_code/types/types.gd") +const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd") + +var array: Array[ASTPair] + + +class ASTPair: + var ast: BlockAST + var canvas_position: Vector2 + + func _init(p_ast: BlockAST, p_canvas_position: Vector2): + ast = p_ast + canvas_position = p_canvas_position + + +func _init(): + array = [] + + +func append(ast: BlockAST, canvas_position: Vector2): + array.append(ASTPair.new(ast, canvas_position)) + + +func clear(): + array.clear() + + +func get_top_level_nodes_of_type(block_type: Types.BlockType) -> Array[BlockAST]: + var asts: Array[BlockAST] = [] + + for ast_pair in array: + if ast_pair.ast.root.data.type == block_type: + asts.append(ast_pair.ast) + + return asts diff --git a/addons/block_code/code_generation/block_ast.gd b/addons/block_code/code_generation/block_ast.gd new file mode 100644 index 00000000..a90657e2 --- /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 new file mode 100644 index 00000000..fe36c001 --- /dev/null +++ b/addons/block_code/code_generation/block_definition.gd @@ -0,0 +1,131 @@ +@tool + +extends Resource + +const Types = preload("res://addons/block_code/types/types.gd") + +const FORMAT_STRING_PATTERN = "\\[(?[^\\]]+)\\]|\\{(?[^}]+)\\}|(?