TowerDefense/addons/LevelEditor/WaveMaker.gd

303 lines
8.9 KiB
GDScript3
Raw Normal View History

2025-08-29 20:07:58 +02:00
@tool
extends Control
const LEVEL_PATH : String = "res://Levels/"
const LEVEL_REGEX_PATERN : String = "(level_.*)\\.tres$"
const LEVEL_PATH_REGEX_PATERN : String = "res://Levels/[^/]*/$"
2025-08-29 20:07:58 +02:00
const ENEMY_PATH : String = "res://enemies/"
const ENEMY_REGEX_PATERN : String = "(enemy.*)\\.tscn$"
2025-08-29 20:07:58 +02:00
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}
2025-08-30 20:08:12 +02:00
var enemies : Dictionary
2025-08-29 20:07:58 +02:00
2025-08-31 23:07:55 +02:00
@onready var autoLaunchLevel : CheckButton = $VBoxContainer2/ButtonContainer2/AutoLaunchLevel
@onready var levelSelect : CustomOptionButton = $VBoxContainer2/HBoxContainer/LevelSelect
2025-08-31 16:46:11 +02:00
@onready var waveTabContainer : TabContainer = $VBoxContainer2/ScrollContainer/WaveContainer
2025-08-31 23:07:55 +02:00
@onready var towerSelector : ItemList = $VBoxContainer2/ButtonContainer5/TowerSelector
2025-08-30 20:08:12 +02:00
2025-08-29 20:07:58 +02:00
var level : Level
var currentWave : int = -1
2025-08-30 20:08:12 +02:00
func _ready() -> void:
2025-08-31 16:46:11 +02:00
resetApp()
2025-08-30 20:08:12 +02:00
towerSelector.clear()
towerSelector.max_columns = Tower.TYPE.size()
for towerType : String in Tower.TYPE:
if Tower.TYPE.NONE != Tower.TYPE.get(towerType):
2025-08-30 20:08:12 +02:00
towerSelector.add_item(" " + towerType + " ")
2025-08-29 20:07:58 +02:00
func _input(event: InputEvent) -> void:
2025-08-30 20:08:12 +02:00
if event is not InputEventKey || !event.pressed:
2025-08-29 20:07:58 +02:00
return
2025-08-31 16:46:11 +02:00
# NOTE prevent to select new wave button
if event.keycode == KEY_PAGEUP:
2025-08-30 20:08:12 +02:00
waveTabContainer.select_next_available()
2025-08-31 16:46:11 +02:00
elif event.keycode == KEY_PAGEDOWN:
2025-08-30 20:08:12 +02:00
waveTabContainer.select_previous_available()
2025-08-29 20:07:58 +02:00
func buildTree() -> void:
if !level:
return
2025-08-30 00:46:34 +02:00
manageAllowedTowers()
2025-08-29 20:07:58 +02:00
autoLaunchLevel.button_pressed = level.auto_start
for i in level.waves.size():
2025-08-31 16:46:11 +02:00
waveTabContainer.add_child(buildWave(level.waves[i]))
recreateTabBar()
2025-08-29 20:07:58 +02:00
2025-08-30 00:46:34 +02:00
func manageAllowedTowers() -> void:
towerSelector.deselect_all()
for towerAllowed in level.allowedTowers:
# NOTE minus one is for remove none value
towerSelector.select(towerAllowed - 1, false)
2025-08-31 16:46:11 +02:00
func buildWave(wave : Wave) -> VBoxContainer:
2025-08-31 23:07:55 +02:00
var troopContainer : VBoxContainer = VBoxContainer.new()
2025-08-30 20:08:12 +02:00
2025-08-29 20:07:58 +02:00
for i in wave.troops.size():
var troop : Troop = wave.troops[i]
2025-08-31 16:46:11 +02:00
if troop.spawn_delay > 0 || i > 0 && \
!troopContainer.get_child(troopContainer.get_child_count() - 1).has_meta("troop_group"):
2025-08-29 20:07:58 +02:00
troopContainer.add_child(HSeparator.new())
if troop.spawn_delay:
2025-08-31 23:07:55 +02:00
var timeSeparator : HBoxContainer = buildInputLabel(
2025-08-29 20:07:58 +02:00
func(newValue):
troop.spawn_delay = newValue
if newValue == 0:
2025-08-31 16:46:11 +02:00
refreshWaveNode(level.waves[currentWave], currentWave),
2025-08-29 20:07:58 +02:00
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())
2025-08-31 23:07:55 +02:00
var ennemyContainer : VBoxContainer = VBoxContainer.new()
2025-08-29 20:07:58 +02:00
ennemyContainer.add_child(createSection("Troop N°" + str(i + 1), removeTroop.bind(troop, wave), TROOP_LABEL_SETTINGS))
if troop.spawn_delay == 0:
2025-08-31 23:07:55 +02:00
var button : Button = Button.new()
2025-08-29 20:07:58 +02:00
button.text = "Séparer"
button.pressed.connect(
func():
troop.spawn_delay = 1
2025-08-31 16:46:11 +02:00
refreshWaveNode(level.waves[currentWave], currentWave),
2025-08-29 20:07:58 +02:00
)
ennemyContainer.add_child(button)
buildTroop(troop, ennemyContainer)
nodeToAppend.add_child(ennemyContainer)
2025-08-31 23:07:55 +02:00
var addTroopBtn : Button = Button.new()
2025-08-29 20:07:58 +02:00
addTroopBtn.text = "Ajouter une troupe"
addTroopBtn.pressed.connect(addTroop.bind(wave))
troopContainer.add_child(addTroopBtn)
2025-08-31 16:46:11 +02:00
return troopContainer
2025-08-29 20:07:58 +02:00
func buildTroop(troop : Troop, ennemyContainer : VBoxContainer) -> void:
2025-08-31 23:07:55 +02:00
var qtyEdit : HBoxContainer = buildInputLabel(
2025-08-29 20:07:58 +02:00
func(newValue): troop.number_to_spawn = newValue,
troop.number_to_spawn,
CustomLineEdit.TYPE.INT,
"x"
)
2025-08-31 23:07:55 +02:00
var enemySelector : CustomOptionButton = CustomOptionButton.new()
enemySelector.onValueChanged.connect(func(resourcePath): troop.enemy = resourcePath)
enemySelector.setOptions(enemies, troop.enemy)
2025-08-29 20:07:58 +02:00
qtyEdit.add_child(enemySelector)
ennemyContainer.add_child(qtyEdit)
func buildInputLabel(updateCallback : Callable, delay : float, type : CustomLineEdit.TYPE, text : String = "") -> HBoxContainer:
2025-08-31 23:07:55 +02:00
var container : HBoxContainer = HBoxContainer.new()
2025-08-29 20:07:58 +02:00
container.size_flags_vertical = Control.SIZE_SHRINK_CENTER
container.alignment = BoxContainer.ALIGNMENT_CENTER
2025-08-31 23:07:55 +02:00
var timeEdit : CustomLineEdit = CustomLineEdit.new()
2025-08-29 20:07:58 +02:00
timeEdit.inputType = type
timeEdit.setValue(delay)
timeEdit.valueHasChanged.connect(updateCallback)
container.add_child(timeEdit)
if text:
2025-08-31 23:07:55 +02:00
var label : Label = Label.new()
2025-08-29 20:07:58 +02:00
label.text = text
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.label_settings = BASE_LABEL_SETTINGS
container.add_child(label)
return container
2025-08-30 20:08:12 +02:00
2025-08-29 20:07:58 +02:00
func createSection(sectionName : String, BtnCallback : Callable, settings : LabelSettings = BASE_LABEL_SETTINGS) -> HSplitContainer :
2025-08-31 23:07:55 +02:00
var container : HSplitContainer = HSplitContainer.new()
2025-08-29 20:07:58 +02:00
2025-08-31 23:07:55 +02:00
var label : Label = Label.new()
2025-08-29 20:07:58 +02:00
label.text = sectionName
label.label_settings = settings
2025-08-31 23:07:55 +02:00
var button : Button = Button.new()
2025-08-29 20:07:58 +02:00
button.text = "Supprimer"
button.pressed.connect(BtnCallback)
container.add_child(label)
container.add_child(button)
return container
func cleanMenu() -> void:
2025-08-30 20:08:12 +02:00
for child in waveTabContainer.get_children():
2025-08-31 16:46:11 +02:00
waveTabContainer.remove_child(child)
child.queue_free()
2025-08-30 20:08:12 +02:00
func addTroop(toWave : Wave) -> void:
toWave.troops.append(Troop.new())
2025-08-31 16:46:11 +02:00
refreshWaveNode(toWave, currentWave)
2025-08-30 20:08:12 +02:00
func removeTroop(troop : Troop, fromWave : Wave) -> void:
fromWave.troops.erase(troop)
2025-08-31 16:46:11 +02:00
refreshWaveNode(fromWave, currentWave)
func refreshWaveNode(wave : Wave, waveIdx : int) -> void:
2025-08-31 23:07:55 +02:00
var waveNode : VBoxContainer = waveTabContainer.get_child(waveIdx)
2025-08-31 16:46:11 +02:00
waveTabContainer.remove_child(waveNode)
waveNode.queue_free()
var waveUI : VBoxContainer = buildWave(wave)
2025-08-31 16:46:11 +02:00
waveTabContainer.add_child(waveUI)
waveTabContainer.move_child(waveUI, waveIdx)
recreateTabBar()
waveTabContainer.current_tab = waveIdx
2025-08-30 20:08:12 +02:00
func cleanAndBuildMenu() -> void:
cleanMenu()
buildTree()
2025-08-29 20:07:58 +02:00
2025-08-31 16:46:11 +02:00
func resetApp() -> void:
enemies = getOptionsFromFile(ENEMY_PATH, ENEMY_REGEX_PATERN)
levelSelect.setOptions(getOptionsFromFile(LEVEL_PATH, LEVEL_REGEX_PATERN, LEVEL_PATH_REGEX_PATERN))
2025-08-31 16:46:11 +02:00
cleanAndBuildMenu()
2025-08-29 20:07:58 +02:00
func removeWave() -> void:
level.waves.remove_at(currentWave)
2025-08-31 23:07:55 +02:00
var wave : VBoxContainer = waveTabContainer.get_child(currentWave)
2025-08-31 16:46:11 +02:00
waveTabContainer.remove_child(wave)
wave.queue_free()
recreateTabBar()
2025-08-29 20:07:58 +02:00
func addWave() -> void:
2025-08-31 23:07:55 +02:00
var wave : Wave = Wave.new()
2025-08-30 20:08:12 +02:00
level.waves.append(wave)
2025-08-31 16:46:11 +02:00
waveTabContainer.add_child(buildWave(wave))
recreateTabBar()
waveTabContainer.current_tab = level.waves.size() - 1
2025-08-29 20:07:58 +02:00
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
2025-08-30 20:08:12 +02:00
func selectLevel(levelPath : String) -> void :
level = load(levelPath)
2025-08-29 20:07:58 +02:00
cleanAndBuildMenu()
func tabFocusHaschanged(idx : int) -> void:
currentWave = idx
func _on_auto_launch_wave_toggled(toggled_on: bool) -> void:
level.auto_start = toggled_on
func _on_new_level_pressed() -> void:
level = Level.new()
2025-08-30 20:08:12 +02:00
var levelName = "level_" + str(levelSelect.item_count + 1)
levelSelect.addOption(LEVEL_PATH + "/" + levelName + ".tres", levelName, true)
2025-08-29 20:07:58 +02:00
_on_save_pressed()
func _on_save_pressed() -> void:
2025-08-30 20:08:12 +02:00
ResourceSaver.save(level, levelSelect.selectedValue, ResourceSaver.FLAG_BUNDLE_RESOURCES)
2025-08-30 00:46:34 +02:00
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)
2025-08-30 20:08:12 +02:00
func getOptionsFromFile(path : String, regexPattern : String, pathRegexPattern : String = "") -> Dictionary :
var files : Dictionary
var filePaths : Array[String] = EnhancedResource.getFileFromDir(path, regexPattern, pathRegexPattern)
var regex : RegEx = RegEx.create_from_string(regexPattern)
for file in filePaths:
2025-08-31 23:07:55 +02:00
var fileMatch : RegExMatch = regex.search(file)
files.set(fileMatch.strings[1], file)
2025-08-30 20:08:12 +02:00
return files
2025-08-31 16:46:11 +02:00
func recreateTabBar() -> void:
2025-08-31 23:07:55 +02:00
var waveTabBar : TabBar = waveTabContainer.get_tab_bar()
2025-08-31 16:46:11 +02:00
for i in waveTabBar.tab_count:
if i < waveTabContainer.get_child_count():
waveTabBar.set_tab_title(i, "Vague N°" + str(i + 1))
else:
waveTabBar.remove_tab(i)