302 lines
8.5 KiB
GDScript
302 lines
8.5 KiB
GDScript
@tool
|
|
extends Control
|
|
|
|
const LEVEL_PATH : String = "res://Levels"
|
|
const LEVEL_REGEX_PATERN : String = "(level_.*)\\.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}
|
|
|
|
var enemies : Dictionary
|
|
|
|
@onready var autoLaunchLevel := $VBoxContainer2/ButtonContainer2/AutoLaunchLevel
|
|
@onready var levelSelect := $VBoxContainer2/HBoxContainer/LevelSelect
|
|
@onready var waveTabContainer : TabContainer = $VBoxContainer2/ScrollContainer/WaveContainer
|
|
@onready var towerSelector := $VBoxContainer2/ButtonContainer5/TowerSelector
|
|
|
|
var level : Level
|
|
var currentWave : int = -1
|
|
|
|
|
|
func _ready() -> void:
|
|
resetApp()
|
|
|
|
towerSelector.clear()
|
|
towerSelector.max_columns = Tower.TYPES.size()
|
|
for towerType : String in Tower.TYPES:
|
|
if Tower.TYPES.NONE != Tower.TYPES.get(towerType):
|
|
towerSelector.add_item(" " + towerType + " ")
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if event is not InputEventKey || !event.pressed:
|
|
return
|
|
|
|
# NOTE prevent to select new wave button
|
|
if event.keycode == KEY_PAGEUP:
|
|
waveTabContainer.select_next_available()
|
|
elif event.keycode == KEY_PAGEDOWN:
|
|
waveTabContainer.select_previous_available()
|
|
|
|
|
|
func buildTree() -> void:
|
|
if !level:
|
|
return
|
|
|
|
manageAllowedTowers()
|
|
autoLaunchLevel.button_pressed = level.auto_start
|
|
for i in level.waves.size():
|
|
waveTabContainer.add_child(buildWave(level.waves[i]))
|
|
|
|
recreateTabBar()
|
|
|
|
|
|
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) -> VBoxContainer:
|
|
var troopContainer := VBoxContainer.new()
|
|
|
|
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:
|
|
refreshWaveNode(level.waves[currentWave], currentWave),
|
|
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
|
|
refreshWaveNode(level.waves[currentWave], currentWave),
|
|
)
|
|
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)
|
|
|
|
return troopContainer
|
|
|
|
|
|
|
|
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.onValueChanged.connect(func(resourcePath): troop.enemy = load(resourcePath))
|
|
var enemy := troop.enemy.resource_path if troop.enemy else ""
|
|
enemySelector.setOptions(enemies, enemy)
|
|
|
|
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:
|
|
for child in waveTabContainer.get_children():
|
|
waveTabContainer.remove_child(child)
|
|
child.queue_free()
|
|
|
|
|
|
func addTroop(toWave : Wave) -> void:
|
|
toWave.troops.append(Troop.new())
|
|
refreshWaveNode(toWave, currentWave)
|
|
|
|
|
|
func removeTroop(troop : Troop, fromWave : Wave) -> void:
|
|
fromWave.troops.erase(troop)
|
|
refreshWaveNode(fromWave, currentWave)
|
|
|
|
|
|
func refreshWaveNode(wave : Wave, waveIdx : int) -> void:
|
|
var waveNode := waveTabContainer.get_child(waveIdx)
|
|
waveTabContainer.remove_child(waveNode)
|
|
waveNode.queue_free()
|
|
var waveUI := buildWave(wave)
|
|
waveTabContainer.add_child(waveUI)
|
|
waveTabContainer.move_child(waveUI, waveIdx)
|
|
recreateTabBar()
|
|
waveTabContainer.current_tab = waveIdx
|
|
|
|
|
|
func cleanAndBuildMenu() -> void:
|
|
cleanMenu()
|
|
buildTree()
|
|
|
|
|
|
func resetApp() -> void:
|
|
enemies = getOptionsFromFile(ENEMY_PATH, ENEMY_REGEX_PATERN)
|
|
levelSelect.setOptions(getOptionsFromFile(LEVEL_PATH, LEVEL_REGEX_PATERN))
|
|
cleanAndBuildMenu()
|
|
|
|
|
|
func removeWave() -> void:
|
|
level.waves.remove_at(currentWave)
|
|
var wave := waveTabContainer.get_child(currentWave)
|
|
waveTabContainer.remove_child(wave)
|
|
wave.queue_free()
|
|
recreateTabBar()
|
|
|
|
|
|
func addWave() -> void:
|
|
var wave := Wave.new()
|
|
level.waves.append(wave)
|
|
waveTabContainer.add_child(buildWave(wave))
|
|
recreateTabBar()
|
|
waveTabContainer.current_tab = level.waves.size() - 1
|
|
|
|
|
|
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 selectLevel(levelPath : String) -> void :
|
|
level = load(levelPath)
|
|
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()
|
|
var levelName = "level_" + str(levelSelect.item_count + 1)
|
|
levelSelect.addOption(LEVEL_PATH + "/" + levelName + ".tres", levelName, true)
|
|
_on_save_pressed()
|
|
|
|
|
|
func _on_save_pressed() -> void:
|
|
ResourceSaver.save(level, 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)
|
|
|
|
|
|
func getOptionsFromFile(path : String, regexPattern : String) -> Dictionary :
|
|
var files = {}
|
|
var regex := RegEx.create_from_string(regexPattern)
|
|
var dir := DirAccess.open(path)
|
|
for file in dir.get_files():
|
|
var fileMatch := regex.search(file)
|
|
if fileMatch:
|
|
files.set(fileMatch.strings[1], path + "/" + file)
|
|
|
|
return files
|
|
|
|
|
|
func recreateTabBar() -> void:
|
|
var waveTabBar := waveTabContainer.get_tab_bar()
|
|
|
|
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)
|