class_name BattleLauncher extends Node #lance les combats dans le monde. A ajouter UNE FOIS a ta scene de jeu (node "BattleLauncher"). # #- ecoute GameData.battle_requested (lancer d'un pokemon sur un AimTarget qui a un "Encounter") #- start_encounter() sert aussi a un dresseur qui defie le joueur (dialogue, zone de detection...) # #pendant le combat : le monde est en pause (GameData.in_battle = true), une camera de combat prend le relais, #puis tout est restitue a la fin. signal battle_started signal battle_ended(outcome : int) @export var heal_team_on_defeat : bool = true #provisoire : evite de bloquer le joueur si toute son equipe est K.O. var _running : bool = false func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS GameData.battle_requested.connect(_on_battle_requested) func _on_battle_requested(sent : SentPokemon, target : Node) -> void: if _running: push_warning("[BattleLauncher] un combat est deja en cours") return if !(target is AimTarget): push_warning("[BattleLauncher] la cible n'est pas un AimTarget") return var aim_target : AimTarget = target if aim_target.encounter == null: push_warning("[BattleLauncher] cet AimTarget n'a pas d'Encounter (inspecteur : Battle > Encounter)") return await start_encounter(aim_target.encounter, aim_target.global_position, sent, aim_target.world_actor, aim_target) func start_encounter(encounter : BattleEncounter, enemy_pos : Vector3, sent : SentPokemon = null, world_actor : Node3D = null, target : AimTarget = null) -> Battle.Outcome: #sent = pokemon lance par le joueur (il passe en premier) ; null si le joueur est defie : l'ordre de l'equipe s'applique if _running or encounter == null: push_warning("[BattleLauncher] start_encounter ignore (combat deja en cours ou Encounter vide)") return Battle.Outcome.NONE #camera et acteurs doivent etre dans le monde que le joueur voit (SubViewport eventuel du jeu) var world_viewport : Viewport = get_viewport() var world_root : Node = world_viewport if world_viewport is SubViewport else get_tree().current_scene var game_camera : Camera3D = world_viewport.get_camera_3d() var player_pos : Vector3 = sent.global_position if (sent != null and is_instance_valid(sent)) else _find_player_position() var ui := BattleUI.new() get_tree().root.add_child(ui) #directement sous la fenetre : toujours affiche par-dessus tout var setup := BattleSetup.new() setup.can_flee = encounter.can_flee var lead : PokemonInstance = sent.pokemon if (sent != null and is_instance_valid(sent)) else null setup.add(BattleSetup.player_participant(GameData.player_team, encounter.player_slots, lead, PlayerBattleController.new(ui))) for participant in encounter.build_participants(): setup.add(participant) if !setup.is_valid(): var details : PackedStringArray = PackedStringArray() for participant in setup.participants: details.append("%s (camp %d, %d pokemon aptes)" % [participant.name, participant.side, participant.team.get_able_pokemon().size()]) push_warning("[BattleLauncher] combat invalide : chaque camp doit avoir un participant avec un pokemon apte. Participants : " + ", ".join(details) + ". Verifie que l'Encounter contient bien une espece (wild_pokemon / trainer) et que ton equipe a un pokemon non K.O.") ui.queue_free() return Battle.Outcome.NONE _running = true GameData.in_battle = true get_tree().paused = true battle_started.emit() #les pokemon sortis et l'adversaire du monde sont caches : les acteurs du combat les remplacent var hidden : Array[Node3D] = [] for node in get_tree().get_nodes_in_group(SentPokemon.GROUP): if node is Node3D and node.visible: node.visible = false hidden.append(node) if world_actor != null and world_actor.visible: world_actor.visible = false hidden.append(world_actor) var presenter := BattleWorldPresenter.new(ui) var battle := Battle.new(setup, presenter) presenter.setup(battle, player_pos, enemy_pos, world_root, game_camera) var outcome : Battle.Outcome = await battle.run() await presenter.cleanup() ui.queue_free() #retour au monde for node in hidden: if is_instance_valid(node): node.visible = true for node in get_tree().get_nodes_in_group(SentPokemon.GROUP): if node is SentPokemon and node.pokemon != null and node.pokemon.is_fainted(): node.recall() #un pokemon K.O. est rappele if world_actor != null and is_instance_valid(world_actor): if outcome == Battle.Outcome.PLAYER_WON and encounter.remove_world_actor_on_win: world_actor.queue_free() if outcome == Battle.Outcome.PLAYER_LOST and heal_team_on_defeat: GameData.player_team.heal_all() GameData.in_battle = false get_tree().paused = false _running = false if target != null and is_instance_valid(target): target.battle_finished.emit(outcome) GameData.battle_finished.emit(outcome, target) battle_ended.emit(outcome) return outcome func _find_player_position() -> Vector3: #ajoute ton PlayerCharacter au groupe "player" (sinon la camera du jeu sert de repere) var player : Node3D = get_tree().get_first_node_in_group("player") as Node3D if player != null: return player.global_position var cam : Camera3D = get_viewport().get_camera_3d() return cam.global_position if cam != null else Vector3.ZERO