Release 0.15.0 — Le monde devient le champ de bataille #25

Merged
Reliodas merged 81 commits from release/0.15.0 into main 2026-06-22 02:18:08 +00:00
Owner

Release 0.15.0 : clôture du jalon « Le monde devient le champ de bataille ».

Le combat se joue désormais en place dans World, sans arène séparée ni changement de scène.

Contenu du jalon

  • Cycle exploration ↔ combat en place (EncounterFlow), gel des entrées, reprise victoire/fuite/défaite (#1, #5).
  • Overlay tactique en surimpression (#8) ; TacticalGrid issue de la zone (#3) avec invariants durcis (#15).
  • Entités de monde à identité stable + Creature persistante (#2) corrigeant le retour d'un ennemi vaincu après fuite (#16).
  • Détection / perception / hostilité (#4) ; sélection des participants et combat à plusieurs ennemis (#7).
  • Attaques à distance, portée et ligne de vue (#6) — a livré le moteur dnd55e 1.17.0 (sous-module repointé).
  • CI durcie (#14) et clôture : smoke test de bout en bout + retrait de l'ancienne arène (#9).

Version

config/version 0.15.0, CHANGELOG.md section [0.15.0], titre README.md v0.15.0 (étape version-coherence verte).

Validation

Suite complète 147/147. Validation manuelle GUI (entrées, caméra, reprise immédiate) à confirmer en lançant le jeu.

Release **0.15.0** : clôture du jalon « Le monde devient le champ de bataille ». Le combat se joue désormais **en place** dans `World`, sans arène séparée ni changement de scène. ## Contenu du jalon - Cycle exploration ↔ combat en place (`EncounterFlow`), gel des entrées, reprise victoire/fuite/défaite (#1, #5). - Overlay tactique en surimpression (#8) ; `TacticalGrid` issue de la zone (#3) avec invariants durcis (#15). - Entités de monde à identité stable + `Creature` persistante (#2) corrigeant le retour d'un ennemi vaincu après fuite (#16). - Détection / perception / hostilité (#4) ; sélection des participants et combat à plusieurs ennemis (#7). - Attaques à distance, portée et ligne de vue (#6) — a livré le moteur **dnd55e 1.17.0** (sous-module repointé). - CI durcie (#14) et clôture : smoke test de bout en bout + retrait de l'ancienne arène (#9). ## Version `config/version` **0.15.0**, `CHANGELOG.md` section `[0.15.0]`, titre `README.md` `v0.15.0` (étape version-coherence verte). ## Validation Suite complète **147/147**. Validation manuelle GUI (entrées, caméra, reprise immédiate) **à confirmer** en lançant le jeu.
Reliodas added this to the 0.15.0 milestone 2026-06-20 02:01:19 +00:00
feat: prototype foundation (milestone 0)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ee171b5db3
Wire the game project onto the dnd55e rules engine and lay down the project
scaffolding for the 2D "explore -> fight" prototype.

- Vendor the dnd55e rules engine as a submodule (bare-addon layout, mounted at
  res://addons/dnd55e/) and wire its autoloads single-level: DndEvents,
  DndRules, plus the game GameState autoload; register the EN/FR translations.
- Add the runnable Main scene/state-machine anchor (scenes/main.tscn,
  scripts/main.gd) and scripts/game_state.gd (hero Creature + scene routing).
- Project docs: README, CONTRIBUTING, LICENSE (MIT, Reza DROUIN), NOTICE,
  CLAUDE.md, docs/ARCHITECTURE.md, ROADMAP.md (7 prototype milestones).
- Conventions + tooling: Git Flow / Conventional Commits / SemVer
  (config/version 0.2.0), Woodpecker CI, .editorconfig/.gitattributes/.gitignore.

Verified: Godot import clean, engine class_names resolve (Creature,
RulesEngine...), Main scene runs, hero+goblin smoke test passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: fetch the dnd55e submodule over HTTPS in CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ea0aafa63d
The runner clones the repo over HTTPS but .gitmodules points at the SSH remote,
which the runner cannot reach (no SSH key/known_hosts -> "Host key verification
failed"). Override the submodule URL to HTTPS in the clone step so it uses the
forge netrc Woodpecker injects; .gitmodules stays SSH for local devs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: fix version-coherence sed delimiter
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
7ed5508978
config/version contains a slash, which sed read as its s/// delimiter ("bad
option in substitution expression"). Use # as the delimiter so no escaping is
needed and the YAML/shell pipeline can't mangle a backslash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the 2D top-down exploration scene the player moves around before combat.

- scenes/exploration/world.{gd,tscn}: tile map (TileMapLayer with a
  runtime-generated TileSet — flat floor/wall tiles, no art dependency) built
  from a text MAP definition; exposes is_wall() (walls + out-of-bounds).
- scenes/exploration/player.{gd,tscn}: token with 4-direction grid movement,
  wall collision (via World.is_wall), smooth per-cell tween, following Camera2D.
- scripts/main.gd: load the exploration scene on startup.
- Bump to 0.3.0 (CHANGELOG + README).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'feature/exploration' into develop
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
45b42020d8
HeroFactory builds "Léa", a level-3 Fighter, with weapon, armor and shield
pulled from the dnd55e registries (longsword / chain_mail / shield) → 28 HP,
AC 18. GameState.ensure_hero() lazily builds the hero once and keeps the same
Creature instance across scene switches, so HP and state persist. Main builds
the hero at startup before loading exploration.

GUT tests cover the level-3 build, fixed-average HP, equipment/AC, species and
background, and GameState persistence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An enemy token (scenes/exploration/enemy.tscn) is placed on the exploration map
from the grid's 'g' symbol and bound to a bestiary monster (goblin). Stepping
into an enemy's cell stores the encounter (GameState.current_encounter_id) and
switches to the combat scene via GameState.start_combat. A placeholder combat
scene reads the persisted hero and the encounter and displays them; the
turn-based loop lands in milestone 4.

GUT tests cover token placement, enemy_at lookups (floor/out-of-bounds) and that
the triggered monster id resolves in the bestiary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CombatController drives the engine's CombatManager (initiative, begin_turn/
end_turn, is_combat_over) for a hero-vs-monster encounter. The player turn is
Attack (RulesEngine.resolve_weapon_attack with the equipped weapon) or Dodge
(RulesEngine.take_dodge_action), chosen by keyboard; the enemy turn is a simple
AI that attacks the hero via RulesEngine.resolve_multiattack. Teams are opposed
so is_combat_over detects win/lose, and damage taken persists on the hero
(foundation for the milestone-6 return loop). The combat scene is a minimal view
(text log + action prompt); HP panels, the DndEvents-fed log and the polished
end screen land in milestone 5.

GUT tests cover opposed teams, the out-of-turn guard, a full seeded combat run
to victory, and engine-resolved Dodge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audit complet du prototype (forme + fond) : implémentation jugée viable,
aucun refactor structurel nécessaire. Resynchronisation de la documentation
restée en retard sur le code :
- docs/ARCHITECTURE.md : architecture réelle (flux de scènes, classes du jeu,
  journal DndEvents, autoloads, limites & points à vérifier)
- AGENTS.md : architecture actuelle, tests existants, état & directions
- ROADMAP.md : jalons marqués livrés (v0.2.0 → v0.9.0)
- CLAUDE.md / README.md (état + contrôles) / CONTRIBUTING.md : état réel
- CHANGELOG.md : entrée [Unreleased]

Aucun changement de code ; suite GUT 35/35 inchangée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Issu de l'audit : limites structurelles actuelles (clé de rencontre par
cellule seule, position non restaurée, carte codée en dur) et ordre
recommandé des fondations (identité de carte, persistance, données).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase A (jalon 8). Rencontres identifiées par clé composite
map_id/encounter_id (corrige la collision inter-cartes), position du
joueur mémorisée par carte et restaurée au retour de combat, et
sérialisation d'état (GameSave + GameState.to_save/apply_save) réutilisant
la sérialisation .tres de Creature du moteur.

Correctif: combat.gd lit current_encounter_monster (id de monstre séparé
de l'id de rencontre stable). Tests: test_world_foundations.gd (clé
composite, position par carte, round-trip sauvegarde mémoire + disque).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase A — fondations du monde: identité de carte, clé de rencontre
composite, position persistante par carte, sérialisation d'état (GameSave).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase A (jalon 9). Autoload SaveManager: écrit/relit des GameSave (.tres)
sous user://saves/ (emplacements manuels nommés, autosave, list/has/delete),
au-dessus de la logique pure GameState.to_save/apply_save (tout l'I/O disque
vit dans SaveManager).

Menu principal (main.tscn/main.gd en Control): Nouvelle partie / Continuer
(dernière autosave) / Charger / Quitter. Autosave au retour à l'exploration
(end_combat). Sauvegarde manuelle F5 (emplacement horodaté) avec message à
l'écran. Tests: test_save_manager.gd (round-trip, listing, autosave, delete).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase A — système de sauvegarde: SaveManager (slots manuels + autosave),
menu principal Nouvelle partie/Continuer/Charger, sauvegarde manuelle F5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Définit la prochaine phase post-prototype : fondations + sauvegarde (A,
livrée v0.10.0→v0.11.0), création de perso (B), combat tactique sur grille
(C), horloge in-game (D), inventaire + monnaie (E), PNJ/dialogues/boutiques
(F). Précise l'ordre A→B→C→D→E→F et la frontière moteur/jeu. Met à jour la
section hors-périmètre (sauvegarde désormais livrée). FR + EN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase A (jalon 10, complète). Resource ZoneData (map_id + layout +
encounter_monsters) et zone par défaut data/zones/beaulieu_proto.tres.
World se construit à partir d'une ZoneData choisie via
GameState.current_map_id (repli sur la zone par défaut), le layout et les
rencontres ne sont plus codés en dur — prérequis du multi-cartes.

World n'expose plus MAP/MAP_ID/ENEMY_MONSTER_ID. Tests: test_zone_data.gd.
ROADMAP: Phase A marquée complète (jalons 8–10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase B (jalon 11). Écran de création (nom, espèce, classe, historique,
caractéristiques + ASI d'historique) construisant le héros via
CharacterBuilder à partir des registries du moteur ; HeroFactory conservé
en preset quick_start. GameState.begin_new_game démarre une partie sans
héros (la création le renseigne) ; le menu Nouvelle partie ouvre la
création. Tests: test_character_builder.gd, test_character_creation.gd,
couverture begin_new_game.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase C (jalon 12). TacticalGrid (modèle pur cases de 1,5 m : positions,
atteignabilité par budget de déplacement, portée de contact, pas glouton
IA). CombatController gagne une couche tactique optionnelle (setup_grid,
player_move, can_player_attack au contact, player_end_turn, IA d'approche) ;
sans grille, comportement abstrait inchangé. Scène de combat repensée :
grille + jetons, surbrillance des cases atteignables, menu Attaquer /
Se déplacer / Esquiver / Fin du tour, sélection souris.

Le moteur ne suit pas la position : tout le spatial est côté jeu. Tests :
test_tactical_grid.gd, scénarios tactiques du contrôleur, smoke scène.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: planifie le combat en monde et les groupes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
edd57455c4
ci: corrige la détection des tests GUT
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ac597db8f1
docs: documente l’accès Forgejo pour les agents
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
c45b9d6803
feat: introduit le cycle exploration/combat dans World (#1)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
33b62056bb
Remplace la transition vers combat_scene.tscn par une machine à états jouée
en place dans World, sans changement de scène : World et ses Node restent
vivants pendant la rencontre.

- EncounterFlow (RefCounted) : phases explicites EXPLORING / ENGAGING /
  FIGHTING / RESUMING, démarrage du CombatController (combat abstrait, sans
  grille) et tours ennemis automatiques.
- World câble le contact sur EncounterFlow, gèle le déplacement libre et la
  sauvegarde manuelle pendant le combat, retire le jeton vaincu et
  autosauvegarde à la victoire.
- player suspend ses entrées hors exploration.

Hors périmètre, délégué aux tickets sœurs : grille spatiale depuis la carte
(#3), UI tactique et pilotage du tour joueur (#8), reprise défaite/fuite (#5),
retrait de l'ancien chemin combat_scene.tscn (#9).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: affiche l'interface de combat en surimpression dans World (#8)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
805248b64b
Extrait le HUD de combat dans un CombatOverlay (CanvasLayer réutilisable)
affiché au-dessus de la carte d'exploration conservée, sans recréer la caméra
ni la carte.

- CombatOverlay : barre d'actions (Attaquer / Esquiver / Second Wind / Fin du
  tour), panneaux de PV, journal, prompt et écran de fin, branchés sur le
  CombatController actif via bind() ; unbind() masque l'overlay et coupe toutes
  les connexions (aucun résidu d'entrée hors combat).
- World instancie l'overlay (masqué), le lie au contrôleur à l'entrée en combat
  (phase FIGHTING) et le retire au retour à l'exploration.
- EncounterFlow se maintient en RESUMING jusqu'à l'acquittement de l'écran de
  fin (acknowledge_resume), déclenché par [Entrée] dans l'overlay.

Hors périmètre, repris en #3 : bouton « Se déplacer », sélection et surbrillance
des cases (tributaires d'une TacticalGrid, absente en combat abstrait).

Tests : suite complète 93/93 — smoke test de l'overlay (instanciation, cycle de
visibilité, routage des actions, absence de résidu d'entrée) et mise à jour des
tests EncounterFlow/World pour la reprise acquittée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: construit TacticalGrid depuis la carte et active le déplacement (#3)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
0137776e46
Fait de la zone réelle la source unique du spatial tactique, au lieu d'une
arène générée, et rend le déplacement du joueur jouable en combat.

- TacticalGrid.from_zone(zone) : dimensions et murs dérivés du layout. World
  délègue sa collision (is_wall) à cette grille — exploration et combat
  partagent la même source ; la cellule monde == cellule tactique (identité).
- World place les combattants depuis leurs positions courantes, passe la grille
  au CombatController, synchronise les jetons depuis la grille (source unique
  des positions) et fait reprendre l'exploration au héros sur sa case de fin de
  combat. TacticalGrid.remove() nettoie l'occupation à la reprise.
- Déplacement joueur : l'overlay rétablit le bouton « Se déplacer » (signal
  move_mode_changed) ; World dessine la surbrillance des cases atteignables
  (ReachHighlight) et gère le clic-pour-bouger.

Tests : suite complète 98/98 — construction depuis une zone, obstacles,
occupation/remove, cohérence murs/positions exploration↔combat, placement des
combattants, synchronisation de la position du héros, bouton de déplacement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: définit la reprise après victoire, défaite ou fuite (#5)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
dc0052329f
Donne une politique explicite à chaque sortie de rencontre intégrée au monde,
sans reconstruire la carte. EncounterFlow.Outcome remplace le booléen de
victoire par trois issues.

- Victoire : ennemi retiré et marqué vaincu (persiste en sauvegarde), reprise à
  la position finale du héros, autosave. (inchangé sur le fond)
- Fuite : bouton « Fuir » dans l'overlay → EncounterFlow.flee() ; l'ennemi non
  vaincu reste sur la carte à sa case de fin de combat (tables d'exploration
  réalignées dessus, source unique), repli sur place, autosave. Il se réengage
  au contact.
- Défaite : rechargement de la dernière autosave (retour au menu principal à
  défaut) — politique explicite, pas de transition fantôme.
- Nettoyage systématique à la sortie : overlay délié, signaux déconnectés,
  occupation de la grille libérée, surbrillance effacée.

GameState.clear_current_encounter() clôt une rencontre sans la marquer vaincue.

Tests : suite complète 104/104 — issues victoire/défaite/fuite au niveau du
flow, fuite et persistance save/load au niveau de World, bouton Fuir de
l'overlay. Le rechargement de scène de la défaite n'est pas exercé sous GUT
(convention du dépôt) mais couvert par l'issue DEFEAT du flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: durcit le pipeline (import strict, suite obligatoire, smoke test) (#14)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
70441ef096
Supprime les faux verts possibles de la CI.

- Import Godot bloquant : la sortie est inspectée et toute SCRIPT ERROR /
  Parse Error fait échouer le pipeline (le garde `test -d .godot` reste).
- Une suite de tests vide (aucun tests/**/test_*.gd) échoue explicitement,
  plutôt que d'être ignorée.
- GUT garde -gexit (code non nul si un test échoue) et écrit un rapport JUnit
  (test_results.xml, ajouté au .gitignore).
- Nouveau smoke test headless tests/unit/test_boot.gd : démarrage sans erreur
  du menu principal et de World (tourne dans l'étape GUT, donc en CI).
- Étape version-coherence conservée.

Suite complète 106/106.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: décrit les créatures du monde comme entités structurées dans ZoneData (#2)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
135cce5ccb
Sort les entités de la zone du simple mapping caractère→monster_id vers des
données structurées, avec une identité stable indépendante de la position.

- WorldEntityData (Resource) : monster_id, faction/hostilité, portée de vue
  (paramètre de perception consommé au #4).
- ZoneData.entities (caractère → WorldEntityData) remplace encounter_monsters ;
  beaulieu_proto.tres migré (sous-ressource gobelin hostile).
- WorldEntity (runtime) : identité STABLE dérivée de la case d'apparition
  (== ancienne clé "x_y", donc défaites sauvegardées compatibles, aucune
  migration), position courante, données partagées.
- World instancie depuis ces données, tient un registre d'entités et n'engage
  le combat qu'avec une entité hostile (gating de faction ; détection fine #4).
- Enemy porte son entity_id.

La persistance complète de la Creature et le re-keying de la défaite par
entity_id (correctif du bug de fuite/rechargement) sont le #16.

Tests : suite complète 111/111 — modèle de données, identité stable et distincte,
instanciation depuis la zone, gating d'une entité neutre, assertion de zone mise
à jour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: empêche le retour d'un ennemi vaincu après une fuite et un rechargement (#16)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
fec0658f11
Le bug : un ennemi en fuite changeait de case ; l'identité de rencontre étant
dérivée de la cellule, une victoire ultérieure marquait la mauvaise case et
ZoneData recréait l'ennemi à son origine au chargement. Sa Creature était aussi
reconstruite à chaque engagement, restaurant ses PV.

Correctif (sur les entités stables de #2) :
- L'identité de rencontre suit l'entity_id STABLE de l'entité (indépendant de la
  case courante) : start_encounter pose current_encounter_id = ent.entity_id, et
  la victoire marque l'entité.
- WorldEntity porte une Creature PERSISTANTE (construite une fois) ; le combat la
  réutilise (CombatController accepte une Creature fournie via EncounterFlow), si
  bien que les PV survivent à une fuite.
- État persistant des entités vivantes ayant divergé (position + PV) sérialisé
  dans GameSave.entity_state_by_map et restauré par World à l'entrée sur la carte
  → survit au save/load. World libère cet état à la victoire.

Tests : suite complète 115/115 — régression complète (fuite, déplacement,
victoire, rechargement), PV conservés au réengagement, position+PV de fuite
survivant au save/load, identité stable au réengagement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: durcit les invariants de déplacement et de placement de TacticalGrid (#15)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
5a6096a8a0
- Coupe-coin diagonal interdit : un pas diagonal n'est permis que si aucune des
  deux cases orthogonales traversées ne bloque (mur/hors-limites). Règle de
  franchissement extraite dans _can_step et partagée par reachable_cells() et
  step_toward().
- place() valide explicitement la cible : refuse hors-limites, mur et occupation
  par une autre créature (renvoie un booléen), et ne corrompt jamais les tables
  position/occupation (replacer une créature sur sa propre case reste permis).

Tests : suite complète 119/119 — coins (une/deux orthogonales murées), partage
de la règle par step_toward, placements invalides, intégrité d'un déplacement ;
le test d'atteignabilité avec mur reflète désormais les coins bloqués.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: modélise la détection, la perception et l'hostilité (#4)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
239a804915
Couche de logique pure, sans Node ni changement de scène, déterminant quand une
créature en perçoit une autre et si la perception doit déclencher une rencontre.

- TacticalGrid.has_line_of_sight(a, b) : tracé de Bresenham, un mur entre les
  deux coupe la vue (les créatures ne masquent pas). Partagé avec les attaques à
  distance (#6).
- Perception (statique) : can_detect (portée en mètres + ligne de vue, portée
  par défaut si non renseignée) et should_engage (détection ET hostilité). La
  détection, l'hostilité (faction) et l'engagement sont distincts : une cible
  neutre détectée n'engage pas le combat. Vision omnidirectionnelle (les entités
  du monde n'ont pas d'orientation).

Foundation consommée par #7 (sélection des participants détectés) et #6 (portée
à distance partageant la même ligne de vue).

Tests : suite complète 128/128 — portée, masquage par mur, vue ignorant les
occupants, séparation détection/hostilité/engagement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: sélectionne les participants et gère le combat à plusieurs ennemis (#7)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
ff5675e933
Le cycle combat-en-monde n'engageait qu'un duel 1v1. Ce ticket rassemble les
hostiles d'une rencontre depuis le monde et généralise le combat à N ennemis,
tout en gardant le cas 1v1 inchangé.

- EncounterSetup.select_hostiles (couche pure) : à partir de l'agresseur,
  rassemble les hostiles vivants à portée d'engagement (9 m) et en ligne de
  vue ; écarte neutres, morts et hors-vue. Testable sans scène.
- CombatController : add_enemy/place_enemy, équipe ennemie partagée, ennemi
  primaire conservé pour la compat ; ciblage des seuls ennemis adjacents
  (_attackable_targets) et multiattaque résolue par stat block (_data_by_enemy).
- EncounterFlow.begin_encounter accepte des extra_hostiles (data/creature/cell).
- World place les participants à leurs cases du monde, suit leurs jetons, et
  applique victoire comme fuite à chacun (mark_entity_defeated).
- Tests : sélection pure (test_encounter_setup) + cas multi-ennemis du
  contrôleur. Suite complète verte (138 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: attaques à distance, portée et ligne de vue en combat monde (#6)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
1f3444ff28
Rend les armes à distance utilisables sur la grille de la zone et partage la
ligne de vue avec la détection (#4).

- CombatController.can_attack(attacker, target) : API commune mêlée/distance.
  Mêlée = contact ; distance = portée (longue incluse) + ligne de vue
  (TacticalGrid.has_line_of_sight). Le ciblage du joueur et l'IA s'en servent.
- Portée longue → désavantage, appliqué par le moteur via le nouveau paramètre
  de RulesEngine.resolve_weapon_attack (dnd55e 1.17.0), sans le réimplémenter.
- IA ennemie : tire à distance quand le héros est en vue plutôt que de foncer
  au contact (le gobelin emploie son arc court) ; sinon se rapproche puis tire
  s'il est à portée.
- World : jetons attaquables mis en surbrillance au tour du joueur
  (Enemy.set_targetable).
- Sous-module dnd55e 1.16.0 -> 1.17.0 (param advantage/disadvantage).
- Tests : test_ranged_combat (murs, diagonales, portée longue + désavantage,
  mêlée au contact) ; IA à distance et mêlée dans test_combat_controller.
  Suite complète verte (146 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: clôture du flux combat-en-monde et retrait de l'ancienne arène (#9)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
eae32235d4
Sécurise le jalon 0.15.0 par un parcours automatisé de bout en bout et supprime
les dépendances devenues obsolètes à l'arène de combat séparée.

- Smoke test headless (tests/unit/test_world_combat_smoke.gd) : chargement de
  World, détection hostile, engagement au contact, initiative, action joueur,
  tours ennemis, victoire et reprise en place ; plus une régression save/load.
- Suppression de l'ancien chemin : scenes/combat/combat_scene.tscn,
  scenes/combat/combat.gd (+ test), et le routage GameState.start_combat /
  end_combat + la constante COMBAT_SCENE. Aucune rencontre ordinaire n'appelle
  plus change_scene vers une arène. hp_panel/damage_float conservés (overlay).
- Commentaires de l'ancien flux retirés (test_combat_trigger).
- Docs : ARCHITECTURE (combat en place), README (FR+EN), ROADMAP (0.15.0 livré),
  CHANGELOG.

Suite complète verte (147 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore(release): 0.15.0 — Le monde devient le champ de bataille
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_metadata/woodpecker Pipeline was successful
f92e0c2cda
Bump config/version 0.14.0 -> 0.15.0, titre README, et section CHANGELOG
[Unreleased] -> [0.15.0]. Clôt le jalon « Le monde devient le champ de
bataille » : combat joué en place dans World (détection, sélection des
participants, grille issue de la zone, overlay, plusieurs ennemis, attaques à
distance/portée/ligne de vue), sans arène séparée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: choisir une action ennemie compatible avec portée et ligne de vue (#28)
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
ff8394b275
L'IA résolvait toujours actions[0] (cimeterre du gobelin) via
resolve_multiattack, même autorisée à tirer. Nouveau filtre pur
CombatController._valid_monster_attacks (mêlée au contact, distance à portée
longue + ligne de vue, désavantage au-delà de la portée normale) ; can_attack
s'y appuie pour les monstres et _enemy_take_turn résout action par action via
resolve_monster_action. Le gobelin frappe au cimeterre au contact, tire à l'arc
à distance, et ne fait rien si la ligne de vue est coupée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: déclencher les rencontres depuis la perception réelle dans World (#26)
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
6bc6b28957
World ne consultait jamais la couche Perception : la rencontre ne démarrait
qu'au contact. Nouvelle méthode World.evaluate_perception() qui engage tout
hostile percevant le héros (portée + ligne de vue), appelée à la fin du tween
de déplacement du joueur et une fois après l'initialisation (appel différé,
pas de boucle _process). Verrou de désengagement transitoire (_disengaged,
hors sauvegarde) contre la boucle de réengagement après une fuite : levé dès
que la perception est rompue. Le contact reste un filet de sécurité.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: permettre au joueur de choisir sa cible parmi plusieurs ennemis (#27)
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
a11914f2aa
Le bouton Attaquer appelait player_attack() sans cible, le contrôleur frappant
la première cible valide ; le panneau de PV ennemi restait lié au primaire.
Ajout de CombatController.selected_target + select_target() (cible attaquable
seulement) ; player_attack(target) refuse une cible invalide/morte/hors
portée/hors vue sans consommer l'Action, la cible par défaut restant l'unique
ennemi attaquable (1v1 inchangé), et la sélection s'invalide après
déplacement/mort/changement de tour. World convertit le clic gauche en cellule
(_creature_at_cell) pour sélectionner le jeton attaquable ; Enemy.set_selected
ajoute une surbrillance distincte ; l'overlay transmet la cible et fait suivre
le panneau de PV ennemi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: rendre la validation import Godot réellement stricte (#30)
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
3b111f23d6
L'étape import neutralisait le code retour de Godot (|| true) et ne cherchait
que SCRIPT ERROR / Parse Error. Désormais le code de sortie est capturé
explicitement, la détection couvre les erreurs fatales usuelles (chargement de
script/ressource/scène, fichier introuvable, dépendances manquantes) sans
sur-filtrer, et un import réussi exige le cache .godot ET l'absence d'erreur
détectée. Seul cas bénin toléré et documenté : code non nul sur cache froid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: resynchroniser la documentation avec la sortie 0.15.0 (#31)
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
0d40021f7c
Remplace les décomptes de tests périssables (77 tests / 266 assertions) de
README (FR+EN) et AGENTS par un renvoi à la dernière sortie GUT/CI ; retire des
« Limites actuelles » (ARCHITECTURE + AGENTS) l'absence de ligne de vue et
d'attaques à distance, désormais présentes, et décrit l'IA réelle ; présente
0.15.0 comme livré/en publication et 0.16.0 comme prochain jalon dans AGENTS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
merge: intégrer les correctifs post-validation (#26 #27 #28 #30 #31) dans release/0.15.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
c77b5e3f04
ci: faire échouer la CI sur les codes d'import Godot non reconnus (#43)
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
ef45e06ab0
Le correctif #30 tolérait tout code de sortie non nul de `godot --import` dès que
le cache .godot existait et qu'aucun motif fatal ne matchait, contredisant son
commentaire (code 42 -> faux succès). La décision est isolée dans un script POSIX
committé et auto-testé ci/check_godot_import.sh : diagnostic fatal -> échec même
avec code 0, cache absent -> échec, tout code non nul -> échec par défaut (aucune
exception bénigne ; une future exigerait une signature précise). Test shell
ci/check_godot_import_test.sh exécuté en CI avant l'import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: empêcher le réengagement immédiat après rechargement d'une fuite (#42)
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
b945d4db40
Le verrou de désengagement de #26 (World._disengaged) ne vivait qu'en mémoire ;
une fuite autosauve aussitôt avec héros et hostile en perception mutuelle, donc
au rechargement l'appel différé à evaluate_perception relançait le combat.
entity_state_by_map (qui survit au save/load) ne contient que des fuyards :
World._ready reverrouille toute entité dont l'état est restauré, avant l'appel
différé. Le verrou tombe à la rupture de perception, le contact réengage
toujours, et une réacquisition réengage. Isolation des tests World renforcée
(nettoyage de entity_state_by_map).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
fix: appliquer le désavantage aux attaques à distance au contact (#37)
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
0c26ced76d
Le désavantage à distance ne couvrait que la portée longue. Nouveau helper
spatial CombatController._ranged_contact_disadvantage : un adversaire vivant et
non incapacité (RulesEngine.is_incapacitated) adjacent à l'attaquant impose le
désavantage. Appliqué au héros (_attack_has_disadvantage) et aux monstres
(branche RANGED de _valid_monster_attacks). Portée longue et contact se
combinent en un seul désavantage booléen ; la mêlée n'est pas affectée ; la
résolution reste déléguée au moteur.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
test: couvrir l'intégration visuelle et souris du ciblage (#41)
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
a3ed9ced27
Isole le chemin d'entrée du clic-pour-cibler dans `World.select_target_at_cell`
(résolution du jeton + `CombatController.select_target`), pour le tester sans
synthétiser caméra ni souris ; `_unhandled_input` y délègue.

Ajoute `tests/unit/test_world_combat_targeting.gd` : sélection par cellule, clic
sur cellule vide conservant la sélection, états visuels du jeton (neutre /
attaquable / sélectionné / vaincu via `_process` et priorité de teinte), suivi
et repli du panneau de PV ennemi sur un ennemi vivant (recoupe #40), et filtrage
souris de la barre d'actions.

La barre d'actions (`ActionBar`) intercepte désormais aussi les clics sur ses
espaces vides (`mouse_filter = STOP`), pas seulement ses boutons : aucun clic
dans sa zone ne traverse vers la carte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
merge: intégrer la 2e vague de correctifs (#43 #42 #40 #37 #41) dans release/0.15.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
19ae878be3
Resynchronise release/0.15.0 depuis develop après la validation manuelle :
- #43 validation d'import Godot strict (script POSIX auto-testé)
- #42 plus de réengagement immédiat après rechargement d'une fuite
- #40 nettoyage des créatures mortes en combat multi-ennemis
- #37 désavantage des attaques à distance au contact (héros + monstres)
- #41 couverture visuelle/souris du ciblage + filtrage de la barre d'actions

Le contenu [Unreleased] est replié sous [0.15.0] (section [Unreleased] vide).
Version 0.15.0 conservée ; suite GUT complète verte (183 tests, 578 assertions),
import strict rc=0, test shell d'import vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: corrige les derniers angles morts de la 0.15.0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
b0e42659ae
fix: synchronise le menu de combat avec le personnage actif
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
9849059d13
merge: intégrer les derniers correctifs dans release/0.15.0
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
645399b731
# Conflicts:
#	CHANGELOG.md
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#25
No description provided.