diff --git a/Scripts/PlayerManager.gd b/Scripts/PlayerManager.gd index af51b0d..c6032c2 100644 --- a/Scripts/PlayerManager.gd +++ b/Scripts/PlayerManager.gd @@ -40,6 +40,7 @@ func _process(delta: float) -> void: EventBus.close_shop.emit() if selected_collider is Tower: selected_collider.is_rest = true + selected_collider.global_position = Vector3.ZERO emitTeamChanges() func handle_player_controls() -> void: @@ -60,7 +61,7 @@ func handle_player_controls() -> void: selected_collider = ray_result.get("collider") visible = true - selection_icon.visible = false + selection_icon.visible = true global_position = selected_collider.global_position global_position += Vector3(0.0,0.02,0.0) diff --git a/Scripts/Wave.gd b/Scripts/Wave.gd index a498d83..83cb17f 100644 --- a/Scripts/Wave.gd +++ b/Scripts/Wave.gd @@ -2,4 +2,3 @@ extends Resource class_name Wave @export var troops : Array[Troop] -@export var wait_for_enemy_kills : bool = true diff --git a/Scripts/WaveManager.gd b/Scripts/WaveManager.gd index e7a4bfd..11afd4b 100644 --- a/Scripts/WaveManager.gd +++ b/Scripts/WaveManager.gd @@ -10,8 +10,6 @@ var current_troop : Troop var enemies_to_spawn : int = 0 var wave : int = 0 var enemies_alive : int = 0 -var wave_on_going : bool = false - @onready var troopTimer := $TroopTimer @@ -60,35 +58,31 @@ func spawnEnemy(delay : float) -> void: func createEnemy() -> PathFollow3D : - var fp3D : PathFollow3D = current_troop.enemy.instantiate() - var enemy : Enemy = fp3D.find_children("*", "Enemy")[0] + var PF3D : PathFollow3D = current_troop.enemy.instantiate() + var enemy : Enemy = PF3D.find_children("*", "Enemy")[0] enemy.died.connect(func(): enemies_alive -= 1) - return fp3D + return PF3D func spawn_manager() -> void: - #Send next troop - if !troopTimer.is_stopped(): + if not troopTimer.is_stopped(): return - if !current_wave: + if not current_wave || enemies_alive == 0 && current_wave.troops.is_empty(): spawn_next_wave() return - if !current_wave.troops.is_empty(): - current_troop = current_wave.troops.pop_front() - if (current_troop.spawn_delay == 0): - spawn_troop() - else: - troopTimer.start(current_troop.spawn_delay) + current_troop = current_wave.troops.pop_front() + + if not current_troop: + return + + if current_troop.spawn_delay == 0: + spawn_troop() + else: + troopTimer.start(current_troop.spawn_delay) - wave_on_going = true - if enemies_alive == 0 && current_wave.troops.is_empty(): - wave_on_going = false - spawn_next_wave() - elif !current_wave.wait_for_enemy_kills: - spawn_next_wave() func clearLevel() -> void: diff --git a/addons/LevelEditor/Draggable.gd b/addons/LevelEditor/Draggable.gd deleted file mode 100644 index 61510e1..0000000 --- a/addons/LevelEditor/Draggable.gd +++ /dev/null @@ -1 +0,0 @@ -extends Node diff --git a/addons/LevelEditor/Draggable.gd.uid b/addons/LevelEditor/Draggable.gd.uid deleted file mode 100644 index 2c4138e..0000000 --- a/addons/LevelEditor/Draggable.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dno7nph2s6ad0 diff --git a/addons/LevelEditor/UI/CustomLineEdit.gd b/addons/LevelEditor/UI/CustomLineEdit.gd index b7a7d56..78999a1 100644 --- a/addons/LevelEditor/UI/CustomLineEdit.gd +++ b/addons/LevelEditor/UI/CustomLineEdit.gd @@ -1,8 +1,9 @@ @tool extends LineEdit - class_name CustomLineEdit +signal valueHasChanged(newValue) + enum TYPE { INT, TEXT, FLOAT } @export var inputType : TYPE @@ -13,6 +14,7 @@ var value: get: return getTypedValue(text) + func _ready() -> void: text_changed.connect(valueUpdated) @@ -47,5 +49,3 @@ func getTypedValue(valueToType: String): TYPE.INT: return int(valueToType) TYPE.FLOAT: return float(valueToType) _: return valueToType - -signal valueHasChanged(newValue) diff --git a/addons/LevelEditor/UI/CustomOptionButton.gd b/addons/LevelEditor/UI/CustomOptionButton.gd index a5fdbe7..5210d0d 100644 --- a/addons/LevelEditor/UI/CustomOptionButton.gd +++ b/addons/LevelEditor/UI/CustomOptionButton.gd @@ -2,46 +2,48 @@ extends OptionButton class_name CustomOptionButton -@export_dir var resourcePath : String -@export var regexPattern : String -@export var reloadOnOpen : bool = false +signal onValueChanged(value : String) var selectedValue : String +var options : Dictionary + func _ready() -> void: - loadData(true) - item_selected.connect(itemHasBeenSelected) - pressed.connect(loadData) allow_reselect = true + item_selected.connect(itemHasBeenSelected) -func loadData(force : bool = false) -> void: - if !force && !reloadOnOpen: - return - var regex := RegEx.create_from_string(regexPattern) - var dir := DirAccess.open(resourcePath) +func setOptions(options : Dictionary, defaultSelectedValue : String = "") -> void: + self.options = options 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 + options.keys().map(add_item) + selectItemByValue(defaultSelectedValue) + + +func addOption(value : String, displayName : String, autoSelect := false) -> void: + add_item(displayName) + options.set(value, displayName) + + if autoSelect: + selected = options.size() - 1 + selectedValue = value - if !selectedValue && item_count > 0: - selected = 0 func itemHasBeenSelected(index : int) -> void: - if selectedValue != get_item_text(index): - selectedValue = get_item_text(index) + var value := options.get(get_item_text(index)) + if selectedValue != value: + selectedValue = value onValueChanged.emit(selectedValue) -func selectItemByName(name : String) -> void: +func selectItemByValue(value : String) -> void: + var name := options.find_key(value) for i in item_count: if get_item_text(i) == name: selected = i + selectedValue = value return - -signal onValueChanged(value : String) + if options: + selected = 0 + itemHasBeenSelected(0) diff --git a/addons/LevelEditor/WaveMaker.gd b/addons/LevelEditor/WaveMaker.gd index dd01c52..845d4e9 100644 --- a/addons/LevelEditor/WaveMaker.gd +++ b/addons/LevelEditor/WaveMaker.gd @@ -2,8 +2,7 @@ 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 LEVEL_REGEX_PATERN : String = "(level_.*)\\.tres" const ENEMY_PATH : String = "res://enemies" const ENEMY_REGEX_PATERN : String = "(enemy.*)\\.tscn" @@ -15,29 +14,37 @@ const TROOP_LABEL_SETTINGS = preload("res://addons/LevelEditor/UI/troopLabel.tre const space_multiplicator : int = 10 enum DIRECTION { UP, DOWN, TOP, LEFT, VERTICAL, HORIZONTAL} +var enemies : Dictionary @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 _ready() -> void: + enemies = getOptionsFromFile(ENEMY_PATH, ENEMY_REGEX_PATERN) + levelSelect.setOptions(getOptionsFromFile(LEVEL_PATH, LEVEL_REGEX_PATERN)) + + 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 !has_focus() || event is not InputEventKey || !event.pressed: + if event is not InputEventKey || !event.pressed: return if event.keycode == KEY_RIGHT && waveTabContainer.get_tab_count() > waveTabContainer.current_tab: - waveTabContainer.current_tab += 1 + waveTabContainer.select_next_available() elif event.keycode == KEY_LEFT && waveTabContainer.current_tab > 0: - waveTabContainer.current_tab -= 1 + waveTabContainer.select_previous_available() -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: @@ -46,11 +53,7 @@ func buildTree() -> void: 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)) + buildWave(level.waves[i]) func manageAllowedTowers() -> void: @@ -60,7 +63,9 @@ func manageAllowedTowers() -> void: towerSelector.select(towerAllowed - 1, false) -func buildWave(wave : Wave, troopContainer : VBoxContainer) -> void: +func buildWave(wave : Wave) -> void: + var troopContainer := VBoxContainer.new() + for i in wave.troops.size(): var troop : Troop = wave.troops[i] @@ -112,6 +117,9 @@ func buildWave(wave : Wave, troopContainer : VBoxContainer) -> void: addTroopBtn.text = "Ajouter une troupe" addTroopBtn.pressed.connect(addTroop.bind(wave)) troopContainer.add_child(addTroopBtn) + waveTabContainer.add_child(troopContainer) + var tabCount : int = waveTabContainer.get_tab_count() + waveTabContainer.set_tab_title(tabCount - 1, "Vague N°" + str(tabCount)) @@ -124,12 +132,9 @@ func buildTroop(troop : Troop, ennemyContainer : VBoxContainer) -> void: ) 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]) + 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) @@ -155,6 +160,7 @@ func buildInputLabel(updateCallback : Callable, delay : float, type : CustomLine return container + func createSection(sectionName : String, BtnCallback : Callable, settings : LabelSettings = BASE_LABEL_SETTINGS) -> HSplitContainer : var container := HSplitContainer.new() @@ -173,19 +179,35 @@ func createSection(sectionName : String, BtnCallback : Callable, settings : Labe func cleanMenu() -> void: - if waveTabContainer.get_child_count() > 0: - for child in waveTabContainer.get_children(): - child.queue_free() + for child in waveTabContainer.get_children(): + child.free() + + +func addTroop(toWave : Wave) -> void: + toWave.troops.append(Troop.new()) + cleanAndBuildMenu() + + +func removeTroop(troop : Troop, fromWave : Wave) -> void: + fromWave.troops.erase(troop) + cleanAndBuildMenu() + + +func cleanAndBuildMenu() -> void: + cleanMenu() + buildTree() func removeWave() -> void: level.waves.remove_at(currentWave) - waveTabContainer.get_child(currentWave).queue_free() + waveTabContainer.get_child(currentWave).free() func addWave() -> void: - level.waves.append(Wave.new()) - cleanAndBuildMenu() + var wave := Wave.new() + level.waves.append(wave) + buildWave(wave) + waveTabContainer.select_next_available() func changeWaveOrder(newPos : int) -> void: @@ -202,52 +224,28 @@ func changeWaveOrder(newPos : int) -> void: level.waves = newWaveOrder - -func addTroop(toWave : Wave) -> void: - toWave.troops.append(Troop.new()) +func selectLevel(levelPath : String) -> void : + level = load(levelPath) 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) + 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, LEVEL_PATH + "/" + levelSelect.selectedValue, ResourceSaver.FLAG_BUNDLE_RESOURCES) + ResourceSaver.save(level, levelSelect.selectedValue, ResourceSaver.FLAG_BUNDLE_RESOURCES) func onSelectedTowerChange(index: int, selected: int) -> void: @@ -257,3 +255,15 @@ func onSelectedTowerChange(index: int, selected: int) -> void: 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 diff --git a/addons/LevelEditor/wave_maker.tscn b/addons/LevelEditor/wave_maker.tscn index 516cc80..236a4fa 100644 --- a/addons/LevelEditor/wave_maker.tscn +++ b/addons/LevelEditor/wave_maker.tscn @@ -36,32 +36,16 @@ 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 @@ -69,6 +53,10 @@ layout_mode = 2 layout_mode = 2 text = "Ajouter une vague" +[node name="RemoveWave" type="Button" parent="VBoxContainer2/ButtonContainer"] +layout_mode = 2 +text = "Suprimer la vague" + [node name="ButtonContainer2" type="HBoxContainer" parent="VBoxContainer2"] layout_mode = 2 @@ -79,28 +67,6 @@ layout_mode = 2 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="MarginContainer2" type="MarginContainer" parent="VBoxContainer2"] -custom_minimum_size = Vector2(0, 30) -layout_mode = 2 - [node name="ButtonContainer5" type="HBoxContainer" parent="VBoxContainer2"] custom_minimum_size = Vector2(0, 30) layout_mode = 2 @@ -112,13 +78,15 @@ select_mode = 2 allow_search = false auto_width = true auto_height = true -item_count = 6 -item_0/text = "PIERRE" -item_1/text = "ALINE" -item_2/text = "MAXENCE" -item_3/text = "VICTORIA" -item_4/text = "EVAN" -item_5/text = "ALEX" +item_count = 7 +max_columns = 8 +item_0/text = " PIERRE " +item_1/text = " ALINE " +item_2/text = " MAXENCE " +item_3/text = " VICTORIA " +item_4/text = " EVAN " +item_5/text = " ALEX " +item_6/text = " GERALDINE " [node name="MarginContainer4" type="MarginContainer" parent="VBoxContainer2"] custom_minimum_size = Vector2(0, 30) @@ -131,18 +99,17 @@ size_flags_vertical = 3 [node name="WaveContainer" type="TabContainer" parent="VBoxContainer2/ScrollContainer"] layout_mode = 2 size_flags_vertical = 3 +current_tab = 0 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="pressed" from="VBoxContainer2/ButtonContainer/RemoveWave" to="." method="removeWave"] [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="multi_selected" from="VBoxContainer2/ButtonContainer5/TowerSelector" to="." method="onSelectedTowerChange"] [connection signal="active_tab_rearranged" from="VBoxContainer2/ScrollContainer/WaveContainer" to="." method="changeWaveOrder"] [connection signal="tab_changed" from="VBoxContainer2/ScrollContainer/WaveContainer" to="." method="tabFocusHaschanged"] +[connection signal="tab_hovered" from="VBoxContainer2/ScrollContainer/WaveContainer" to="." method="test"]