@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 towerSelector := $VBoxContainer2/ButtonContainer5/TowerSelector 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 _ready() -> void: towerSelector.clear() for towerType : String in Tower.TYPES: if Tower.TYPES.NONE != Tower.TYPES.get(towerType): towerSelector.add_item(towerType) func buildTree() -> void: if !level: return manageAllowedTowers() 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 manageAllowedTowers() -> void: towerSelector.deselect_all() for towerAllowed in level.allowedTowers: # NOTE minus one is for remove none value towerSelector.select(towerAllowed - 1, false) func buildWave(wave : Wave, troopContainer : VBoxContainer) -> void: for i in wave.troops.size(): var troop : Troop = wave.troops[i] if troop.spawn_delay > 0 || i > 0 \ && !troopContainer.get_child(troopContainer.get_child_count() - 1).has_meta("troop_group"): 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 tabFocusHaschanged(idx : int) -> void: 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) func onSelectedTowerChange(index: int, selected: int) -> void: # NOTE Adding one for taking NONE value in account index += 1 if selected: level.allowedTowers.append(index) else: level.allowedTowers.erase(index)