diff --git a/Assets/Icones/ppdf_bio_image_placeholder_2.png b/Assets/Icones/ppdf_bio_image_placeholder_2.png new file mode 100644 index 0000000..cb40316 Binary files /dev/null and b/Assets/Icones/ppdf_bio_image_placeholder_2.png differ diff --git a/Assets/Icones/ppdf_bio_image_placeholder_2.png.import b/Assets/Icones/ppdf_bio_image_placeholder_2.png.import new file mode 100644 index 0000000..7408185 --- /dev/null +++ b/Assets/Icones/ppdf_bio_image_placeholder_2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://uptdcefxlv4c" +path="res://.godot/imported/ppdf_bio_image_placeholder_2.png-95efb56131d6f4393fe011f7f38695e5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Icones/ppdf_bio_image_placeholder_2.png" +dest_files=["res://.godot/imported/ppdf_bio_image_placeholder_2.png-95efb56131d6f4393fe011f7f38695e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/Globals/EventBus.gd b/Globals/EventBus.gd index a1df1b1..eec4b3c 100644 --- a/Globals/EventBus.gd +++ b/Globals/EventBus.gd @@ -11,7 +11,7 @@ signal tower_selected(tower : PackedScene, tower_button : TowerButton) signal tower_builded(tower : PackedScene) -#Emitter : +#Emitter : UpgradeButton.gd #Subscriber : tower.gd signal tower_upgraded(tower_to_upgrade : String, upgrade : TowerUpgrade) diff --git a/Levels/world.gd b/Levels/world.gd index c9a2f5e..213f67a 100644 --- a/Levels/world.gd +++ b/Levels/world.gd @@ -1,7 +1,11 @@ extends Node3D +#TODO Get rid of tower_blueprint, remake player controler @onready var tower_blueprint = preload("res://Towers/tower.tscn") -@onready var selected_tower_button : TowerButton = $CanvasLayer/UI/ShopPanel/VBoxContainer/PierreButton + + +@onready var selected_tower : Tower +@onready var selected_tower_button : TowerButton = $CanvasLayer/GameMenu/VBoxContainer/PierreButton @onready var cam : Camera3D = $Camera3D @onready var selector : Node3D = $Selector @@ -27,7 +31,7 @@ var selected_collider : CollisionObject3D func _ready() -> void: $Spawner.spawn_next_wave() - $CanvasLayer/UI/ShopPanel.visible = in_build_menu + $CanvasLayer/GameMenu.visible = in_build_menu EventBus.tower_selected.connect(_on_EventBus_tower_selected) @@ -109,7 +113,7 @@ func build_tower(scene : PackedScene) -> void: EventBus.money_spent.emit(temp_tower.price) temp_tower.tower_button = selected_tower_button tower_count += 1 - add_child(temp_tower) + $Towers.add_child(temp_tower) EventBus.tower_builded.emit(temp_tower) selected_tower_button.tower_in_game = temp_tower send_to_action(temp_tower) @@ -140,13 +144,13 @@ func open_shop(): return in_build_menu = true - $CanvasLayer/UI/ShopPanel.visible = in_build_menu + $CanvasLayer/GameMenu.visible = in_build_menu func close_shop(): in_build_menu = false - $CanvasLayer/UI/ShopPanel.visible = in_build_menu + $CanvasLayer/GameMenu.visible = in_build_menu func _on_EventBus_tower_selected(tower : PackedScene, tower_button : TowerButton): diff --git a/Levels/world.tscn b/Levels/world.tscn index 8d07afd..a45fee8 100644 --- a/Levels/world.tscn +++ b/Levels/world.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=3 uid="uid://ky0uewndeuwv"] +[gd_scene load_steps=18 format=3 uid="uid://ky0uewndeuwv"] [ext_resource type="Script" uid="uid://qqid42jkpkmv" path="res://Levels/world.gd" id="1_fj7yv"] [ext_resource type="PackedScene" uid="uid://d1f6m15niwgt" path="res://Tiles/tile.tscn" id="3_aqk2v"] @@ -8,8 +8,7 @@ [ext_resource type="PackedScene" uid="uid://dpjfo15otkfru" path="res://Tiles/cube.tscn" id="5_wse8f"] [ext_resource type="Script" uid="uid://chu8s12rtdeqx" path="res://Scripts/Wave.gd" id="6_gbfbk"] [ext_resource type="Resource" uid="uid://dkbuy7fetnihs" path="res://Waves/wave_1.tres" id="7_ioo17"] -[ext_resource type="PackedScene" uid="uid://dbx8ow3v45k4k" path="res://UI/ui.tscn" id="9_ioo17"] -[ext_resource type="PackedScene" uid="uid://p6a6rb7sgeqd" path="res://UI/gui.tscn" id="11_ic0uy"] +[ext_resource type="PackedScene" uid="uid://djyye2otjju2d" path="res://game_menu.tscn" id="9_44brb"] [ext_resource type="PackedScene" uid="uid://c54881eookjth" path="res://Assets/Meshes/SM_B26.glb" id="12_44brb"] [ext_resource type="Script" uid="uid://xkk2pxkrwsq8" path="res://bullet_container.gd" id="12_k3n1d"] [ext_resource type="PackedScene" uid="uid://dxeb1kk5ieix2" path="res://Assets/Meshes/SM_MiniHouse.glb" id="13_ju8gl"] @@ -40,7 +39,7 @@ adjustment_saturation = 1.2 script = ExtResource("1_fj7yv") [node name="Camera3D" type="Camera3D" parent="."] -transform = Transform3D(-0.993794, -0.0553124, 0.0965097, -0.00234758, 0.877843, 0.478942, -0.111212, 0.475744, -0.872525, 5.87173, 3.25008, -2.57606) +transform = Transform3D(-0.992981, -0.0586822, 0.10269, -0.00234758, 0.877843, 0.478943, -0.118251, 0.47534, -0.871819, 5.87173, 2.81458, -3.88415) [node name="Selector" parent="." instance=ExtResource("3_tlwt5")] visible = false @@ -175,10 +174,8 @@ light_energy = 0.0 [node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="UI" parent="CanvasLayer" instance=ExtResource("9_ioo17")] - -[node name="GUI" parent="CanvasLayer/UI" instance=ExtResource("11_ic0uy")] -layout_mode = 1 +[node name="GameMenu" parent="CanvasLayer" instance=ExtResource("9_44brb")] +visible = false [node name="BulletContainer" type="Node3D" parent="."] script = ExtResource("12_k3n1d") @@ -200,7 +197,6 @@ transform = Transform3D(-0.0471892, 0, 1.99944, 0, 2, 0, -1.99944, 0, -0.0471892 [node name="SM_Studio24" parent="Props" instance=ExtResource("16_ju8gl")] transform = Transform3D(1.08365, 0, 1.68098, 0, 2, 0, -1.68098, 0, 1.08365, 1.44235, 0, 4.63481) -[connection signal="timeout" from="Spawner/SpawnTimer" to="Spawner" method="_on_spawn_timer_timeout"] +[node name="Towers" type="Node3D" parent="."] -[editable path="CanvasLayer/UI"] -[editable path="CanvasLayer/UI/GUI"] +[connection signal="timeout" from="Spawner/SpawnTimer" to="Spawner" method="_on_spawn_timer_timeout"] diff --git a/Scripts/Upgrades/Upgrade.gd b/Scripts/Upgrades/Upgrade.gd index cfce2f5..2b90750 100644 --- a/Scripts/Upgrades/Upgrade.gd +++ b/Scripts/Upgrades/Upgrade.gd @@ -3,3 +3,5 @@ class_name Upgrade @export var icon : Texture2D = preload("res://Assets/Icones/medal2.png") @export var text : String = "Upgrade" + +var condition : Upgrade diff --git a/Towers/tower.gd b/Towers/tower.gd index 13b3bb9..34cb400 100644 --- a/Towers/tower.gd +++ b/Towers/tower.gd @@ -46,10 +46,34 @@ var is_rest : bool = false: else: in_action() - +@export_category("Upgrades") var tower_upgrades : Array[TowerUpgrade] +@export var tower_shop : Array[TowerUpgrade] +func get_available_upgrades() -> Array[TowerUpgrade]: + var available_upgrades : Array[TowerUpgrade] + #Check every upgrades in shop + for shop_upgrade in tower_shop: + #If they have no conditions, they are available + if shop_upgrade.condition == null: + available_upgrades.append(shop_upgrade) + continue + else: + # If they have a condition, check for the previously bought upgrades + for owned_upgrade in tower_upgrades: #Check + if shop_upgrade.condition == owned_upgrade: + available_upgrades.append(shop_upgrade) + return available_upgrades + +func apply_upgrade(upgrade : TowerUpgrade): + upgrade.apply_upgrade(self) + if tower_shop.has(upgrade): + tower_shop.erase(upgrade) + tower_upgrades.append(upgrade) + else: + printerr("Upgrade already bought") + func _ready() -> void: EventBus.tower_upgraded.connect(_on_EventBus_tower_upgraded) energy_bar.set_up(energy, max_energy) @@ -129,5 +153,8 @@ func _on_energy_cooldown_timeout() -> void: func _on_EventBus_tower_upgraded(tower_to_upgrade : String, upgrade : TowerUpgrade) -> void: if tower_to_upgrade == tower_name: - upgrade.apply_upgrade(self) - tower_upgrades.append(upgrade) + apply_upgrade(upgrade) + + +func _on_input_event(camera: Node, event: InputEvent, event_position: Vector3, normal: Vector3, shape_idx: int) -> void: + if event diff --git a/Towers/tower.tscn b/Towers/tower.tscn index 5d01d17..ff1be78 100644 --- a/Towers/tower.tscn +++ b/Towers/tower.tscn @@ -1,8 +1,11 @@ -[gd_scene load_steps=6 format=3 uid="uid://trg7ag3dqr2l"] +[gd_scene load_steps=9 format=3 uid="uid://trg7ag3dqr2l"] [ext_resource type="Script" uid="uid://8kpvuurr5h5n" path="res://Towers/tower.gd" id="1_egfuc"] [ext_resource type="Texture2D" uid="uid://bn6ikwol6x8r0" path="res://Assets/Characters/Male1.png" id="2_egfuc"] +[ext_resource type="Script" uid="uid://bg0x4egeu17qb" path="res://Scripts/Upgrades/TowerUpgrade.gd" id="2_mnaic"] +[ext_resource type="Resource" uid="uid://c6844q5o2gjhd" path="res://Upgrades/Tower/TowerUpgradeMaxEnergy1.tres" id="3_jv31o"] [ext_resource type="PackedScene" uid="uid://bm303ot1ck82l" path="res://UI/energy_bar_3d.tscn" id="3_pajr1"] +[ext_resource type="Resource" uid="uid://bus11qw5jg8yo" path="res://Upgrades/Tower/TowerUpgradeMaxEnergy2.tres" id="4_5dr1v"] [sub_resource type="BoxShape3D" id="BoxShape3D_egfuc"] size = Vector3(1, 0.1, 1) @@ -14,7 +17,14 @@ radius = 3.0 collision_layer = 4 collision_mask = 4 script = ExtResource("1_egfuc") +tower_name = null +price = null bullet_damage = 5 +action_cooldown = null +max_energy = null +energy_regen = null +energy_cost = null +tower_shop = Array[ExtResource("2_mnaic")]([ExtResource("3_jv31o"), ExtResource("4_5dr1v")]) [node name="CollisionShape3D" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.00914001, 0) @@ -51,6 +61,7 @@ frame = 75 [node name="EnergyBar3D" parent="." instance=ExtResource("3_pajr1")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.44473, 0) +[connection signal="input_event" from="." to="." method="_on_input_event"] [connection signal="body_entered" from="Range" to="." method="_on_range_body_entered"] [connection signal="body_exited" from="Range" to="." method="_on_range_body_exited"] [connection signal="timeout" from="Cooldown" to="." method="_on_cooldown_timeout"] diff --git a/UI/shop_panel.tscn b/UI/shop_panel.tscn index ac4e570..b3789d7 100644 --- a/UI/shop_panel.tscn +++ b/UI/shop_panel.tscn @@ -1,12 +1,6 @@ -[gd_scene load_steps=9 format=3 uid="uid://he31g7f3ru1c"] +[gd_scene load_steps=3 format=3 uid="uid://he31g7f3ru1c"] [ext_resource type="PackedScene" uid="uid://bqpeu50pgsdfk" path="res://UI/tower_button.tscn" id="1_txsyf"] -[ext_resource type="PackedScene" uid="uid://cpdr2a3xqscfa" path="res://UI/upgrade_button.tscn" id="2_pyoxy"] -[ext_resource type="Script" uid="uid://tn3qdhcyaxsg" path="res://UI/upgrade_button.gd" id="3_jgt3m"] -[ext_resource type="Resource" uid="uid://c6844q5o2gjhd" path="res://Upgrades/Tower/TowerUpgradeMaxEnergy1.tres" id="4_jgt3m"] -[ext_resource type="Resource" uid="uid://b50h2sx6xhdb2" path="res://Upgrades/Tower/TowerUpgradeEnergyRegen1.tres" id="5_wvtj5"] -[ext_resource type="Resource" uid="uid://cfyahlbi4apsj" path="res://Upgrades/Tower/TowerUpgradeActionCooldown1.tres" id="6_32wxa"] -[ext_resource type="Resource" uid="uid://bus11qw5jg8yo" path="res://Upgrades/Tower/TowerUpgradeMaxEnergy2.tres" id="7_dyfge"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_oxe32"] bg_color = Color(0.921569, 0.890196, 0.415686, 1) @@ -44,34 +38,6 @@ grow_vertical = 0 theme_override_styles/fill = SubResource("StyleBoxFlat_oxe32") show_percentage = false -[node name="UpgradeButton" parent="VBoxContainer" instance=ExtResource("2_pyoxy")] -layout_mode = 2 -text = "Max Energy Upgrade +50" -script = ExtResource("3_jgt3m") -upgrade = ExtResource("4_jgt3m") -tower_to_upgrade = "Pierre" - -[node name="UpgradeButton2" parent="VBoxContainer" instance=ExtResource("2_pyoxy")] -layout_mode = 2 -text = "Energy Regen Upgrade +5" -script = ExtResource("3_jgt3m") -upgrade = ExtResource("5_wvtj5") -tower_to_upgrade = "Pierre" - -[node name="UpgradeButton3" parent="VBoxContainer" instance=ExtResource("2_pyoxy")] -layout_mode = 2 -text = "Cooldown Upgrade -0.3" -script = ExtResource("3_jgt3m") -upgrade = ExtResource("6_32wxa") -tower_to_upgrade = "Pierre" - -[node name="UpgradeButton4" parent="VBoxContainer" instance=ExtResource("2_pyoxy")] -layout_mode = 2 -text = "Max Energy Upgrade +100" -script = ExtResource("3_jgt3m") -upgrade = ExtResource("7_dyfge") -tower_to_upgrade = "Pierre" - [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] layout_mode = 2 size_flags_vertical = 3 @@ -81,8 +47,3 @@ custom_minimum_size = Vector2(0, 50) layout_mode = 2 text = "Cancel " - -[connection signal="pressed" from="VBoxContainer/UpgradeButton" to="VBoxContainer/UpgradeButton" method="_on_pressed"] -[connection signal="pressed" from="VBoxContainer/UpgradeButton2" to="VBoxContainer/UpgradeButton2" method="_on_pressed"] -[connection signal="pressed" from="VBoxContainer/UpgradeButton3" to="VBoxContainer/UpgradeButton3" method="_on_pressed"] -[connection signal="pressed" from="VBoxContainer/UpgradeButton4" to="VBoxContainer/UpgradeButton4" method="_on_pressed"] diff --git a/UI/tower_button.gd b/UI/tower_button.gd index 84b7a75..8336f8a 100644 --- a/UI/tower_button.gd +++ b/UI/tower_button.gd @@ -2,11 +2,7 @@ extends Button class_name TowerButton @export var tower : PackedScene -var tower_in_game : Tower +#var tower_in_game : Tower func _on_pressed() -> void: EventBus.tower_selected.emit(tower, self) - -func _process(delta: float) -> void: - if tower_in_game: - $EnergyBar.value = tower_in_game.energy/tower_in_game.max_energy * 100.0 diff --git a/UI/upgrade_button.gd b/UI/upgrade_button.gd index 7dfed08..bb57960 100644 --- a/UI/upgrade_button.gd +++ b/UI/upgrade_button.gd @@ -7,3 +7,4 @@ class_name UpgradeButton func _on_pressed() -> void: EventBus.tower_upgraded.emit(tower_to_upgrade, upgrade) + queue_free() diff --git a/addons/signal_lens/autoload/signal_lens_autoload.gd b/addons/signal_lens/autoload/signal_lens_autoload.gd new file mode 100644 index 0000000..4531ddb --- /dev/null +++ b/addons/signal_lens/autoload/signal_lens_autoload.gd @@ -0,0 +1,192 @@ +## This autoload is responsible for receiving the node path that will be inspected +## from the editor panel and then sending back all the signal data from that node +## parsed into a debugger friendly array +extends Node + +## Reference to the currently targeted node in the remote tree +var target_node: Node = null + +## On singleton ready in scene +## subscribe to the editor panel's request +func _ready() -> void: + EngineDebugger.register_message_capture("signal_lens", _on_node_signal_data_requested) + +## This callback parses a node's signal data into an array that can be sent to the debugger +## The data is packaged in the following structure: +## Pseudo-code: [Name of target node, [All of the node's signals and each signal's respective callables], Class of target node] +## This request is received in the debugger with an array containing the target node's node path +## which will be used to retrieve the target node from the scene +func _on_node_signal_data_requested(prefix, data) -> bool: + var new_target_node = get_tree().root.get_node(data[0]) + + # If node is not found, return false + # If found, keep going + if new_target_node == null: + printerr("No node found in path " + str(data[0])) + return false + + # Avoid error when trying to inspect root node + if new_target_node == get_tree().root: + EngineDebugger.send_message("signal_lens:incoming_node_signal_data", ["Root"]) + return false + + # Disconnect this autoload's callable connections from the previously targeted node's signals + if target_node != null: + if target_node != new_target_node: + for signal_name in target_node.get_signal_list().map(func(p_signal): return p_signal["name"]): + if target_node.is_connected(signal_name, _on_target_node_signal_emitted): + target_node.disconnect(signal_name, _on_target_node_signal_emitted) + + # Acquire newly targeted node reference + target_node = new_target_node + + # Initialize the first piece of data that will be sent to the debugger + # The unique name of the targeted node + # This will be used to set the name of the main graph node in the editor panel + var target_node_name: String = target_node.name + + # Get target node class using Object.get_class() + var target_node_class: String = target_node.get_class() + + # Initialize the array that will store the node's signal data + var target_node_signal_data: Array + + # Get unparsed signal data from target node + var target_node_signal_list: Array[Dictionary] = target_node.get_signal_list() + + # Iterate all signals in target node and parse signal data + # to debugger-friendly format + for i in range(target_node_signal_list.size()): + + var raw_signal_data: Dictionary = target_node_signal_list[i] + # Raw signal data is formatted as: + # [name] is the name of the method, as a String + # [args] is an Array of dictionaries representing the arguments + # [default_args] is the default arguments as an Array of variants + # [flags] is a combination of MethodFlags + # [id] is the method's internal identifier int + # [return] is the returned value, as a Dictionary; + + # Parse signal name + var parsed_signal_name: String = raw_signal_data["name"] + + # Parse signal callables + var raw_signal_connections: Array[Dictionary] = target_node.get_signal_connection_list(raw_signal_data["name"]) + # Raw signal connection is formatted as: + # [signal] is a reference to the Signal; + # [callable] is a reference to the connected Callable; + # [flags] is a combination of ConnectFlags. + + var parsed_signal_callables = parse_signal_callables_to_debugger_format(raw_signal_connections) + + # Create debugger-friendly signal data dictionary + var parsed_signal_data: Dictionary = { + "signal": parsed_signal_name, + "callables": parsed_signal_callables + } + + # Append to overall signal data that will be sent to debugger + target_node_signal_data.append(parsed_signal_data) + + # Connect this autoload's signal emission capture callable to currently iterated signal + # so we can send signal emissions to the editor panel + if not target_node.is_connected(parsed_signal_name, _on_target_node_signal_emitted): + var signal_args: Array = raw_signal_data["args"] + if signal_args.size() > 0: + target_node.connect(parsed_signal_name, _on_target_node_signal_emitted.bind(target_node_name, parsed_signal_name).unbind(signal_args.size())) + else: + target_node.connect(parsed_signal_name, _on_target_node_signal_emitted.bind(target_node_name, parsed_signal_name)) + + # On node data ready, prepare the array as per the debugger's specifications + EngineDebugger.send_message("signal_lens:incoming_node_signal_data", [target_node_name, target_node_signal_data, target_node_class]) + return true + + +func parse_signal_callables_to_debugger_format(raw_signal_connections): + var parsed_signal_callables: Array[Dictionary] + # Iterate all connections of signal to parse callables + for raw_signal_connection: Dictionary in raw_signal_connections: + var parsed_callable_object: Object = raw_signal_connection["callable"].get_object() + var parsed_callable_object_name: String + + # If object has property "name", get this property + # Otherwise, get the string value of the object + # This is important to allow parsing anonymous lambdas, which + # don't have name properties. The names in the nodes are not + # very user-friendly right now, so this is a good spot for a + # TODO: improve readability of anonymous lambda nodes + if not parsed_callable_object: return {"object_name": "ERROR: Couldn't parse node name."} + if parsed_callable_object.get("name") != null: + parsed_callable_object_name = parsed_callable_object.get("name") + else: + parsed_callable_object_name = parsed_callable_object.to_string() + + var parsed_callable_method_name = str(raw_signal_connection["callable"].get_method()) + + # Don't parse callable that is in this autoload + if parsed_callable_method_name == "_on_target_node_signal_emitted": continue + + var parsed_callable_data = { + "object_name": parsed_callable_object_name, + "method_name": parsed_callable_method_name + } + + parsed_signal_callables.append(parsed_callable_data) + return parsed_signal_callables + + +## This callable receives all signal emissions from the currently targeted node +## and sends them to the editor panel + +func _on_target_node_signal_emitted(node_name: String, signal_name: String): + var emission_data: Dictionary = { + "node_name": node_name, + "signal_name": signal_name, + "signal_arguments": [], # NOTE: Empty for now, will be available in a future release targeting Godot 4.5 + "datetime": get_current_datetime_string(), + "timestamp": get_engine_ticks_string(), + "process_frames": Engine.get_process_frames(), + "physics_frames": Engine.get_physics_frames(), + } + + EngineDebugger.send_message("signal_lens:incoming_node_signal_emission", [emission_data]) + + +# NOTE: This function is compatible with Godot 4.5+ only, but 1.4.0 version of Signal Lens +# will still support 4.3+, so I'm keeping it here so it can reimplemented in a future release. +#func _on_target_node_signal_emitted(...args: Array): + # + #var parsed_args: Array + #var signal_args = args.slice(0, args.size() - 2) + #for arg in signal_args: + #parsed_args.append(str(arg)) + # + #var emission_data: Dictionary = { + #"node_name": args[args.size() - 2], + #"signal_name": args[args.size() - 1], + #"datetime": get_current_datetime_string(), + #"timestamp": get_engine_ticks_string(), + #"process_frames": Engine.get_process_frames(), + #"physics_frames": Engine.get_physics_frames(), + #"signal_arguments": parsed_args + #} + # + #EngineDebugger.send_message("signal_lens:incoming_node_signal_emission", [emission_data]) + +func get_current_datetime_string() -> String: + return Time.get_datetime_string_from_system() + +func get_engine_ticks_string() -> String: + var ticks: int = Time.get_ticks_msec() + + # Convert milliseconds to total seconds + var total_seconds = ticks / 1000 + var milliseconds = ticks % 1000 + + # Calculate hours, minutes, and seconds + var hours = total_seconds / 3600 + var minutes = (total_seconds % 3600) / 60 + var seconds = total_seconds % 60 + + # Format with leading zeros + return "%02d:%02d:%02d:%03d" % [hours, minutes, seconds, milliseconds] diff --git a/addons/signal_lens/autoload/signal_lens_autoload.gd.uid b/addons/signal_lens/autoload/signal_lens_autoload.gd.uid new file mode 100644 index 0000000..2197ea2 --- /dev/null +++ b/addons/signal_lens/autoload/signal_lens_autoload.gd.uid @@ -0,0 +1 @@ +uid://85rkx60vm7a7 diff --git a/addons/signal_lens/debugger/signal_lens_debugger.gd b/addons/signal_lens/debugger/signal_lens_debugger.gd new file mode 100644 index 0000000..013ac6a --- /dev/null +++ b/addons/signal_lens/debugger/signal_lens_debugger.gd @@ -0,0 +1,77 @@ +## Backend debugger class that is necessary for debugger plugins +## Handles the engine's debugger callbacks for receiving and sending data +## to the project that is playing +## Sets up the editor panel inside the debugger tab +class_name SignalLensDebugger +extends EditorDebuggerPlugin + +## Emitted on debugging session started +## Can happen on engine open or on play +signal started + +## Emitted when node data is received from autoload in remote scene +signal received_node_data_from_remote(data) + +## Emitted when signal emission data is received from autoload in remote scene +signal received_signal_emission_from_remote(data) + +## Emitted when editor panel requests inspection for [param node_path] +signal requested_node_data_from_remote(node_path: NodePath) + +## Emitted on a breakpoint activated +## Not used yet, can be useful in the future +signal breaked + +## Emitted on continued after breakpoint +## Not used yet, can be useful in the future +signal continued + +## Emitted on debugging session stopped +## Can happen on editor quit or on stopped playing +signal stopped + +## Identifier for debugger session +## Acquired on session started and cached +var current_session_id: int = 0 + +## Designates debugger messages containing this prefix +## as being specific to the plugin +const DEBUGGER_MESSAGE_PREFIX := "signal_lens" + +## Sends data request message to remote scene with [param node_path] +func request_node_data_from_remote(node_path: NodePath): + get_session(current_session_id).send_message("signal_lens:node_signal_data_requested", [node_path]) + requested_node_data_from_remote.emit(node_path) + +## Appends editor panel control to Godot's debugger bottom panel +func setup_editor_panel(editor_panel: SignalLensEditorPanel): + get_session(current_session_id).add_session_tab(editor_panel) + +## This override is necessary so you can send and receive +## messages from the project that is playing +func _has_capture(prefix) -> bool: + return prefix == DEBUGGER_MESSAGE_PREFIX + +## On data from autoload received, send it to the editor panel +func _capture(message, data, session_id) -> bool: + if message == "signal_lens:incoming_node_signal_data": + received_node_data_from_remote.emit(data) + return true + elif message == "signal_lens:incoming_node_signal_emission": + received_signal_emission_from_remote.emit(data) + return true + return false + +## Engine callback override where we set up signals +func _setup_session(session_id): + current_session_id = session_id + var session = get_session(current_session_id) + session.started.connect(_on_session_started) + session.stopped.connect(_on_session_stopped) + session.breaked.connect(_on_session_breaked) + session.continued.connect(_on_session_continued) + +func _on_session_started(): started.emit() +func _on_session_stopped(): stopped.emit() +func _on_session_breaked(can_debug: bool): breaked.emit(can_debug) +func _on_session_continued(): continued.emit() diff --git a/addons/signal_lens/debugger/signal_lens_debugger.gd.uid b/addons/signal_lens/debugger/signal_lens_debugger.gd.uid new file mode 100644 index 0000000..c440ebb --- /dev/null +++ b/addons/signal_lens/debugger/signal_lens_debugger.gd.uid @@ -0,0 +1 @@ +uid://clkfqlih838to diff --git a/addons/signal_lens/editor/logger/logger.gd b/addons/signal_lens/editor/logger/logger.gd new file mode 100644 index 0000000..ada2714 --- /dev/null +++ b/addons/signal_lens/editor/logger/logger.gd @@ -0,0 +1,92 @@ +@tool +class_name Logger +extends Control + +const MAX_LOGS := 1000 + +enum Options { + DATETIME, + TIMESTAMP, + PROCESS_FRAME, + PHYSICS_FRAME +} + +@export var log_container: Control +@export var copy_button: Button +@export var clear_button: Button +@export var v_split_container: VSplitContainer +@export var h_split_container: HSplitContainer +@export var log_scroll: ScrollContainer +@export var counter_label: Label + +var _logs: Array[String] + +var _starting_v_split_margin_end: int +var _starting_h_split_offset: int + +var _options_popup: PopupMenu + +func _ready() -> void: + copy_button.icon = EditorInterface.get_base_control().get_theme_icon("ActionCopy", "EditorIcons") + clear_button.icon = EditorInterface.get_base_control().get_theme_icon("Clear", "EditorIcons") + _starting_v_split_margin_end = v_split_container.drag_area_margin_end + _starting_h_split_offset = h_split_container.split_offset + counter_label.text = "" + +func clear(): + _logs.clear() + for child in log_container.get_children(): + child.queue_free() + counter_label.text = "" + +func create_log(datetime: String, timestamp: String, node_name: String, signal_name: String, signal_arguments: Array, process_frames: int, physics_frames: int): + if _logs.size() >= MAX_LOGS: + _logs.pop_front() + log_container.get_child(0).queue_free() + + var raw_log = "%s | %s\n%s | %s\n%s → %s" % [datetime, timestamp, process_frames, physics_frames, node_name, signal_name] + if not signal_arguments.is_empty(): raw_log += "\n" + str(signal_arguments) + _logs.append(raw_log) + + var pretty_log: String = "" + + pretty_log += "[font_size=12]" + pretty_log += "[color=WEB_GRAY]%s | %s\nProcess: %s | Physics: %s[/color]\n" % [datetime, timestamp, process_frames, physics_frames] + pretty_log += "[font_size=13][color=WHITE][b]%s → %s[/b]\n" % [node_name, signal_name] + if not signal_arguments.is_empty(): pretty_log += "[color=WHITE]" + str(signal_arguments) + + var log_label: RichTextLabel = RichTextLabel.new() + log_label.bbcode_enabled = true + log_label.fit_content = true + log_label.selection_enabled = true + log_label.add_theme_constant_override("line_separation", 5) + log_label.context_menu_enabled = true + log_label.text = pretty_log + log_label.add_theme_stylebox_override("background", StyleBoxEmpty.new()) + log_label.add_theme_stylebox_override("normal", StyleBoxEmpty.new()) + + log_container.add_child(log_label) + await get_tree().process_frame + log_scroll.scroll_vertical = log_scroll.get_v_scroll_bar().max_value + + counter_label.text = "(%s)" % [str(_logs.size())] + +func _on_clear_button_pressed() -> void: + clear() + + +func _on_copy_button_pressed() -> void: + var result: String + var index = 0 + for pretty_log: String in _logs: + result += pretty_log + index += 1 + if index < _logs.size(): + result += "\n\n" + DisplayServer.clipboard_set(result) + + +func _on_h_split_container_dragged(offset: int) -> void: + var h_split_offset_progress = inverse_lerp(_starting_h_split_offset, 0, offset) + var new_v_split_drag_area = lerp(_starting_v_split_margin_end, 0, h_split_offset_progress) + v_split_container.drag_area_margin_end = new_v_split_drag_area diff --git a/addons/signal_lens/editor/logger/logger.gd.uid b/addons/signal_lens/editor/logger/logger.gd.uid new file mode 100644 index 0000000..6aaa898 --- /dev/null +++ b/addons/signal_lens/editor/logger/logger.gd.uid @@ -0,0 +1 @@ +uid://dpn0ev0370mga diff --git a/addons/signal_lens/editor/signal_lens_editor_panel.gd b/addons/signal_lens/editor/signal_lens_editor_panel.gd new file mode 100644 index 0000000..1e5908c --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_editor_panel.gd @@ -0,0 +1,542 @@ +## Draw's Signal Lens panel in the debugger bottom tab inside Godot's editor +## Parses data received from the runtime autoload into +## a user-friendly graph of a node's signal and its connections +@tool +class_name SignalLensEditorPanel +extends Control + +## Written in the panel's line edit if nothing is selected and a debugger +## session is active +const TUTORIAL_TEXT: String = "Select a node in the remote scene" + +## Default duration a signal emission pulse in seconds +## TODO: This could be a user setting +const DEFAULT_EMISSION_DURATION: float = 1.0 + +## Default opacity of a connection line in the graph when rendered +## TODO: This could be a user setting +const DEFAULT_CONNECTION_OPACITY: float = 0.3 + +## This enum is used to set up the graph node's ports +## in a way that provides more legibility in the code +enum Direction {LEFT, RIGHT} + +## This enum keeps available option IDs +enum Options { + HIDE_SIGNALS_WITHOUT_CONNECTIONS, + HIDE_BUILT_IN_SIGNALS, + SHOW_GRAPH_TOOLBAR, + SHOW_GRAPH_MINIMAP +} + +## Emitted on user pressed "refresh" button +signal node_data_requested(node_path) + +## Current node path being inspected +var current_node: NodePath = "" + +## If true, ignores new incoming data from the remote tree +## effectively locking the panel current node path +var block_new_inspections: bool = false + +## If true, all incoming signal emissions will be drawn and won't fade out +var keep_emissions: bool = false + +## Multiplier that increases or decreases emission drawing speed +## Acquired from slider in scene +var emission_speed_multiplier: float = 1.0 + +var connection_opacity: float = DEFAULT_CONNECTION_OPACITY + +## Array that collects active pulse connections so that they can be +## all cleanup together when unfreezing emissions +var pulsing_connections: Array = [] + +## Value of options +var settings: Dictionary = { + Options.HIDE_SIGNALS_WITHOUT_CONNECTIONS: true, + Options.HIDE_BUILT_IN_SIGNALS: false, + Options.SHOW_GRAPH_TOOLBAR: false, + Options.SHOW_GRAPH_MINIMAP: false +} + +# Scene references +@export var graph_edit: GraphEdit +@export var logger_button: Button +@export var node_path_line_edit: LineEdit +@export var refresh_button: Button +@export var options_button: MenuButton +@onready var options_popup: PopupMenu = options_button.get_popup() +@export var clear_button: Button +@export var inactive_text: Label +@export var warning_text: Label +@export var pin_checkbox: CheckButton +@export var keep_emissions_checkbox: CheckButton +@export var emission_speed_slider: Slider +@export var emission_speed_icon: Button +@export var connection_opacity_icon: Button +@export var logger: Control + +## Initialize panel: Load icons +func _ready() -> void: + _get_parent_editor_split() + options_button.icon = EditorInterface.get_base_control().get_theme_icon("GuiTabMenuHl", "EditorIcons") + clear_button.icon = EditorInterface.get_base_control().get_theme_icon("Clear", "EditorIcons") + refresh_button.icon = EditorInterface.get_base_control().get_theme_icon("Reload", "EditorIcons") + pin_checkbox.icon = EditorInterface.get_base_control().get_theme_icon("Pin", "EditorIcons") + keep_emissions_checkbox.icon = EditorInterface.get_base_control().get_theme_icon("Override", "EditorIcons") + emission_speed_icon.icon = EditorInterface.get_base_control().get_theme_icon("Timer", "EditorIcons") + logger_button.icon = EditorInterface.get_base_control().get_theme_icon("FileList", "EditorIcons") + options_popup.index_pressed.connect(_on_options_index_pressed) # NOTE: ID & index must have same value! + connection_opacity_icon.icon = EditorInterface.get_base_control().get_theme_icon("GuiVisibilityVisible", "EditorIcons") + #main_buttons_container.reparent(graph_edit.get_menu_hbox()) + #graph_edit.get_menu_hbox().move_child(main_buttons_container, 0) + #graph_edit.get_menu_hbox().custom_minimum_size.x = graph_edit.size.x + graph_edit.get_menu_hbox().reparent(panel_container) + graph_edit.get_menu_hbox().hide() + #graph_edit.get_menu_hbox().hide() + repo_button.icon = EditorInterface.get_base_control().get_theme_icon("ExternalLink", "EditorIcons") + +@onready var repo_button: Button = $EditorPanel/MainButtonsContainer/HBoxContainer2/RepoButton + +@onready var panel_container: PanelContainer = $EditorPanel/PanelContainer +@onready var main_buttons_container: MarginContainer = $EditorPanel/MainButtonsContainer + +## Requests inspection of [param current_node] in remote scene +func request_node_data(): + node_data_requested.emit(current_node) + +## Receives node signal data from remote scene +## Data structure is detailed further below +func receive_node_data(data: Array): + draw_node_data(data) + +## Sets up editor on project play +func start_session(): + clear_graph() + pin_checkbox.button_pressed = false + keep_emissions_checkbox.button_pressed = false + emission_speed_slider.editable = true + emission_speed_icon.disabled = false + node_path_line_edit.text = TUTORIAL_TEXT + inactive_text.hide() + + +## Cleans up editor on project stop +func stop_session(): + clear_graph() + pin_checkbox.disabled = true + keep_emissions_checkbox.disabled = true + refresh_button.disabled = true + clear_button.disabled = true + emission_speed_slider.editable = false + emission_speed_icon.disabled = true + node_path_line_edit.text = "" + pin_checkbox.button_pressed = false + keep_emissions_checkbox.button_pressed = false + inactive_text.show() + warning_text.hide() + warning_text.text = "" + + +## Assigns a [param target_node] to internal member [param current_node] +func assign_node_path(target_node: NodePath): + # If locked button is toggled, don't change the current node + if block_new_inspections: return + + # If incoming node is invalid, disable refreshing to avoid null nodes + refresh_button.disabled = target_node.is_empty() + + # Assign incoming node as the current one + current_node = target_node + + # Update line edit + node_path_line_edit.text = current_node + node_path_line_edit.caret_column = node_path_line_edit.text.length() + +#region Graph Rendering + +## Clears all nodes from the debugger panel +## Called on user inspecting new node or on play/quit current +## debug session +func clear_graph(): + # If nothing in graph, return + if graph_edit.get_child_count() == 0: return + # Not clearing connection activity can lead to unwanted behaviour + # So this function must be called here for now + clean_connection_activity() + # Frees child nodes + for child: Node in graph_edit.get_children(): + # This seems to be necessary as per Godot 4.3 + # because this child, despite being internal, + # is iterated in get_children() and if it is + # destroyed, the editor crashed + # so let's ignore it and move on + if child.name == "_connection_layer": continue + child.free() + ## Necessary for the minimap to update, it seems + if graph_edit.minimap_enabled: + graph_edit.minimap_enabled = false + graph_edit.minimap_enabled = true + +## Draws data received from the runtime autoload +## The data is packages in the following structure: +## Pseudo-code: [Name of target node, [All of the node's signals and each signal's respective callables], Class of target node] +## Print result: [{&"name_of_targeted_node", [{"signal": "item_rect_changed", "callables": [{ "object_name": &"Control", "callable_method": "Control::_size_changed"}], "Control"] +## Is is parsed and drawin into nodes, with connections established between signals and their callables +func draw_node_data(data: Array): + # If lock button toggled on, don't draw incoming data + if block_new_inspections: return + + # Clear graph to avoid drawing over old data + clear_graph() + logger.clear() + + # This line is super important to avoid random rendering errors + # It seems we need to give a small breathing room for the graph edit + # to fully cleanup, otherwise, artifacts from a previously rendered + # graph edit may appear and mess up the new drawing + await get_tree().create_timer(0.1).timeout + + # Retrieve the targeted node from the data array, which is always index 0 + var target_node_name = data[0] + + # Handle root node inspection edge case + if target_node_name == "Root": + warning_text.show() + warning_text.text = "Root node inspection is not supported." + return + else: + warning_text.hide() + + # Retrieve the targeted node signal data, which is always index 1 + var target_node_signal_data: Array = data[1] + + # Create main node from which connections will be created + # and add it to the graph + var target_node: SignalLensGraphNode = create_node(target_node_name, "(Signals)") + graph_edit.add_child(target_node) + + var current_signal_index = 0 + + # Start iterating signal by signal + for signal_data in target_node_signal_data: + # Check signal connections and skip not connected signals (based on settings) + if settings[Options.HIDE_SIGNALS_WITHOUT_CONNECTIONS] and signal_data["callables"].size() == 0: continue + + # Check signal connections and skip if signal is built-in (based on settings) + if settings[Options.HIDE_BUILT_IN_SIGNALS]: + var class_signals: Array = [] + for class_signal in ClassDB.class_get_signal_list(data[2]): + class_signals.append(class_signal["name"]) + if signal_data["signal"] in class_signals: + continue + + # Get the color based on the index so we can have the rainbow vibes + var slot_color = get_slot_color(current_signal_index, target_node_signal_data.size()) + + # Create the slot button with the signal's name + create_button_slot(signal_data["signal"], target_node, Direction.RIGHT, slot_color) + + # Start iterating each callable in the signal + var callables_for_current_signal = signal_data["callables"] + for callable_index in range(callables_for_current_signal.size()): + var object_name: String = callables_for_current_signal[callable_index]["object_name"] + var callable_method: String = callables_for_current_signal[callable_index]["method_name"] + # If a node has already been created for the object that owns the callable + # Then we don't create an entirely new node + # Otherwise, we create a new node + if graph_edit.has_node(object_name): + var callable_node = graph_edit.get_node(object_name) + # If callable's object is the same as signal's + # It means that the target node listens to it's own signals + # So we create a new node to avoid confusion and keep everything legible + # for the user + # Otherwise, we just add a new button to an already existing node + if callable_node.name == target_node.name: + var target_callables_node + if !graph_edit.has_node(target_node_name + " (Callables)"): + target_callables_node = create_node(target_node_name + " (Callables)") + graph_edit.add_child(target_callables_node) + target_callables_node.position_offset += Vector2(250, 0) + else: + target_callables_node = graph_edit.get_node(target_node_name + " (Callables)") + create_button_slot(callable_method, target_callables_node, Direction.LEFT, slot_color) + graph_edit.connect_node(target_node.name, current_signal_index, target_callables_node.name, target_callables_node.get_child_count() - 1) + else: + create_button_slot(callable_method, callable_node, Direction.LEFT, slot_color) + graph_edit.connect_node(target_node.name, current_signal_index, callable_node.name, callable_node.get_child_count() - 1) + else: + var callable_node: SignalLensGraphNode = create_node(object_name, "(Callables)") + create_button_slot(callable_method, callable_node, Direction.LEFT, slot_color) + graph_edit.add_child(callable_node) + # We set the offsets here to se can have the descending stair effect in the resulting graph + # TODO: This could be a user setting + callable_node.position_offset += Vector2(callable_node.get_index() * 250, callable_node.get_index() * 50) + graph_edit.connect_node(target_node.name, current_signal_index, callable_node.name, callable_node.get_child_count() - 1) + # Finally, we add to the current iterator and move on to the next signal + current_signal_index += 1 + # Manage button states + # This is important to make sure that if a valid graph is rendered + # in case the buttons are disabled, they are enabled again + if clear_button.disabled: + clear_button.disabled = false + if pin_checkbox.disabled: + pin_checkbox.disabled = false + if keep_emissions_checkbox.disabled: + keep_emissions_checkbox.disabled = false + if emission_speed_slider.editable: + emission_speed_slider.editable = true + emission_speed_icon.disabled = false + +func create_node(node_name: String, title_appendix: String = "") -> SignalLensGraphNode: + var new_node = SignalLensGraphNode.new() + new_node.name = node_name + new_node.title = node_name + " " + title_appendix + return new_node + +func create_button_slot(button_text: String, parent_node: GraphNode, slot_direction: Direction, slot_color: Color): + var signal_button: Button = Button.new() + signal_button.flat = true + signal_button.name = button_text + signal_button.text = button_text + parent_node.add_child(signal_button) + signal_button.pressed.connect(_on_signal_button_pressed.bind(parent_node, signal_button.get_index())) + signal_button.focus_exited.connect(clean_connection_activity) + parent_node.set_slot(signal_button.get_index(), slot_direction == Direction.LEFT, 0, slot_color, slot_direction == Direction.RIGHT, 0, slot_color) + +func get_slot_color(slot_index, signal_amount) -> Color: + var hue = float(slot_index) / float(signal_amount) + return Color.from_hsv(hue, 1.0, 0.5, connection_opacity) + +func clean_connection_activity(): + for connection in graph_edit.get_connection_list(): + graph_edit.set_connection_activity(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"], 0) + +#endregion + +#region Signal Emission Rendering + +func draw_signal_emission(data: Array): + logger.create_log(data[0]["datetime"], data[0]["timestamp"], data[0]["node_name"], data[0]["signal_name"], data[0]["signal_arguments"], data[0]["physics_frames"], data[0]["process_frames"]) + # Avoid trying to draw signal emission if graph not fully drawn yet + if graph_edit.get_child_count() <= 1: return + var target_node: GraphNode = graph_edit.get_child(1) + var port_index = get_port_index_from_signal_name(data[0]["signal_name"]) + if port_index == -1: return + for connection in graph_edit.get_connection_list(): + if connection["from_node"] == target_node.name && connection["from_port"] == port_index: + pulse_connection(connection) + + +func pulse_connection(connection: Dictionary) -> void: + if connection not in pulsing_connections: pulsing_connections.append(connection) + + var from_node = connection["from_node"] + var from_port = connection["from_port"] + var to_node = connection["to_node"] + var to_port = connection["to_port"] + + if keep_emissions: + graph_edit.set_connection_activity(from_node, from_port, to_node, to_port, 1.0) + else: + fade_out_connection(connection) + +func fade_out_connection(connection: Dictionary): + var tween := create_tween() + var fade_out_duration = DEFAULT_EMISSION_DURATION * emission_speed_multiplier + var from_node = connection["from_node"] + var from_port = connection["from_port"] + var to_node = connection["to_node"] + var to_port = connection["to_port"] + + tween.tween_method( + func(value): graph_edit.set_connection_activity(from_node, from_port, to_node, to_port, value), 1.0, 0.0, fade_out_duration + ).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + + tween.tween_callback(func(): pulsing_connections.erase(connection)) + +func get_port_index_from_signal_name(signal_name: String): + var target_node = graph_edit.get_child(1) + for child in target_node.get_children(): + if child.name == signal_name: + return child.get_index() + return -1 + +func keep_signal_emissions(): + keep_emissions = true + +func dont_keep_signal_emissions(): + for connection in pulsing_connections: + fade_out_connection(connection) + keep_emissions = false + +#endregion + +#region Panel Resizing + +# Reference to the Split Container that holds the bottom panel in the editor +var _editor_dock + +# split_offset value of editor dock, works as a size for the panel +# Important: the value is negative because of split_offset's implementation +var _original_panel_size: float + +## Grabs a reference to the parent split container of the debugger +func _get_parent_editor_split(): + var base = EditorInterface.get_base_control() + var waiting := base.get_children() + while not waiting.is_empty(): + var node := waiting.pop_back() as Node + if node.name.find("DockVSplitCenter") >= 0: + _editor_dock = node + _original_panel_size = _editor_dock.split_offset + if visible: + _resize_panel(-ProjectSettings.get_setting("addons/Signal Lens/height_to_resize_to")) + else: + _resize_panel(0) + else: + waiting.append_array(node.get_children()) + +## Resizes panel to new_size if possible +func _resize_panel(new_size: float): + if _can_resize_panel(): + _editor_dock.split_offset = new_size + + +func _can_resize_panel() -> bool: + # If user wants to resize panel on open + if not ProjectSettings.get_setting("addons/Signal Lens/resize_panel_on_open"): return false + + # If editor dock reference has been acquired + if not _editor_dock: return false + return true + + +func _on_visibility_changed() -> void: + # Only resize bottom panel if both visible and visible in editor + if visible and is_visible_in_tree(): + _resize_panel(-ProjectSettings.get_setting("addons/Signal Lens/height_to_resize_to")) + else: + _resize_panel(_original_panel_size) + +func _open_project_settings(): + var base = EditorInterface.get_base_control() + + # Find the Project Settings Editor + var settings = base.find_child('*ProjectSettingsEditor*', true, false) + if not settings: + print('ProjectSettingsEditor not found (?)') + return + + # Grab the tab container from the sectioned editor + var tab_container = settings.find_child('*TabContainer*', true, false) + if not tab_container is TabContainer: + print('Could not find the tab container') + return + + # Set the current tab to General + tab_container.current_tab = 0 + + # Find the Sectioned Editor inside it + var sectioned_inspector = tab_container.find_child('*SectionedInspector*', true, false) + if not sectioned_inspector: + print('SectionedInspector not found (?)') + return + + # Find the Tree inside it + var tree = sectioned_inspector.find_child("Tree", true, false) as Tree + if not tree: + print('Could not find Tree') + return + + # Find the entry in the tree + var found_item = null + var item = tree.get_root() + while item: + item = item.get_next_visible() + if not item: + print('--finished') + break + if item.get_text(0) == "Signal Lens": + found_item = item + break + + # Select the found item + if found_item: + tree.set_selected(found_item, 0) + tree.ensure_cursor_is_visible() + + # Finally popup the Project Settings Editor + settings.popup() + +#endregion + +#region Signal Callbacks + +func _on_refresh_button_pressed() -> void: + if current_node.is_empty(): return + request_node_data() + +func _on_signal_button_pressed(graph_node: GraphNode, internal_index: int): + graph_edit.set_selected(null) + clean_connection_activity() + for connection in graph_edit.get_connection_list(): + if (connection["from_node"] == graph_node.name && connection["from_port"] == internal_index) or (connection["to_node"] == graph_node.name && connection["to_port"] == internal_index): + graph_edit.set_connection_activity(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"], 0.75) + +func _on_graph_edit_node_selected(node: Node) -> void: + var graph_node = node as GraphNode + for connection in graph_edit.get_connection_list(): + if connection["to_node"] == graph_node.name: + graph_edit.set_connection_activity(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"], 0.75) + +func _on_graph_edit_node_deselected(node: Node) -> void: + var graph_node = node as GraphNode + for connection in graph_edit.get_connection_list(): + if connection["to_node"] == graph_node.name: + graph_edit.set_connection_activity(connection["from_node"], connection["from_port"], connection["to_node"], connection["to_port"], 0) + +func _on_clear_button_pressed() -> void: + clear_graph() + +func _on_repo_button_pressed() -> void: + OS.shell_open("https://github.com/yannlemos/signal-lens") + +func _on_pin_checkbox_toggled(toggled_on: bool) -> void: + block_new_inspections = toggled_on + +func _on_emission_speed_slider_value_changed(value: float) -> void: + emission_speed_multiplier = value + +func _on_keep_emissions_checkbox_toggled(toggled_on: bool) -> void: + if toggled_on: + keep_signal_emissions() + else: + dont_keep_signal_emissions() + +func _on_logger_button_toggled(toggled_on: bool) -> void: + logger.visible = toggled_on + +func _on_options_index_pressed(option_index: int) -> void: + if options_popup.is_item_checkable(option_index): + settings[option_index] = not options_popup.is_item_checked(option_index) # Change state + options_popup.set_item_checked(option_index, settings[option_index]) # Apply state + + if option_index in [Options.HIDE_SIGNALS_WITHOUT_CONNECTIONS, Options.HIDE_BUILT_IN_SIGNALS]: + refresh_button.pressed.emit() + elif option_index == Options.SHOW_GRAPH_TOOLBAR: + graph_edit.get_menu_hbox().visible = settings[option_index] + elif option_index == Options.SHOW_GRAPH_MINIMAP: + graph_edit.minimap_enabled = settings[option_index] + else: + _open_project_settings() + +func _on_connection_opacity_slider_value_changed(value: float) -> void: + connection_opacity = value + if graph_edit.get_child_count() <= 1: return + var target_node: GraphNode = graph_edit.get_child(1) + for slot_index in target_node.get_child_count(): + target_node.set_slot_color_right(slot_index, Color(target_node.get_slot_color_right(slot_index), connection_opacity)) + +#endregion diff --git a/addons/signal_lens/editor/signal_lens_editor_panel.gd.uid b/addons/signal_lens/editor/signal_lens_editor_panel.gd.uid new file mode 100644 index 0000000..14b4353 --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_editor_panel.gd.uid @@ -0,0 +1 @@ +uid://dwepw5abso8f1 diff --git a/addons/signal_lens/editor/signal_lens_editor_panel.tscn b/addons/signal_lens/editor/signal_lens_editor_panel.tscn new file mode 100644 index 0000000..b98c76c --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_editor_panel.tscn @@ -0,0 +1,357 @@ +[gd_scene load_steps=21 format=3 uid="uid://f4ie0ytcicf0"] + +[ext_resource type="Script" uid="uid://dwepw5abso8f1" path="res://addons/signal_lens/editor/signal_lens_editor_panel.gd" id="1_abwge"] +[ext_resource type="PackedScene" uid="uid://bps1tpcpa1fj2" path="res://addons/signal_lens/editor/signal_lens_logger.tscn" id="3_v6bl3"] + +[sub_resource type="Image" id="Image_fhx01"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 69, 224, 224, 224, 222, 224, 224, 224, 221, 224, 224, 224, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 68, 224, 224, 224, 220, 224, 224, 224, 220, 227, 227, 227, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 69, 224, 224, 224, 222, 224, 224, 224, 221, 224, 224, 224, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 68, 224, 224, 224, 220, 224, 224, 224, 220, 224, 224, 224, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 69, 224, 224, 224, 222, 224, 224, 224, 220, 224, 224, 224, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 68, 224, 224, 224, 220, 224, 224, 224, 220, 224, 224, 224, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_v6bl3"] +image = SubResource("Image_fhx01") + +[sub_resource type="Image" id="Image_qib1m"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_kqale"] +image = SubResource("Image_qib1m") + +[sub_resource type="Image" id="Image_4h13t"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 3, 224, 224, 224, 105, 224, 224, 224, 192, 224, 224, 224, 244, 224, 224, 224, 238, 224, 224, 224, 197, 224, 224, 224, 105, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 232, 232, 23, 224, 224, 224, 207, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 226, 226, 226, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 6, 224, 224, 224, 205, 224, 224, 224, 255, 224, 224, 224, 218, 224, 224, 224, 83, 237, 237, 237, 14, 237, 237, 237, 14, 224, 224, 224, 82, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 102, 224, 224, 224, 255, 224, 224, 224, 218, 227, 227, 227, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 16, 224, 224, 224, 221, 224, 224, 224, 255, 224, 224, 224, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 198, 224, 224, 224, 255, 224, 224, 224, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 86, 224, 224, 224, 255, 224, 224, 224, 194, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 255, 255, 255, 4, 224, 224, 224, 238, 224, 224, 224, 255, 227, 227, 227, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 229, 229, 19, 224, 224, 224, 255, 224, 224, 224, 233, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 237, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 230, 230, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 90, 224, 224, 224, 255, 224, 224, 224, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 42, 224, 224, 224, 245, 224, 224, 224, 245, 224, 224, 224, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 232, 232, 22, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 96, 226, 226, 226, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 20, 224, 224, 224, 88, 224, 224, 224, 221, 224, 224, 224, 255, 224, 224, 224, 199, 255, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 227, 227, 227, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 236, 224, 224, 224, 195, 224, 224, 224, 96, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_vbdfx"] +image = SubResource("Image_4h13t") + +[sub_resource type="Image" id="Image_5voln"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 184, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 75, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 227, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 243, 224, 224, 224, 217, 224, 224, 224, 191, 224, 224, 224, 166, 224, 224, 224, 140, 224, 224, 224, 114, 224, 224, 224, 89, 227, 227, 227, 63, 229, 229, 229, 38, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_4kas2"] +image = SubResource("Image_5voln") + +[sub_resource type="Image" id="Image_dyx7v"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 127, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 85, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 234, 231, 231, 231, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 171, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 192, 224, 224, 224, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 64, 227, 227, 227, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_c4iiw"] +image = SubResource("Image_dyx7v") + +[sub_resource type="Image" id="Image_5afs0"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 75, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 227, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 75, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 227, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 96, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 224, 224, 224, 42, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 43, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 230, 230, 230, 10, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 159, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 227, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 73, 224, 224, 224, 226, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 227, 227, 227, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_j6ipi"] +image = SubResource("Image_5afs0") + +[sub_resource type="Image" id="Image_jffwt"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 163, 224, 224, 224, 255, 224, 224, 224, 218, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 8, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 28, 224, 224, 224, 235, 224, 224, 224, 255, 224, 224, 224, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 143, 224, 224, 224, 255, 224, 224, 224, 233, 227, 227, 227, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 69, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 105, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 110, 224, 224, 224, 255, 224, 224, 224, 252, 224, 224, 224, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 119, 224, 224, 224, 255, 224, 224, 224, 250, 224, 224, 224, 49, 226, 226, 226, 52, 224, 224, 224, 251, 224, 224, 224, 255, 224, 224, 224, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 169, 224, 224, 224, 255, 224, 224, 224, 207, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 170, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 122, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 227, 227, 71, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 224, 224, 224, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 229, 229, 29, 224, 224, 224, 236, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 234, 227, 227, 227, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 164, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_tvsga"] +image = SubResource("Image_jffwt") + +[sub_resource type="Image" id="Image_wqlh8"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 2, 224, 224, 224, 96, 224, 224, 224, 188, 224, 224, 224, 233, 224, 224, 224, 233, 224, 224, 224, 188, 224, 224, 224, 97, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 40, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 224, 224, 224, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 37, 224, 224, 224, 230, 224, 224, 224, 255, 224, 224, 224, 218, 224, 224, 224, 82, 224, 224, 224, 16, 224, 224, 224, 16, 224, 224, 224, 83, 224, 224, 224, 219, 224, 224, 224, 255, 224, 224, 224, 231, 229, 229, 229, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 4, 224, 224, 224, 219, 224, 224, 224, 255, 224, 224, 224, 218, 224, 224, 224, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 17, 224, 224, 224, 219, 224, 224, 224, 255, 224, 224, 224, 221, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 97, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 82, 0, 0, 0, 0, 226, 226, 226, 69, 224, 224, 224, 222, 224, 224, 224, 221, 224, 224, 224, 68, 0, 0, 0, 0, 224, 224, 224, 85, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 94, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 217, 224, 224, 224, 255, 224, 224, 224, 255, 236, 236, 236, 13, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 219, 0, 0, 0, 0, 227, 227, 227, 18, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 214, 224, 224, 224, 255, 224, 224, 224, 255, 237, 237, 237, 14, 0, 0, 0, 0, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 217, 0, 0, 0, 0, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 97, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 82, 0, 0, 0, 0, 224, 224, 224, 68, 224, 224, 224, 220, 224, 224, 224, 220, 224, 224, 224, 65, 0, 0, 0, 0, 224, 224, 224, 88, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 93, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 5, 224, 224, 224, 223, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 229, 229, 19, 224, 224, 224, 223, 224, 224, 224, 255, 224, 224, 224, 221, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 42, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 84, 224, 224, 224, 17, 227, 227, 227, 18, 226, 226, 226, 87, 224, 224, 224, 223, 224, 224, 224, 255, 224, 224, 224, 233, 224, 224, 224, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 227, 227, 45, 224, 224, 224, 217, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 217, 226, 226, 226, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 3, 224, 224, 224, 99, 224, 224, 224, 189, 224, 224, 224, 233, 224, 224, 224, 233, 224, 224, 224, 189, 224, 224, 224, 98, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_vayht"] +image = SubResource("Image_wqlh8") + +[sub_resource type="Image" id="Image_ioqw6"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 7, 224, 224, 224, 138, 224, 224, 224, 231, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 176, 0, 0, 0, 0, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 138, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 172, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 231, 224, 224, 224, 255, 224, 224, 224, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 193, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 0, 0, 0, 0, 224, 224, 224, 129, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 129, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 194, 224, 224, 224, 193, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 129, 224, 224, 224, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 232, 224, 224, 224, 255, 224, 224, 224, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 76, 224, 224, 224, 255, 224, 224, 224, 230, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 132, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 6, 224, 224, 224, 135, 224, 224, 224, 230, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 230, 224, 224, 224, 132, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_0xhoi"] +image = SubResource("Image_ioqw6") + +[node name="Signal Lens" type="Control" node_paths=PackedStringArray("graph_edit", "logger_button", "node_path_line_edit", "refresh_button", "options_button", "clear_button", "inactive_text", "warning_text", "pin_checkbox", "keep_emissions_checkbox", "emission_speed_slider", "emission_speed_icon", "connection_opacity_icon", "logger")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_abwge") +graph_edit = NodePath("EditorPanel/GraphEdit") +logger_button = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/LoggerButton") +node_path_line_edit = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/LineEdit") +refresh_button = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/RefreshButton") +options_button = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/OptionsButton") +clear_button = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/ClearButton") +inactive_text = NodePath("EditorPanel/InactiveTextContainer/InactiveText") +warning_text = NodePath("EditorPanel/WarningTextContainer/WarningText") +pin_checkbox = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/PinCheckbox") +keep_emissions_checkbox = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/KeepEmissionsCheckbox") +emission_speed_slider = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/EmissionSpeedContainer/EmissionSpeedSlider") +emission_speed_icon = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/EmissionSpeedContainer/EmissionSpeedIcon") +connection_opacity_icon = NodePath("EditorPanel/MainButtonsContainer/HBoxContainer2/ConnectionOpacityContainer/ConnectionOpacityIcon") +logger = NodePath("EditorPanel/Logger") + +[node name="EditorPanel" type="PanelContainer" parent="."] +self_modulate = Color(1, 1, 1, 0) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="GraphEdit" type="GraphEdit" parent="EditorPanel"] +layout_mode = 2 +grid_pattern = 1 +connection_lines_thickness = 3.0 +zoom = 0.8 +minimap_enabled = false +minimap_size = Vector2(120, 80) +show_menu = false +show_zoom_label = true +show_grid_buttons = false +show_minimap_button = false + +[node name="MainButtonsContainer" type="MarginContainer" parent="EditorPanel"] +layout_mode = 2 +size_flags_vertical = 0 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="HBoxContainer2" type="HBoxContainer" parent="EditorPanel/MainButtonsContainer"] +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_constants/separation = 10 + +[node name="OptionsButton" type="MenuButton" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +custom_minimum_size = Vector2(0, 28) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 0 +tooltip_text = "Options Button +See and change options." +icon = SubResource("ImageTexture_v6bl3") +icon_alignment = 1 +item_count = 5 +popup/item_0/text = "Hide signals without connections" +popup/item_0/checkable = 1 +popup/item_0/checked = true +popup/item_0/id = 0 +popup/item_1/text = "Hide built-in signals" +popup/item_1/checkable = 1 +popup/item_1/id = 1 +popup/item_2/text = "Show graph toolbar" +popup/item_2/checkable = 1 +popup/item_2/id = 2 +popup/item_3/text = "Show graph minimap" +popup/item_3/checkable = 1 +popup/item_3/id = 3 +popup/item_4/text = "More options..." +popup/item_4/id = 4 + +[node name="LoggerButton" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_vertical = 0 +tooltip_text = "Log Button +Open Signal Log." +toggle_mode = true +icon = SubResource("ImageTexture_kqale") + +[node name="RefreshButton" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 0 +tooltip_text = "Refresh Button +Redraw the graph." +disabled = true +icon = SubResource("ImageTexture_vbdfx") + +[node name="ClearButton" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 0 +tooltip_text = "Clear Button +Clear the graph." +disabled = true +icon = SubResource("ImageTexture_4kas2") + +[node name="PinCheckbox" type="CheckButton" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 0 +tooltip_text = "Pin This Node +If toggled on, ignores any new selections made in the remote scene." +disabled = true +icon = SubResource("ImageTexture_c4iiw") + +[node name="LineEdit" type="LineEdit" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 +theme_override_font_sizes/font_size = 14 +placeholder_text = "/root" +editable = false +select_all_on_focus = true + +[node name="KeepEmissionsCheckbox" type="CheckButton" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_vertical = 0 +tooltip_text = "Keep emissions +If toggled on, keeps any received signal emissions drawn in the graph. +When toggled off, drawn signal emissions will fade out." +icon = SubResource("ImageTexture_j6ipi") + +[node name="EmissionSpeedContainer" type="HBoxContainer" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_vertical = 0 +tooltip_text = "Emission Duration +Controls the speed of the fade out of signal emissions. +Left means means quick, right means slow." +theme_override_constants/separation = 4 + +[node name="EmissionSpeedIcon" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2/EmissionSpeedContainer"] +layout_mode = 2 +mouse_filter = 2 +icon = SubResource("ImageTexture_tvsga") +flat = true + +[node name="EmissionSpeedSlider" type="HSlider" parent="EditorPanel/MainButtonsContainer/HBoxContainer2/EmissionSpeedContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 +size_flags_vertical = 1 +min_value = 0.3 +max_value = 6.0 +step = 0.1 +value = 1.0 + +[node name="ConnectionOpacityContainer" type="HBoxContainer" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_vertical = 0 +tooltip_text = "Connection Opacity +Controls how transparent the connections betweent the nodes are. +Left means more transparent, right means more opaque." +theme_override_constants/separation = 4 + +[node name="ConnectionOpacityIcon" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2/ConnectionOpacityContainer"] +layout_mode = 2 +mouse_filter = 2 +icon = SubResource("ImageTexture_vayht") +flat = true + +[node name="ConnectionOpacitySlider" type="HSlider" parent="EditorPanel/MainButtonsContainer/HBoxContainer2/ConnectionOpacityContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 +size_flags_vertical = 1 +min_value = 0.1 +max_value = 1.0 +step = 0.01 +value = 0.3 + +[node name="RepoButton" type="Button" parent="EditorPanel/MainButtonsContainer/HBoxContainer2"] +modulate = Color(1, 0.372549, 0.372549, 1) +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 0 +tooltip_text = "Signal Lens on Github +Access the plugin's repository on Github to report issues, suggest features and contribute to the project." +icon = SubResource("ImageTexture_0xhoi") +flat = true + +[node name="Logger" parent="EditorPanel" instance=ExtResource("3_v6bl3")] +visible = false +layout_mode = 2 +theme_override_constants/margin_top = 55 +theme_override_constants/margin_bottom = 55 + +[node name="InactiveTextContainer" type="PanelContainer" parent="EditorPanel"] +custom_minimum_size = Vector2(300, 50) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +mouse_filter = 2 + +[node name="InactiveText" type="Label" parent="EditorPanel/InactiveTextContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +text = "Play project to start debugging" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="WarningTextContainer" type="PanelContainer" parent="EditorPanel"] +custom_minimum_size = Vector2(300, 50) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +mouse_filter = 2 + +[node name="WarningText" type="Label" parent="EditorPanel/WarningTextContainer"] +visible = false +modulate = Color(0.996567, 0.82419, 0.525376, 1) +layout_mode = 2 +size_flags_horizontal = 4 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PanelContainer" type="PanelContainer" parent="EditorPanel"] +layout_mode = 2 +size_flags_vertical = 8 + +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] +[connection signal="node_deselected" from="EditorPanel/GraphEdit" to="." method="_on_graph_edit_node_deselected"] +[connection signal="node_selected" from="EditorPanel/GraphEdit" to="." method="_on_graph_edit_node_selected"] +[connection signal="toggled" from="EditorPanel/MainButtonsContainer/HBoxContainer2/LoggerButton" to="." method="_on_logger_button_toggled"] +[connection signal="pressed" from="EditorPanel/MainButtonsContainer/HBoxContainer2/RefreshButton" to="." method="_on_refresh_button_pressed"] +[connection signal="pressed" from="EditorPanel/MainButtonsContainer/HBoxContainer2/ClearButton" to="." method="_on_clear_button_pressed"] +[connection signal="toggled" from="EditorPanel/MainButtonsContainer/HBoxContainer2/PinCheckbox" to="." method="_on_pin_checkbox_toggled"] +[connection signal="toggled" from="EditorPanel/MainButtonsContainer/HBoxContainer2/KeepEmissionsCheckbox" to="." method="_on_keep_emissions_checkbox_toggled"] +[connection signal="value_changed" from="EditorPanel/MainButtonsContainer/HBoxContainer2/EmissionSpeedContainer/EmissionSpeedSlider" to="." method="_on_emission_speed_slider_value_changed"] +[connection signal="value_changed" from="EditorPanel/MainButtonsContainer/HBoxContainer2/ConnectionOpacityContainer/ConnectionOpacitySlider" to="." method="_on_connection_opacity_slider_value_changed"] +[connection signal="pressed" from="EditorPanel/MainButtonsContainer/HBoxContainer2/RepoButton" to="." method="_on_repo_button_pressed"] diff --git a/addons/signal_lens/editor/signal_lens_graph_node.gd b/addons/signal_lens/editor/signal_lens_graph_node.gd new file mode 100644 index 0000000..64599fe --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_graph_node.gd @@ -0,0 +1,9 @@ +## Base class for all graph nodes instantiated +## in the debugger panel +class_name SignalLensGraphNode +extends GraphNode + +# TODO: This node should absorb a lot of the implemenation contained in signal_lens_editor +# Things like instantiating the buttons, and setting up the node +# This would be a next step to make the code in signal_lens_editor.gd +# much more readable and easily testable diff --git a/addons/signal_lens/editor/signal_lens_graph_node.gd.uid b/addons/signal_lens/editor/signal_lens_graph_node.gd.uid new file mode 100644 index 0000000..4a07a28 --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_graph_node.gd.uid @@ -0,0 +1 @@ +uid://dibsrsyq5uw81 diff --git a/addons/signal_lens/editor/signal_lens_graph_node.tscn b/addons/signal_lens/editor/signal_lens_graph_node.tscn new file mode 100644 index 0000000..8593ebb --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_graph_node.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://cicwkklbx7x1m"] + +[ext_resource type="Script" uid="uid://dibsrsyq5uw81" path="res://addons/signal_lens/editor/signal_lens_graph_node.gd" id="1_p48ar"] + +[node name="SignalLensGraphNode" type="GraphNode"] +offset_left = 358.0 +offset_top = 170.5 +offset_right = 383.0 +offset_bottom = 232.5 +mouse_filter = 1 +position_offset = Vector2(358, 170.5) +script = ExtResource("1_p48ar") diff --git a/addons/signal_lens/editor/signal_lens_logger.tscn b/addons/signal_lens/editor/signal_lens_logger.tscn new file mode 100644 index 0000000..5e29ec4 --- /dev/null +++ b/addons/signal_lens/editor/signal_lens_logger.tscn @@ -0,0 +1,222 @@ +[gd_scene load_steps=13 format=3 uid="uid://bps1tpcpa1fj2"] + +[ext_resource type="Script" uid="uid://dpn0ev0370mga" path="res://addons/signal_lens/editor/logger/logger.gd" id="1_16bi3"] + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_16bi3"] +content_margin_left = 2.0 +content_margin_top = 4.0 +content_margin_right = 0.0 +content_margin_bottom = 4.0 +color = Color(0.499956, 0.499956, 0.499956, 0.479) +thickness = 2 +vertical = true + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v6bl3"] +bg_color = Color(0.0605303, 0.0605303, 0.0605303, 0.878431) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + +[sub_resource type="LabelSettings" id="LabelSettings_v6bl3"] +font_size = 12 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_16bi3"] +content_margin_left = 0.0 +content_margin_top = 0.0 +content_margin_right = 0.0 +content_margin_bottom = 0.0 +bg_color = Color(0.262933, 0.262932, 0.262932, 0.6) +corner_radius_top_left = 30 +corner_radius_top_right = 30 +corner_radius_bottom_right = 30 +corner_radius_bottom_left = 30 +corner_detail = 5 + +[sub_resource type="LabelSettings" id="LabelSettings_tqw23"] +font_size = 8 + +[sub_resource type="LabelSettings" id="LabelSettings_16bi3"] +font_size = 10 + +[sub_resource type="Image" id="Image_16bi3"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_tqw23"] +image = SubResource("Image_16bi3") + +[sub_resource type="Image" id="Image_5voln"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 184, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 75, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 227, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 243, 224, 224, 224, 217, 224, 224, 224, 191, 224, 224, 224, 166, 224, 224, 224, 140, 224, 224, 224, 114, 224, 224, 224, 89, 227, 227, 227, 63, 229, 229, 229, 38, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_4kas2"] +image = SubResource("Image_5voln") + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_v6bl3"] +color = Color(0.244776, 0.244776, 0.244776, 1) + +[node name="Logger" type="MarginContainer" node_paths=PackedStringArray("log_container", "copy_button", "clear_button", "v_split_container", "h_split_container", "log_scroll", "counter_label")] +custom_minimum_size = Vector2(400, 0) +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 277.0 +grow_vertical = 2 +size_flags_horizontal = 0 +mouse_filter = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_bottom = 30 +script = ExtResource("1_16bi3") +log_container = NodePath("VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/LogContainer") +copy_button = NodePath("VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CopyButton") +clear_button = NodePath("VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ClearButton") +v_split_container = NodePath("VSplitContainer") +h_split_container = NodePath("VSplitContainer/HSplitContainer") +log_scroll = NodePath("VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer") +counter_label = NodePath("VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CounterLabel") + +[node name="Control" type="Control" parent="."] +layout_mode = 2 +mouse_filter = 2 + +[node name="VSeparator" type="VSeparator" parent="Control"] +layout_mode = 0 +offset_left = 50.0 +offset_top = -16.0 +offset_right = 54.0 +offset_bottom = 4.0 +mouse_filter = 2 +theme_override_styles/separator = SubResource("StyleBoxLine_16bi3") + +[node name="VSplitContainer" type="VSplitContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 +drag_area_margin_end = 130 + +[node name="HSplitContainer" type="HSplitContainer" parent="VSplitContainer"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 2 +split_offset = -130 + +[node name="Panel" type="MarginContainer" parent="VSplitContainer/HSplitContainer"] +custom_minimum_size = Vector2(260, 80) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="PanelContainer" type="PanelContainer" parent="VSplitContainer/HSplitContainer/Panel"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_v6bl3") + +[node name="MarginContainer" type="MarginContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBoxContainer" type="VBoxContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TitleLabel" type="Label" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Signal Log" +label_settings = SubResource("LabelSettings_v6bl3") + +[node name="PanelContainer" type="PanelContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(10, 9) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "A log will be recorded based on every signal emitted by the currently selected node in the Scene Tree. + +The log consists of: +- Datetime: Human-friendly date and time at the time of emission, e.g. 2025-07-08T10:27:33 +- Timestamp: Amount of milliseconds passed since the project started running, e.g. 00:00:05:646 +- Process: Current _process frame at the time of emission, e.g. 255 (look for _get_process_frame() in the godot documentation for more info) +- Physics: Current _physics_process frame at the time of emission, e.g. 567 (look for _get_physics_frame() in the godot documentation for more info) +- Node Name: The node that is emitting the signal. +- Signal Name: The signal being emitted." +theme_override_styles/panel = SubResource("StyleBoxFlat_16bi3") + +[node name="Button" type="Label" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/PanelContainer"] +layout_mode = 2 +text = "?" +label_settings = SubResource("LabelSettings_tqw23") +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CounterLabel" type="Label" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] +modulate = Color(0.501407, 0.501406, 0.501406, 1) +layout_mode = 2 +size_flags_horizontal = 3 +label_settings = SubResource("LabelSettings_16bi3") +horizontal_alignment = 2 + +[node name="CopyButton" type="Button" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Copy All +Copies all the logs to the clipboard." +icon = SubResource("ImageTexture_tqw23") +flat = true + +[node name="ClearButton" type="Button" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Clear +Clears all the logs." +icon = SubResource("ImageTexture_4kas2") +flat = true + +[node name="HSeparator" type="HSeparator" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_styles/separator = SubResource("StyleBoxLine_v6bl3") + +[node name="ScrollContainer" type="ScrollContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="LogContainer" type="VBoxContainer" parent="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 13 + +[node name="Control" type="Control" parent="VSplitContainer/HSplitContainer"] +layout_mode = 2 +mouse_filter = 2 + +[node name="Control" type="Control" parent="VSplitContainer"] +layout_mode = 2 +mouse_filter = 2 + +[connection signal="dragged" from="VSplitContainer/HSplitContainer" to="." method="_on_h_split_container_dragged"] +[connection signal="pressed" from="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CopyButton" to="." method="_on_copy_button_pressed"] +[connection signal="pressed" from="VSplitContainer/HSplitContainer/Panel/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ClearButton" to="." method="_on_clear_button_pressed"] diff --git a/addons/signal_lens/icons/icon_logo.png b/addons/signal_lens/icons/icon_logo.png new file mode 100644 index 0000000..5cc4c9d Binary files /dev/null and b/addons/signal_lens/icons/icon_logo.png differ diff --git a/addons/signal_lens/icons/icon_logo.png.import b/addons/signal_lens/icons/icon_logo.png.import new file mode 100644 index 0000000..fae8270 --- /dev/null +++ b/addons/signal_lens/icons/icon_logo.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brdnqivcfnsun" +path.bptc="res://.godot/imported/icon_logo.png-5b28148684ce391f1d573fc1b0f67ff7.bptc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://addons/signal_lens/icons/icon_logo.png" +dest_files=["res://.godot/imported/icon_logo.png-5b28148684ce391f1d573fc1b0f67ff7.bptc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=true +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/signal_lens/icons/icon_monogram.png b/addons/signal_lens/icons/icon_monogram.png new file mode 100644 index 0000000..2ce07e2 Binary files /dev/null and b/addons/signal_lens/icons/icon_monogram.png differ diff --git a/addons/signal_lens/icons/icon_monogram.png.import b/addons/signal_lens/icons/icon_monogram.png.import new file mode 100644 index 0000000..958b9f3 --- /dev/null +++ b/addons/signal_lens/icons/icon_monogram.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dgayeoxpknqkv" +path.bptc="res://.godot/imported/icon_monogram.png-7d0e77813dbd8089a5f529fe0e619493.bptc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://addons/signal_lens/icons/icon_monogram.png" +dest_files=["res://.godot/imported/icon_monogram.png-7d0e77813dbd8089a5f529fe0e619493.bptc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=true +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd b/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd new file mode 100644 index 0000000..bd7c951 --- /dev/null +++ b/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd @@ -0,0 +1,20 @@ +## Initializes a hidden inspector plugin that +## enables the automatic grabbing of node paths when selecting them +## in the remote scene +class_name SignalLensRemoteNodeInspector +extends EditorInspectorPlugin + +## Emitted on user selection of node in remote scene +signal node_selected(node_path: NodePath) + +func _can_handle(object: Object) -> bool: + # If the requested node is a section in the project settings, it will skip receiving its + # signals. This line use node type inspection, the "SectionedInspectorFilter" type is a + # built-in type in the editor for the project settings. (See #21) + if object.get_property_list()[0]["name"] == "SectionedInspectorFilter": return false + + return object.get('Node/path') != null + + +func _parse_begin(object: Object) -> void: + node_selected.emit(object.get('Node/path')) diff --git a/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd.uid b/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd.uid new file mode 100644 index 0000000..d630531 --- /dev/null +++ b/addons/signal_lens/inspector/signal_lens_remote_node_inspector.gd.uid @@ -0,0 +1 @@ +uid://dpjyrlyj58ma2 diff --git a/addons/signal_lens/plugin.cfg b/addons/signal_lens/plugin.cfg new file mode 100644 index 0000000..a6afbc6 --- /dev/null +++ b/addons/signal_lens/plugin.cfg @@ -0,0 +1,9 @@ +[plugin] + +name="Signal Lens" +description="Signal Lens is a visual debugger for signal connections. +Given any node in a project, it will render a graph with all the connections to that node's signals while the project is playing in the editor. +It doesn't matter if that node is an autoload, or if the signal's are built-in, or custom made - Signal Lens will draw it all when given the node's global path in the remote." +author="Yann Lemos" +version="1.4.0" +script="plugin.gd" diff --git a/addons/signal_lens/plugin.gd b/addons/signal_lens/plugin.gd new file mode 100644 index 0000000..ae4f2dc --- /dev/null +++ b/addons/signal_lens/plugin.gd @@ -0,0 +1,121 @@ +## Initializes Signal Lens plugin and its internal components +@tool +extends EditorPlugin + +## Preloaded reference to the editor panel that will be added to +## Godot's debugger bottom panel +const SIGNAL_LENS_EDITOR_PANEL = preload("res://addons/signal_lens/editor/signal_lens_editor_panel.tscn") + +## Name of autoload node/class that will be instantiated in the remote scene +## so we can retrieve/send data to it +const AUTOLOAD_NAME = "SignalLens" + +## Debugger object that listens to Godot's callbacks +var debugger: SignalLensDebugger = null + +## Inspector plugin that allows selecting remote scene nodes +var remote_node_inspector: SignalLensRemoteNodeInspector = null + +## Editor panel that draws data received from remote scene +var editor_panel: SignalLensEditorPanel = null + + +## Setups the plugin and connects internal components +## Called on enter editor scene tree +func initialize(): + # Create debugger and inspector objects + debugger = SignalLensDebugger.new() + remote_node_inspector = SignalLensRemoteNodeInspector.new() + + # Register plugins in the engine + add_inspector_plugin(remote_node_inspector) + add_debugger_plugin(debugger) + + # Add plugin project settings + setup_project_settings() + + # Connect node selection in scene tree to backend request for + # that node's signal data + remote_node_inspector.node_selected.connect(debugger.request_node_data_from_remote) + + # Create editor panel and add it to the debugger + editor_panel = SIGNAL_LENS_EDITOR_PANEL.instantiate() + debugger.setup_editor_panel(editor_panel) + + # Connect data received from debugger to editor panel so data can be + # rendered in graph form + debugger.received_node_data_from_remote.connect(editor_panel.draw_node_data) + + # Connect signal emissions from targeted node to editor panel + # so they can be rendered in the graph + debugger.received_signal_emission_from_remote.connect(editor_panel.draw_signal_emission) + + # Connect refresh request to debugger so we can retrieve data from + # currently selected node on demand + editor_panel.node_data_requested.connect(debugger.request_node_data_from_remote) + + # Connect start and stop debugging signals for editor panel setup/cleanup + debugger.started.connect(editor_panel.start_session) + debugger.stopped.connect(editor_panel.stop_session) + + # Connect node selection to editor panel so line edit can reflect currently + # selected node's path + remote_node_inspector.node_selected.connect(editor_panel.assign_node_path) + + +## Creates and sets default values for Project Settings related to the plugin +func setup_project_settings(): + # Resize on open + var setting_resize_panel_on_open = "addons/Signal Lens/resize_panel_on_open" + if not ProjectSettings.has_setting(setting_resize_panel_on_open): + ProjectSettings.set_setting(setting_resize_panel_on_open, true) + ProjectSettings.add_property_info({ + "name": setting_resize_panel_on_open, + "type": TYPE_BOOL, + "hint": TYPE_BOOL, + "hint_string": TYPE_BOOL + }) + ProjectSettings.set_initial_value(setting_resize_panel_on_open, true) + ProjectSettings.set_as_basic(setting_resize_panel_on_open, true) + + # Resize on open + var setting_height_to_resize_to = "addons/Signal Lens/height_to_resize_to" + if not ProjectSettings.has_setting(setting_height_to_resize_to): + ProjectSettings.set_setting(setting_height_to_resize_to, 500) + ProjectSettings.add_property_info({ + "name": setting_height_to_resize_to, + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "200, 900" + }) + ProjectSettings.set_initial_value(setting_height_to_resize_to, 500) + ProjectSettings.set_as_basic(setting_height_to_resize_to, true) + + +## Removes plugin from editor and cleans references +func cleanup(): + # De-register plugins from engine + remove_debugger_plugin(debugger) + remove_inspector_plugin(remote_node_inspector) + + # Remove references to initialized components + remote_node_inspector = null + debugger = null + editor_panel = null + + +#region Engine Callbacks + +func _enter_tree() -> void: + initialize() + +func _exit_tree() -> void: + cleanup() + +func _enable_plugin(): + add_autoload_singleton(AUTOLOAD_NAME, "res://addons/signal_lens/autoload/signal_lens_autoload.gd") + +func _disable_plugin(): + remove_autoload_singleton(AUTOLOAD_NAME) + +#endregion diff --git a/addons/signal_lens/plugin.gd.uid b/addons/signal_lens/plugin.gd.uid new file mode 100644 index 0000000..fb1d7c9 --- /dev/null +++ b/addons/signal_lens/plugin.gd.uid @@ -0,0 +1 @@ +uid://cuq28xy5jnlow diff --git a/addons/signal_lens/test_scene/signal_lens_test_scene.gd b/addons/signal_lens/test_scene/signal_lens_test_scene.gd new file mode 100644 index 0000000..c2e7673 --- /dev/null +++ b/addons/signal_lens/test_scene/signal_lens_test_scene.gd @@ -0,0 +1,20 @@ +extends Control + +signal counter_updated(new_value: int) + +@onready var counter_value_label: Label = $VBoxContainer/Counter/CounterValueLabel + +var _counter: int = 0 + +func _ready() -> void: + counter_updated.connect(func(value): print("Signal fired from anonymous lambda! Value is " + str(value))) + +func _on_counter_updated(new_value: int) -> void: + counter_value_label.text = str(new_value) + +func _on_add_button_pressed() -> void: + _counter += 1 + counter_updated.emit(_counter) + +func _on_counter_value_label_focus_entered() -> void: + print("Player checking out counter value") diff --git a/addons/signal_lens/test_scene/signal_lens_test_scene.gd.uid b/addons/signal_lens/test_scene/signal_lens_test_scene.gd.uid new file mode 100644 index 0000000..5e26c27 --- /dev/null +++ b/addons/signal_lens/test_scene/signal_lens_test_scene.gd.uid @@ -0,0 +1 @@ +uid://dqa2qee7m1lb0 diff --git a/addons/signal_lens/test_scene/signal_lens_test_scene.tscn b/addons/signal_lens/test_scene/signal_lens_test_scene.tscn new file mode 100644 index 0000000..3a55b04 --- /dev/null +++ b/addons/signal_lens/test_scene/signal_lens_test_scene.tscn @@ -0,0 +1,45 @@ +[gd_scene load_steps=2 format=3 uid="uid://ds8cfe4b5j2x6"] + +[ext_resource type="Script" uid="uid://dqa2qee7m1lb0" path="res://addons/signal_lens/test_scene/signal_lens_test_scene.gd" id="1_3ongb"] + +[node name="SignalLensTestScene" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3ongb") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -62.0 +offset_top = -29.0 +offset_right = 62.0 +offset_bottom = 29.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="AddButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Add to counter" + +[node name="Counter" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/Counter"] +layout_mode = 2 +text = "Total times clicked: " + +[node name="CounterValueLabel" type="Label" parent="VBoxContainer/Counter"] +layout_mode = 2 +text = "0" + +[connection signal="counter_updated" from="." to="." method="_on_counter_updated"] +[connection signal="pressed" from="VBoxContainer/AddButton" to="." method="_on_add_button_pressed"] +[connection signal="focus_entered" from="VBoxContainer/Counter/CounterValueLabel" to="." method="_on_counter_value_label_focus_entered"] diff --git a/game_menu.gd b/game_menu.gd new file mode 100644 index 0000000..5f431ec --- /dev/null +++ b/game_menu.gd @@ -0,0 +1,11 @@ +extends Control + +var selected_tower + +func _ready() -> void: + EventBus.tower_selected.connect(_on_EventBus_tower_selected) + + +func _on_EventBus_tower_selected(tower : PackedScene, tower_button : TowerButton): + selected_tower = tower + selected_tower_button = tower_button diff --git a/game_menu.gd.uid b/game_menu.gd.uid new file mode 100644 index 0000000..353ccbb --- /dev/null +++ b/game_menu.gd.uid @@ -0,0 +1 @@ +uid://jg5ny0u7buos diff --git a/game_menu.tscn b/game_menu.tscn new file mode 100644 index 0000000..5d7ea80 --- /dev/null +++ b/game_menu.tscn @@ -0,0 +1,362 @@ +[gd_scene load_steps=7 format=3 uid="uid://djyye2otjju2d"] + +[ext_resource type="Theme" uid="uid://bbp4qcbwkeg7x" path="res://default_theme.tres" id="1_xxfb2"] +[ext_resource type="PackedScene" uid="uid://bqpeu50pgsdfk" path="res://UI/tower_button.tscn" id="2_qus6b"] +[ext_resource type="Script" uid="uid://jg5ny0u7buos" path="res://game_menu.gd" id="2_wr0q8"] +[ext_resource type="Texture2D" uid="uid://uptdcefxlv4c" path="res://Assets/Icones/ppdf_bio_image_placeholder_2.png" id="3_wr0q8"] +[ext_resource type="Script" uid="uid://tn3qdhcyaxsg" path="res://UI/upgrade_button.gd" id="4_3rcmp"] +[ext_resource type="Texture2D" uid="uid://dksohsrys0yy7" path="res://Assets/Icones/baby-face.svg" id="5_pbvm6"] + +[node name="GameMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_xxfb2") +script = ExtResource("2_wr0q8") + +[node name="Background" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MenuHeader" type="HBoxContainer" parent="."] +layout_mode = 1 +offset_left = 304.0 +offset_right = 1918.0 +offset_bottom = 82.0 +alignment = 1 + +[node name="ButtonSettings" type="Button" parent="MenuHeader"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Settings" + +[node name="ButtonPause" type="Button" parent="MenuHeader"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Pause" + +[node name="ButtonContinue" type="Button" parent="MenuHeader"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Continue" + +[node name="ButtonQuit" type="Button" parent="MenuHeader"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Quit" + +[node name="ButtonMoney" type="Button" parent="MenuHeader"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Money" + +[node name="Towers" type="VBoxContainer" parent="."] +layout_mode = 1 +offset_right = 300.0 +offset_bottom = 722.0 + +[node name="ButtonCube" type="Button" parent="Towers"] +layout_mode = 2 +text = "Cube" + +[node name="TowerButtonPierre" parent="Towers" instance=ExtResource("2_qus6b")] +layout_mode = 2 + +[node name="TowerButtonAline" parent="Towers" instance=ExtResource("2_qus6b")] +visible = false +layout_mode = 2 + +[node name="TowerButtonMaxence" parent="Towers" instance=ExtResource("2_qus6b")] +visible = false +layout_mode = 2 + +[node name="TowerButtonAlex" parent="Towers" instance=ExtResource("2_qus6b")] +visible = false +layout_mode = 2 + +[node name="TowerButtonGeraldine" parent="Towers" instance=ExtResource("2_qus6b")] +visible = false +layout_mode = 2 + +[node name="CharacterSheet" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TowerStats" type="Panel" parent="CharacterSheet"] +custom_minimum_size = Vector2(800, 512) +layout_mode = 0 +offset_left = 313.0 +offset_top = 86.0 +offset_right = 1113.0 +offset_bottom = 598.0 + +[node name="MarginContainer2" type="MarginContainer" parent="CharacterSheet/TowerStats"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="HBoxContainer" type="HBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="LabelCharacterSheet" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Fiche Perso : +FRIANDISE" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 40) +layout_mode = 2 + +[node name="StatDamage" type="HBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="NameStat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatDamage"] +layout_mode = 2 +text = "Damage :" + +[node name="Stat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatDamage"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "0" +horizontal_alignment = 2 + +[node name="StatCooldown" type="HBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="NameStat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatCooldown"] +layout_mode = 2 +text = "Cooldown :" + +[node name="Stat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatCooldown"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "0" +horizontal_alignment = 2 + +[node name="StatMaxEnergy" type="HBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="NameStat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatMaxEnergy"] +layout_mode = 2 +text = "Max Energy :" + +[node name="Stat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatMaxEnergy"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "0" +horizontal_alignment = 2 + +[node name="StatRegen" type="HBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="NameStat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatRegen"] +layout_mode = 2 +text = "Energy Regen :" + +[node name="Stat" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/VBoxContainer/StatRegen"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "0" +horizontal_alignment = 2 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="Bio" type="VBoxContainer" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer"] +layout_mode = 2 + +[node name="BioImage" type="TextureRect" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/Bio"] +custom_minimum_size = Vector2(300, 300) +layout_mode = 2 +texture = ExtResource("3_wr0q8") +expand_mode = 1 + +[node name="Label" type="Label" parent="CharacterSheet/TowerStats/MarginContainer2/HBoxContainer/Bio"] +custom_minimum_size = Vector2(300, 0) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/font_size = 30 +text = "Aime se promener dans l'herbe et manger des framboises. Sa petite bouille la rend trop mignonne." +horizontal_alignment = 1 +autowrap_mode = 2 + +[node name="TowerUpgrades" type="Panel" parent="CharacterSheet"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -786.0 +offset_top = -990.0 +offset_right = -19.0 +offset_bottom = -28.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="MarginContainer2" type="MarginContainer" parent="CharacterSheet/TowerUpgrades"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="CharacterSheet/TowerUpgrades/MarginContainer2"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="Title" type="Label" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Boutique" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +custom_minimum_size = Vector2(0, 40) +layout_mode = 2 + +[node name="UpgradeButton" type="Button" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Upgrade 1" +script = ExtResource("4_3rcmp") +metadata/_custom_type_script = "uid://tn3qdhcyaxsg" + +[node name="UpgradeButton2" type="Button" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Upgrade 2" +script = ExtResource("4_3rcmp") +metadata/_custom_type_script = "uid://tn3qdhcyaxsg" + +[node name="UpgradeButton3" type="Button" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Upgrade 3" +script = ExtResource("4_3rcmp") +metadata/_custom_type_script = "uid://tn3qdhcyaxsg" + +[node name="UpgradeButton4" type="Button" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Upgrade 4" +script = ExtResource("4_3rcmp") +metadata/_custom_type_script = "uid://tn3qdhcyaxsg" + +[node name="UpgradeButton5" type="Button" parent="CharacterSheet/TowerUpgrades/MarginContainer2/VBoxContainer"] +layout_mode = 2 +text = "Upgrade 5" +script = ExtResource("4_3rcmp") +metadata/_custom_type_script = "uid://tn3qdhcyaxsg" + +[node name="TowerSkills" type="Panel" parent="CharacterSheet"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -646.0 +offset_top = -471.0 +offset_right = 153.0 +offset_bottom = -28.0 +grow_horizontal = 2 +grow_vertical = 0 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerSkills"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="CharacterSheet/TowerSkills/MarginContainer"] +layout_mode = 2 + +[node name="Title" type="Label" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Capacités" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="SkillUI" type="HBoxContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Icone" type="TextureRect" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI"] +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +texture = ExtResource("5_pbvm6") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI"] +layout_mode = 2 +text = "Bonjour" + +[node name="SkillUI2" type="HBoxContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Icone" type="TextureRect" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI2"] +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +texture = ExtResource("5_pbvm6") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI2"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI2"] +layout_mode = 2 +text = "Compliments" + +[node name="SkillUI3" type="HBoxContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Icone" type="TextureRect" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI3"] +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +texture = ExtResource("5_pbvm6") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI3"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="CharacterSheet/TowerSkills/MarginContainer/VBoxContainer/SkillUI3"] +layout_mode = 2 +text = "Cinéphile" diff --git a/project.godot b/project.godot index c2c9974..f459e04 100644 --- a/project.godot +++ b/project.godot @@ -20,12 +20,17 @@ config/icon="res://icon.svg" GameManager="*res://Globals/GameManager.gd" EventBus="*res://Globals/EventBus.gd" Transition="*res://Globals/transition.tscn" +SignalLens="*res://addons/signal_lens/autoload/signal_lens_autoload.gd" [display] window/size/viewport_width=1920 window/size/viewport_height=1080 +[editor_plugins] + +enabled=PackedStringArray("res://addons/signal_lens/plugin.cfg") + [file_customization] folder_colors={ diff --git a/spawner.gd b/spawner.gd index 5d01d78..f229c0d 100644 --- a/spawner.gd +++ b/spawner.gd @@ -45,7 +45,6 @@ func spawn_manager() -> void: if enemies_to_spawn > 0: spawn() - print(GameManager.enemies_alive) if GameManager.enemies_alive > 0: wave_on_going = true else: