fix: nettoyer les créatures mortes pendant un combat multi-ennemis (#40) #46

Merged
Reliodas merged 1 commit from fix/40-cleanup-dead-combatants into develop 2026-06-20 11:34:47 +00:00
Owner

Closes #40

Pendant une rencontre à plusieurs ennemis, une créature morte restait occupante de sa case dans TacticalGrid jusqu'à la fin complète du combat ; World._process ignorait son jeton (qui pouvait garder sa teinte « sélectionné ») ; et si l'ennemi primaire mourait, le panneau de PV ennemi retombait sur lui faute de sélection.

Changements

  • CombatController._cleanup_dead_combatants() : nettoyage centralisé et idempotent appelé après chaque attaque du héros, après manager.begin_turn() (mort de début de tour) et après chaque tour ennemi. Parcourt les ennemis morts, grid.remove(creature) libère leur case (déplacement/pathfinding ne sont plus bloqués) et invalide la sélection. N'utilise pas le signal creature_died (aucune dépendance de vue) ; grid.remove est idempotent (_positions.has) ⇒ pas de double retrait.
  • Enemy.set_defeated(on) : teinte grisée estompée, prioritaire sur targetable/selected. World._process appelle set_defeated(true) pour un participant mort au lieu de l'ignorer.
  • CombatController.first_living_enemy() + CombatOverlay._enemy_panel_target : le panneau de PV suit la cible sélectionnée si elle est vivante, sinon un ennemi vivant (jamais un primaire mort tant qu'un autre survit).
  • Sélection : select_target/can_attack refusent déjà une cible morte ; l'invalidation après mort passe par _cleanup_dead_combatants_validate_selected_target.

Critères d'acceptation

  • une créature morte libère sa case tactique pendant que le combat continue ;
  • son jeton est neutralisé visuellement sans attendre la fin du combat ;
  • elle ne peut plus être sélectionnée ni bloquer déplacement/pathfinding ;
  • le panneau ennemi affiche la cible sélectionnée ou, à défaut, un ennemi vivant ;
  • aucun signal ou événement de mort ne provoque de double retrait (grid.remove idempotent, pas de creature_died) ;
  • tests couvrent mort de l'ennemi primaire avec un second vivant, occupation de case et état panneau.

Tests

  • tests/unit/test_combat_controller.gd : test_dead_primary_frees_cell_and_panel_falls_back_to_living (case libérée, sélection effacée, repli vivant, combat continue), test_dead_enemy_cleaned_outside_apply_attack (mort hors _apply_attack nettoyée à la transition de tour), test_cleanup_dead_combatants_is_idempotent.
  • Suite GUT complète verte : 166 tests, 531 assertions.
Closes #40 Pendant une rencontre à plusieurs ennemis, une créature morte restait occupante de sa case dans `TacticalGrid` jusqu'à la fin complète du combat ; `World._process` ignorait son jeton (qui pouvait garder sa teinte « sélectionné ») ; et si l'ennemi primaire mourait, le panneau de PV ennemi retombait sur lui faute de sélection. ## Changements - `CombatController._cleanup_dead_combatants()` : nettoyage **centralisé et idempotent** appelé après chaque attaque du héros, après `manager.begin_turn()` (mort de début de tour) et après chaque tour ennemi. Parcourt les ennemis morts, `grid.remove(creature)` libère leur case (déplacement/pathfinding ne sont plus bloqués) et invalide la sélection. N'utilise pas le signal `creature_died` (aucune dépendance de vue) ; `grid.remove` est idempotent (`_positions.has`) ⇒ pas de double retrait. - `Enemy.set_defeated(on)` : teinte grisée estompée, **prioritaire** sur targetable/selected. `World._process` appelle `set_defeated(true)` pour un participant mort au lieu de l'ignorer. - `CombatController.first_living_enemy()` + `CombatOverlay._enemy_panel_target` : le panneau de PV suit la cible sélectionnée si elle est **vivante**, sinon un **ennemi vivant** (jamais un primaire mort tant qu'un autre survit). - Sélection : `select_target`/`can_attack` refusent déjà une cible morte ; l'invalidation après mort passe par `_cleanup_dead_combatants` → `_validate_selected_target`. ## Critères d'acceptation - [x] une créature morte libère sa case tactique pendant que le combat continue ; - [x] son jeton est neutralisé visuellement sans attendre la fin du combat ; - [x] elle ne peut plus être sélectionnée ni bloquer déplacement/pathfinding ; - [x] le panneau ennemi affiche la cible sélectionnée ou, à défaut, un ennemi vivant ; - [x] aucun signal ou événement de mort ne provoque de double retrait (`grid.remove` idempotent, pas de `creature_died`) ; - [x] tests couvrent mort de l'ennemi primaire avec un second vivant, occupation de case et état panneau. ## Tests - `tests/unit/test_combat_controller.gd` : `test_dead_primary_frees_cell_and_panel_falls_back_to_living` (case libérée, sélection effacée, repli vivant, combat continue), `test_dead_enemy_cleaned_outside_apply_attack` (mort hors `_apply_attack` nettoyée à la transition de tour), `test_cleanup_dead_combatants_is_idempotent`. - Suite GUT complète verte : **166 tests, 531 assertions**.
fix: nettoyer les créatures mortes pendant un combat multi-ennemis (#40)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
01a57497c0
Une créature morte restait occupante de sa case jusqu'à la fin du combat, son
jeton gardait sa teinte, et un primaire mort restait le repli du panneau de PV.
Nettoyage centralisé et idempotent CombatController._cleanup_dead_combatants()
(après attaque, begin_turn et tour ennemi) : grid.remove libère la case et la
sélection est invalidée. Enemy.set_defeated neutralise le jeton (grisé) sans
attendre la fin. Le panneau se replie sur first_living_enemy(). grid.remove étant
idempotent, pas de double retrait (sans recourir au signal creature_died).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reliodas merged commit 71f0e26b91 into develop 2026-06-20 11:34:47 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: jeux/beaulieu-sur-brume#46
No description provided.