TowerDefense/addons/LevelEditor/WaveMaker.gd
2025-08-29 20:08:41 +02:00

237 lines
7.1 KiB
GDScript

@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 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()
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_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)