feat: add level editor addon

This commit is contained in:
Varylios 2025-08-29 20:07:58 +02:00
parent a2213dcce5
commit fefc3e8a5c
18 changed files with 588 additions and 33 deletions

View file

@ -0,0 +1 @@
extends Node

View file

@ -0,0 +1 @@
uid://dno7nph2s6ad0

View file

@ -0,0 +1,32 @@
@tool
extends EditorPlugin
# Replace this value with a PascalCase autoload name, as per the GDScript style guide.
#const AUTOLOAD_NAME = "LevelEditorAutoload"
const ui = preload("res://addons/LevelEditor/wave_maker.tscn")
var main_panel_instance
func _enter_tree():
main_panel_instance = ui.instantiate()
# Add the main panel to the editor's main viewport.
EditorInterface.get_editor_main_screen().add_child(main_panel_instance)
# Hide the main panel. Very much required.
_make_visible(false)
func _has_main_screen():
return true
func _make_visible(visible):
if main_panel_instance:
main_panel_instance.visible = visible
func _get_plugin_name():
return "Level Editor"
func _get_plugin_icon():
return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons")

View file

@ -0,0 +1 @@
uid://ppf3jmjydns0

View file

@ -0,0 +1,51 @@
@tool
extends LineEdit
class_name CustomLineEdit
enum TYPE { INT, TEXT, FLOAT }
@export var inputType : TYPE
@export var step : float = 1
var oldValue : String = ""
var value:
get:
return getTypedValue(text)
func _init() -> void:
text_changed.connect(valueUpdated)
func _input(event : InputEvent) -> void:
if !has_focus() || event is not InputEventKey || !event.pressed:
return
processKeyInput(event)
func processKeyInput(event : InputEventKey) -> void:
if inputType in [TYPE.INT, TYPE.FLOAT]:
if event.keycode == KEY_UP:
text = str(getTypedValue(str(value + step)))
valueUpdated(text)
elif event.keycode == KEY_DOWN:
text = str(getTypedValue(str(value - step)))
valueUpdated(text)
func valueUpdated(newText : String) -> void:
valueHasChanged.emit(value)
func setValue(value) -> void:
text = str(getTypedValue(str(value)))
func getTypedValue(valueToType: String):
match inputType:
TYPE.INT: return int(valueToType)
TYPE.FLOAT: return float(valueToType)
_: return valueToType
signal valueHasChanged(newValue)

View file

@ -0,0 +1 @@
uid://bpv75ucqoy446

View file

@ -0,0 +1,47 @@
@tool
extends OptionButton
class_name CustomOptionButton
@export_dir var resourcePath : String
@export var regexPattern : String
@export var reloadOnOpen : bool = false
var selectedValue : String
func _ready() -> void:
loadData(true)
item_selected.connect(itemHasBeenSelected)
pressed.connect(loadData)
allow_reselect = true
func loadData(force : bool = false) -> void:
if !force && !reloadOnOpen:
return
var regex := RegEx.create_from_string(regexPattern)
var dir := DirAccess.open(resourcePath)
clear()
for file in dir.get_files():
var fileMatch := regex.search(file)
if fileMatch:
add_item(fileMatch.strings[1])
if selectedValue == fileMatch.strings[1]:
selected = item_count - 1
if !selectedValue && item_count > 0:
selected = 0
func itemHasBeenSelected(index : int) -> void:
if selectedValue != get_item_text(index):
selectedValue = get_item_text(index)
onValueChanged.emit(selectedValue)
func selectItemByName(name : String) -> void:
for i in item_count:
if get_item_text(i) == name:
selected = i
return
signal onValueChanged(value : String)

View file

@ -0,0 +1 @@
uid://b47p2u458hsn0

View file

