|
JustAnotherDnDGame 0.1.0
Jeu de rôle tactique au d20, vue de dessus, en C++/Qt
|
Statut : fait (vérification automatisée : build /W4 /WX sans avertissement, ctest à 1047/1047, clang-format, les huit lints, cahier de test et Doxygen verts). Prérequis : LOT-05 (modes de jeu), LOT-10 (déclencheurs et drapeaux de monde), LOT-13 (fiches des combattants).
Aucune exigence ajoutée : EX-CBT-001 couvre ce lot, et c'est son premier vrai emploi.
Déclencher une rencontre depuis l'exploration, geler le monde, monter les combattants, et en revenir — sans que le joueur perde quoi que ce soit au passage.
Il livre la bascule, pas le combat. Ni initiative, ni tour actif, ni résolution d'action : ce sont les LOT-19 et LOT-20. Séparer les deux n'est pas un découpage administratif — un aller-retour qui perd la position du personnage est un défaut qu'on ne voit plus une fois qu'il y a des tours à jouer par-dessus, et qu'on n'aurait jamais isolé.
Le critère d'acceptation demandait que le montage et le démontage d'une rencontre se testent headless. Ils le sont, parce que rien de ce qui décide n'est dans un widget :
hmi::CombatMode, lui, ne fait qu'ordonner des passes. Il ne retient rien de la rencontre : la tentation était de lui faire porter l'état, et l'aller-retour serait alors devenu invérifiable sans fenêtre.
Il porte la position du personnage, son orientation et la caméra. Sans mémoire, le personnage reviendrait de son combat ailleurs qu'il ne l'avait quitté — un pas de côté à chaque rencontre, invisible une fois, gênant au dixième — et regardant une direction par défaut (EX-EXP-004).
Il ne porte pas les points de vie, et c'est délibéré. Le critère dit « restituer exactement l'état d'exploration, aux PV près » : les points de vie sont précisément ce que le combat a changé, et les remettre à leur valeur d'avant annulerait le combat. Les inclure « pour être complet » aurait été le bogue, pas la prudence.
Il ne porte pas non plus les ennemis vaincus : cela ne se restitue pas, cela s'acquiert.
C'est le piège que le LOT-10 avait déjà nommé pour les coffres, et il se pose identiquement ici : l'entité de l'ennemi est détruite et recréée depuis la couche objects au rechargement de la carte, et un booléen porté par elle disparaîtrait avec elle. L'ennemi réapparaîtrait à chaque passage — un défaut qui ne casse rien, ne lève aucune alerte, et se confond avec une carte peuplée.
core::WorldFlags porte donc le fait, sous une clé fabriquée par core::keyForEntity — jamais écrite à la main, sinon deux ennemis finiraient par la partager. Le test le vérifie dans les deux sens : deux cases d'une même carte, et la même case sur deux cartes.
Une fuite ramène à l'exploration sans marquer l'ennemi vaincu, et une défaite non plus. Poser le drapeau à la sortie, quelle qu'elle soit, aurait fait de la fuite un moyen de nettoyer une carte — et le défaut ne se serait pas vu : la carte se serait vidée, ce qui ressemble à une progression.
Une entité de type encounter nomme sa rencontre (encounterId). Ce qui la distingue n'est pas son type mais une propriété :
Confondre les deux se paierait dans les deux sens : un ennemi sans clé réapparaîtrait à chaque passage, et une zone avec clé s'éteindrait au premier combat gagné. C'est pourquoi encounterAlreadyCleared répond non pour une clé vide : l'inverse aurait désactivé toutes les zones du jeu dès le premier combat.
Les positions des combattants sont relatives au déclencheur. Une rencontre est écrite une fois et jouée partout : trois rats « un pas devant » gardent leur formation quel que soit l'endroit, alors que des coordonnées absolues les feraient apparaître au même endroit à chaque fois — ou hors de la carte.
core::placeCombatants rend la liste des cases voulues, sans consulter ni carte ni collision : c'est au montage de refuser celles qui tombent dans un mur, et il ne peut le faire que s'il les reçoit toutes.
Trois passes de l'exploration disparaissent, chacune pour une raison précise :
| Passe retirée | Pourquoi |
|---|---|
| moveCharacter | le déplacement suit le budget du tour (LOT-19), pas l'intention libre du joueur ; la laisser donnerait un combat où l'on marche pendant le tour d'un autre |
| updateMechanisms | une plaque de pression qui s'enfoncerait au milieu d'un tour ferait dépendre le combat d'une règle qu'aucun livre ne décrit |
| evaluateOutcome | tomber à zéro point de vie est une issue du combat, pas du niveau ; l'évaluer ici rechargerait le niveau au lieu d'ouvrir l'agonie (LOT-72) |
Ce qui reste tourne parce que le combat demeure une scène : particules et secousse d'écran finissent ce qu'elles ont commencé, les animations continuent — un combattant immobile respire — et la caméra suit. Un test compare la séquence réellement appelée à celle que le mode annonce, et vérifie qu'aucune passe gelée n'y figure.
Rpg/encounters/nuee-de-rats.json : trois bêtes du SRD (LOT-33), seules créatures livrées à ce jour. Déclarée provisoire avec son critère de retrait — elle disparaît quand le contenu du LOT-27 fournira ses propres rencontres.