117 lines
5.3 KiB
GDScript
117 lines
5.3 KiB
GDScript
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
|
|
GameData.battle_started.emit() #les elements d'interface (TeamHUD...) se cachent ici, avant la pause du monde
|
|
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 : tous les pokemon sortis rentrent dans leur balle ; l'adversaire du monde reapparait
|
|
for node in get_tree().get_nodes_in_group(SentPokemon.GROUP):
|
|
if node is SentPokemon: node.recall()
|
|
for node in hidden:
|
|
if is_instance_valid(node) and !(node is SentPokemon): node.visible = true
|
|
|
|
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
|