@ -0,0 +1,7 @@
[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://s1lfc81j20la"]
[ext_resource type="FontFile" uid="uid://dv7ow5e7jj355" path="res://Assets/Fonts/Grandstander/static/Grandstander-Light.ttf" id="1_r6cgw"]
[resource]
font = ExtResource("1_r6cgw")
font_size = 24

View file

@ -0,0 +1,4 @@
[gd_resource type="Theme" format=3 uid="uid://bvji8e8p2d72y"]
[resource]
default_font_size = 20

View file

@ -0,0 +1,7 @@
[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://bee458c1kc0j7"]
[ext_resource type="FontFile" uid="uid://ctmfgwv1dwdyg" path="res://Assets/Fonts/Grandstander/static/Grandstander-BoldItalic.ttf" id="1_kyxue"]
[resource]
font = ExtResource("1_kyxue")
font_size = 24

View file

@ -0,0 +1,7 @@
[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://cafoo04y1t31t"]
[ext_resource type="FontFile" uid="uid://byqqml5g6dwil" path="res://Assets/Fonts/Grandstander/static/Grandstander-BlackItalic.ttf" id="1_m52f8"]
[resource]
font = ExtResource("1_m52f8")
font_size = 32

View file

@ -0,0 +1,243 @@
@tool
extends Control
const LEVEL_PATH : String = "res://Levels"
const LEVEL_NAME_PATERN : String = "level_{id}.tres"
const LEVEL_NAME_PATH_PATERN : String = LEVEL_PATH + "/{name}.tres"
const ENEMY_PATH : String = "res://enemies"
const ENEMY_REGEX_PATERN : String = "(enemy.*)\\.tscn"
const BASE_LABEL_SETTINGS = preload("res://addons/LevelEditor/UI/baseLabel.tres")
const WAVE_LABEL_SETTINGS = preload("res://addons/LevelEditor/UI/waveLabel.tres")
const TROOP_LABEL_SETTINGS = preload("res://addons/LevelEditor/UI/troopLabel.tres")
const space_multiplicator : int = 10
enum DIRECTION { UP, DOWN, TOP, LEFT, VERTICAL, HORIZONTAL}
@onready var autoLaunchLevel := $VBoxContainer2/ButtonContainer2/AutoLaunchLevel
@onready var waitForKill := $VBoxContainer2/ButtonContainer4/WaitForKill
@onready var levelSelect := $VBoxContainer2/HBoxContainer/LevelSelect
@onready var waveTabContainer := $VBoxContainer2/ScrollContainer/WaveContainer
@onready var waveTimerInput := $VBoxContainer2/ButtonContainer3/WaveTimer
var level : Level
var currentWave : int = -1
func _input(event: InputEvent) -> void:
if !has_focus() || event is not InputEventKey || !event.pressed:
return
if event.keycode == KEY_RIGHT && waveTabContainer.get_tab_count() > waveTabContainer.current_tab:
waveTabContainer.current_tab += 1
elif event.keycode == KEY_LEFT && waveTabContainer.current_tab > 0:
waveTabContainer.current_tab -= 1
func buildTree() -> void:
if !level:
return
autoLaunchLevel.button_pressed = level.auto_start
for i in level.waves.size():
var troopContainer := VBoxContainer.new()
waitForKill.button_pressed = level.waves[i].wait_for_enemy_kills
buildWave(level.waves[i], troopContainer)
waveTabContainer.add_child(troopContainer)
waveTabContainer.set_tab_title(i, "Vague N°" + str(i + 1))
func buildWave(wave : Wave, troopContainer : VBoxContainer) -> void:
for i in wave.troops.size():
var troop : Troop = wave.troops[i]
if i > 0 && !troopContainer.get_child(troopContainer.get_child_count() - 1).has_meta("troop_group") \
|| troop.spawn_delay > 0:
troopContainer.add_child(HSeparator.new())
if troop.spawn_delay:
var timeSeparator := buildInputLabel(
func(newValue):
troop.spawn_delay = newValue
if newValue == 0:
cleanAndBuildMenu(),
troop.spawn_delay,
CustomLineEdit.TYPE.FLOAT,
"sec."
)
troopContainer.add_child(timeSeparator)
var nodeToAppend : BoxContainer = troopContainer
if i < wave.troops.size() -1 && wave.troops[i + 1].spawn_delay == 0:
if i == 0 || !troopContainer.get_child(troopContainer.get_child_count() - 1).has_meta("troop_group"):
nodeToAppend = HBoxContainer.new()
nodeToAppend.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
nodeToAppend.set_meta("troop_group", true)
troopContainer.add_child(nodeToAppend)
if i > 0 && troop.spawn_delay == 0:
nodeToAppend = troopContainer.get_child(troopContainer.get_child_count() - 1)
nodeToAppend.add_child(VSeparator.new())
var ennemyContainer := VBoxContainer.new()
ennemyContainer.add_child(createSection("Troop N°" + str(i + 1), removeTroop.bind(troop, wave), TROOP_LABEL_SETTINGS))
if troop.spawn_delay == 0:
var button := Button.new()
button.text = "Séparer"
button.pressed.connect(
func():
troop.spawn_delay = 1
cleanAndBuildMenu()
)
ennemyContainer.add_child(button)
buildTroop(troop, ennemyContainer)
nodeToAppend.add_child(ennemyContainer)
var addTroopBtn := Button.new()
addTroopBtn.text = "Ajouter une troupe"
addTroopBtn.pressed.connect(addTroop.bind(wave))
troopContainer.add_child(addTroopBtn)
func buildTroop(troop : Troop, ennemyContainer : VBoxContainer) -> void:
var qtyEdit := buildInputLabel(
func(newValue): troop.number_to_spawn = newValue,
troop.number_to_spawn,
CustomLineEdit.TYPE.INT,
"x"
)
var enemySelector := CustomOptionButton.new()
enemySelector.resourcePath = ENEMY_PATH
enemySelector.regexPattern = ENEMY_REGEX_PATERN
var regex = RegEx.create_from_string(ENEMY_REGEX_PATERN)
enemySelector.onValueChanged.connect(func(enemyFileName): addEnemy(troop, enemyFileName))
if troop.enemy:
enemySelector.selectItemByName(regex.search(troop.enemy.resource_path).strings[0])
qtyEdit.add_child(enemySelector)
ennemyContainer.add_child(qtyEdit)
func buildInputLabel(updateCallback : Callable, delay : float, type : CustomLineEdit.TYPE, text : String = "") -> HBoxContainer:
var container := HBoxContainer.new()
container.size_flags_vertical = Control.SIZE_SHRINK_CENTER
container.alignment = BoxContainer.ALIGNMENT_CENTER
var timeEdit := CustomLineEdit.new()
timeEdit.inputType = type
timeEdit.setValue(delay)
timeEdit.valueHasChanged.connect(updateCallback)
container.add_child(timeEdit)
if text:
var label := Label.new()
label.text = text
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.label_settings = BASE_LABEL_SETTINGS
container.add_child(label)
return container
func createSection(sectionName : String, BtnCallback : Callable, settings : LabelSettings = BASE_LABEL_SETTINGS) -> HSplitContainer :
var container := HSplitContainer.new()
var label := Label.new()
label.text = sectionName
label.label_settings = settings
var button := Button.new()
button.text = "Supprimer"
button.pressed.connect(BtnCallback)
container.add_child(label)
container.add_child(button)
return container
func cleanMenu() -> void:
if waveTabContainer.get_child_count() > 0:
for child in waveTabContainer.get_children():
child.queue_free()
func removeWave() -> void:
level.waves.remove_at(currentWave)
waveTabContainer.get_child(currentWave).queue_free()
func addWave() -> void:
level.waves.append(Wave.new())
cleanAndBuildMenu()
func changeWaveOrder(newPos : int) -> void:
var newWaveOrder : Array[Wave]
var waveToMove = level.waves[currentWave]
for i in level.waves.size():
if i == 0 && newPos == 0:
newWaveOrder.append(waveToMove)
if i != currentWave:
newWaveOrder.append(level.waves[i])
if i == newPos && newPos != 0:
newWaveOrder.append(waveToMove)
currentWave = newPos
level.waves = newWaveOrder
func addTroop(toWave : Wave) -> void:
toWave.troops.append(Troop.new())
cleanAndBuildMenu()
func addEnemy(toTroop : Troop, enemyResourcePath : String) -> void:
var enemy = load(enemyResourcePath)
toTroop.enemy = enemy
func removeTroop(troop : Troop, fromWave : Wave) -> void:
fromWave.troops.erase(troop)
cleanAndBuildMenu()
func selectLevel(levelName : String) -> void :
level = load(LEVEL_NAME_PATH_PATERN.format([["name", levelName]]))
func changeWaveCooldown(duration : float) -> void:
level.waves[currentWave].wait_time_before_launch_wave = duration
func tabFocusHaschanged(idx : int) -> void:
waveTimerInput.setValue(level.waves[idx].wait_time_before_launch_wave)
currentWave = idx
func cleanAndBuildMenu() -> void:
cleanMenu()
buildTree()
func _on_auto_launch_wave_toggled(toggled_on: bool) -> void:
level.auto_start = toggled_on
func _on_wait_for_kill_toggled(toggled_on: bool) -> void:
level.waves[currentWave].wait_for_enemy_kills = toggled_on
func _on_new_level_pressed() -> void:
level = Level.new()
levelSelect.add_item(LEVEL_NAME_PATERN.format([["id", levelSelect.item_count + 1]]))
levelSelect.select(levelSelect.item_count - 1)
_on_save_pressed()
func _on_save_pressed() -> void:
ResourceSaver.save(level, LEVEL_PATH + "/" + levelSelect.selectedValue, ResourceSaver.FLAG_BUNDLE_RESOURCES)

View file

@ -0,0 +1 @@
uid://27y0jliv6ckx

View file

@ -0,0 +1,7 @@
[plugin]
name="LevelEditor"
description=""
author="Varylios"
version="0.3"
script="LevelEditor.gd"

View file

@ -0,0 +1,144 @@
[gd_scene load_steps=4 format=3 uid="uid://dh24t8804isms"]
[ext_resource type="Script" uid="uid://27y0jliv6ckx" path="res://addons/LevelEditor/WaveMaker.gd" id="1_usfft"]
[ext_resource type="Script" uid="uid://b47p2u458hsn0" path="res://addons/LevelEditor/UI/CustomOptionButton.gd" id="2_xjxpq"]
[ext_resource type="Script" uid="uid://bpv75ucqoy446" path="res://addons/LevelEditor/UI/CustomLineEdit.gd" id="3_qw7ts"]
[node name="Menu" type="HBoxContainer"]
offset_right = 885.0
offset_bottom = 574.0
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_usfft")
[node name="MarginContainer" type="MarginContainer" parent="."]
custom_minimum_size = Vector2(20, 0)
layout_mode = 2
[node name="VBoxContainer2" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer2"]
custom_minimum_size = Vector2(0, 20)
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
[node name="LevelSelect" type="OptionButton" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2
selected = 0
allow_reselect = true
item_count = 2
popup/item_0/text = "level_1"
popup/item_0/id = 0
popup/item_1/text = "level_2"
popup/item_1/id = 1
script = ExtResource("2_xjxpq")
resourcePath = "res://Levels"
regexPattern = "(level_.*)\\.tres"
reloadOnOpen = true
metadata/_custom_type_script = "uid://b47p2u458hsn0"
[node name="NewLevel" type="Button" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2
text = "Nouveau Niveau"
[node name="Show" type="Button" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2
text = "Afficher"
[node name="Clean" type="Button" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2
text = "Effacer"
[node name="Save" type="Button" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2
text = "Sauvegarder"
[node name="Test" type="Button" parent="VBoxContainer2/HBoxContainer"]
visible = false
layout_mode = 2
text = "TESTER !!!!"
[node name="ButtonContainer" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
[node name="Add wave" type="Button" parent="VBoxContainer2/ButtonContainer"]
layout_mode = 2
text = "Ajouter une vague"
[node name="ButtonContainer2" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
[node name="AutoLaunchLevel" type="CheckButton" parent="VBoxContainer2/ButtonContainer2"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer2/ButtonContainer2"]
layout_mode = 2
text = "Lancer le niveau auto. "
[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer2/ButtonContainer2"]
custom_minimum_size = Vector2(20, 0)
layout_mode = 2
[node name="RemoveWave" type="Button" parent="VBoxContainer2/ButtonContainer2"]
layout_mode = 2
text = "Suprimer la vague"
[node name="ButtonContainer4" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
[node name="WaitForKill" type="CheckButton" parent="VBoxContainer2/ButtonContainer4"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer2/ButtonContainer4"]
layout_mode = 2
text = "Attendre la mort des enemies pour lancer la vague suivante"
[node name="ButtonContainer3" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
[node name="WaveTimer" type="LineEdit" parent="VBoxContainer2/ButtonContainer3"]
custom_minimum_size = Vector2(55, 55)
layout_mode = 2
script = ExtResource("3_qw7ts")
inputType = 2
step = 0.2
metadata/_custom_type_script = "uid://bpv75ucqoy446"
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer2/ButtonContainer3"]
custom_minimum_size = Vector2(10, 0)
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer2/ButtonContainer3"]
layout_mode = 2
text = "Timer avant lancement de la vague"
[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer2"]
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer2"]
layout_mode = 2
size_flags_vertical = 3
[node name="WaveContainer" type="TabContainer" parent="VBoxContainer2/ScrollContainer"]
layout_mode = 2
size_flags_vertical = 3
clip_tabs = false
drag_to_rearrange_enabled = true
[connection signal="onValueChanged" from="VBoxContainer2/HBoxContainer/LevelSelect" to="." method="selectLevel"]
[connection signal="pressed" from="VBoxContainer2/HBoxContainer/NewLevel" to="." method="_on_new_level_pressed"]
[connection signal="pressed" from="VBoxContainer2/HBoxContainer/Show" to="." method="cleanAndBuildMenu"]
[connection signal="pressed" from="VBoxContainer2/HBoxContainer/Clean" to="." method="cleanMenu"]
[connection signal="pressed" from="VBoxContainer2/HBoxContainer/Save" to="." method="_on_save_pressed"]
[connection signal="pressed" from="VBoxContainer2/ButtonContainer/Add wave" to="." method="addWave"]
[connection signal="toggled" from="VBoxContainer2/ButtonContainer2/AutoLaunchLevel" to="." method="_on_auto_launch_wave_toggled"]
[connection signal="pressed" from="VBoxContainer2/ButtonContainer2/RemoveWave" to="." method="removeWave"]
[connection signal="toggled" from="VBoxContainer2/ButtonContainer4/WaitForKill" to="." method="_on_wait_for_kill_toggled"]
[connection signal="valueHasChanged" from="VBoxContainer2/ButtonContainer3/WaveTimer" to="." method="changeWaveCooldown"]
[connection signal="active_tab_rearranged" from="VBoxContainer2/ScrollContainer/WaveContainer" to="." method="changeWaveOrder"]
[connection signal="tab_changed" from="VBoxContainer2/ScrollContainer/WaveContainer" to="." method="tabFocusHaschanged"]

View file

@ -17,7 +17,7 @@ const DEFAULT_EMISSION_DURATION: float = 1.0
## 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
## 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}
@ -42,7 +42,7 @@ 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
## Multiplier that increases or decreases emission drawing speed
## Acquired from slider in scene
var emission_speed_multiplier: float = 1.0
@ -61,10 +61,10 @@ var settings: Dictionary = {
}
# Scene references
@export var graph_edit: GraphEdit
@export var graph_edit: GraphEdit
@export var logger_button: Button
@export var node_path_line_edit: LineEdit
@export var refresh_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
@ -96,7 +96,7 @@ func _ready() -> void:
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
@ -143,13 +143,13 @@ func stop_session():
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()
@ -167,7 +167,7 @@ func clear_graph():
clean_connection_activity()
# Frees child nodes
for child: Node in graph_edit.get_children():
# This seems to be necessary as per Godot 4.3
# 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
@ -187,20 +187,20 @@ func clear_graph():
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()
@ -211,19 +211,19 @@ func draw_node_data(data: Array):
# 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 = []
@ -231,13 +231,13 @@ func draw_node_data(data: Array):
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()):
@ -306,8 +306,8 @@ func create_button_slot(button_text: String, parent_node: GraphNode, slot_direct
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)
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():
@ -331,13 +331,13 @@ func draw_signal_emission(data: Array):
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:
if keep_emissions:
graph_edit.set_connection_activity(from_node, from_port, to_node, to_port, 1.0)
else:
fade_out_connection(connection)
@ -349,11 +349,11 @@ func fade_out_connection(connection: Dictionary):
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):
@ -407,7 +407,7 @@ func _resize_panel(new_size: float):
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
@ -417,7 +417,7 @@ 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:
else:
_resize_panel(_original_panel_size)
func _open_project_settings():
@ -517,12 +517,12 @@ func _on_keep_emissions_checkbox_toggled(toggled_on: bool) -> void:
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:

View file

@ -29,7 +29,7 @@ window/size/viewport_height=1080
[editor_plugins]
enabled=PackedStringArray("res://addons/signal_lens/plugin.cfg")
enabled=PackedStringArray("res://addons/LevelEditor/plugin.cfg", "res://addons/signal_lens/plugin.cfg")
[file_customization]