sotc-battle-show-ability : affichage des capacités en combat

This commit is contained in:
Yasser Boulayoune 2026-05-01 16:26:00 +02:00
parent 84aa6b3437
commit e35a3996ee
11 changed files with 1363 additions and 0 deletions

View file

@ -0,0 +1,160 @@
# sotc-clash-arrow — Spécification Technique v1.0
## Vue d'ensemble
Module FoundryVTT v11 permettant de tracer des flèches courbées entre les **Dés de Vitesse** du module *Stars Initiative Overlay* (SIO) de Vani, pour visualiser les clashes dans *Star of the City* (SotC).
S'appuie sur le module **SBSA (clash-resolver)** pour lire les compétences assignées aux dés.
---
## Dépendances
| Module | Rôle |
|---|---|
| `stars-initiative-overlay` | Fournit les marqueurs PIXI des Dés de Vitesse |
| `clash-resolver` (SBSA) | Fournit les compétences stockées par dé via flags |
---
## Logique des couleurs
| Couleur | Condition |
|---|---|
| **Bleu** `#4a9eff` | A cible B, mais B n'a **pas** de compétence active sur son dé |
| **Jaune** `#ffd84a` | A→B **ET** B→A existent simultanément (clash mutuel automatique) |
| **Gris** `#888899` | Flèche de preview pendant le drag (avant relâchement) |
La détection du clash mutuel est **automatique** : dès que la flèche A→B est créée alors que B→A existe déjà, les **deux** flèches passent en jaune.
---
## Interactions utilisateur
### Tracer une flèche
1. Maintenir **clic gauche** sur un Dé de Vitesse SIO
2. Déplacer la souris vers un autre dé — la flèche preview change de couleur en temps réel selon la cible survolée
3. Relâcher sur le dé cible → la flèche devient persistante
**Qui peut tracer ?** Tous les utilisateurs (GM et joueurs), depuis n'importe quel token.
### Supprimer une flèche
- **Clic droit** sur un Dé de Vitesse → supprime toutes les flèches depuis/vers ce dé
### Reset global
- Automatique au début d'un nouveau round (hook `updateCombat` + hook SIO `sioRoundReset`)
- Automatique à la fin du combat (`deleteCombat`)
- Manuel via le bouton **GM** dans la barre de contrôles de scène (icône `×`)
---
## Architecture des fichiers
```
sotc-clash-arrow/
├── module.json
├── styles/
│ └── sotc-clash-arrow.css
├── lang/
│ ├── fr.json
│ └── en.json
└── scripts/
├── main.js ← point d'entrée
├── Hooks.js ← enregistrement des hooks FoundryVTT
├── ArrowStore.js ← store centralisé + sync socket
├── ClashArrowLayer.js ← couche PIXI (dessin des flèches)
├── DieInteraction.js ← gestion du drag souris
└── SIOBridge.js ← intégration SIO + détection compétences SBSA
```
---
## Description des modules JS
### `main.js`
- Point d'entrée unique chargé par `module.json`
- Expose `globalThis.sotcClashArrow` pour débogage console
- Vérifie la présence du module SIO au `ready`
### `ArrowStore.js`
- Store en mémoire (`Map<id, Arrow>`)
- Détecte automatiquement le clash mutuel à chaque `addArrow()`
- Synchronise via `game.socket` (événement `module.sotc-clash-arrow`) pour que tous les clients voient les mêmes flèches
- Émet un événement `change` → `ClashArrowLayer` se redessine
**Structure d'une flèche :**
```js
{
id : "tokenA:0→tokenB:1",
fromToken : "tokenA_id",
fromDie : 0,
toToken : "tokenB_id",
toDie : 1,
x1, y1 : number, // coords canvas du centre du dé source
x2, y2 : number, // coords canvas du centre du dé cible
mutual : boolean, // true = flèche jaune
}
```
### `ClashArrowLayer.js`
- Hérite de `CanvasLayer` (FoundryVTT v11)
- `zIndex: 180` → au-dessus des tokens (100), sous l'UI (900+)
- Deux `PIXI.Graphics` séparés : flèches persistantes + preview
- Courbe de Bézier cubique avec pointe de flèche trigonométrique
- `BEZIER_CURVE = 0.35` : facteur de courbure ajustable
### `DieInteraction.js`
- Écoute `pointerdown` (via hook interne `sotcca:diePointerDown`) → démarre le drag
- `pointermove` → met à jour la preview avec la couleur calculée en temps réel
- `pointerup` → crée la flèche dans `ArrowStore` si une cible valide est sous le curseur
### `SIOBridge.js`
- `getSIODieCenter()` : lit `token._initiativeMarkers[i].getBounds()` pour les coords canvas
- `findDieUnderPoint()` : parcourt tous les tokens/marqueurs, hit-test à ±24px
- `tokenHasActiveSkillOnDie()` : combine état SIO (`diceStates[i] !== 0`) ET présence d'une compétence SBSA (`flag "clash-resolver" → "dieSkills"[i]`)
- `hookSIOMarkers()` : attache les listeners sur les marqueurs PIXI existants + futurs (via hook `refreshToken`)
### `Hooks.js`
- `init` : enregistrement du layer dans `CONFIG.Canvas.layers`
- `canvasReady` : init socket + interactions + hook SIO markers
- `canvasTearDown` : `ArrowStore.clear()`
- `updateCombat` (nouveau round) + `sioRoundReset` + `deleteCombat` → reset
- `getSceneControlButtons` : bouton reset GM
- `sotcca:mutualClash` : message chat signalant un clash mutuel
---
## Chemins de données supposés (à vérifier)
> ⚠️ Ces chemins sont des hypothèses basées sur l'analyse du module SIO et SBSA.
> À valider lors des tests avec les modules installés.
| Donnée | Chemin supposé |
|---|---|
| Marqueurs PIXI SIO | `token._initiativeMarkers[]` |
| États des dés SIO | `tokenDoc.getFlag("stars-initiative-overlay", "diceStates")` → `int[]` |
| Nombre de dés | `actor.system.speed_dice.num_dice` |
| Compétences par dé (SBSA) | `tokenDoc.getFlag("clash-resolver", "dieSkills")` → `string[]` |
| Hook reset SIO | `"sioRoundReset"` (à confirmer dans la source SIO) |
---
## Synchronisation multi-clients
Toutes les modifications du store sont broadcastées via `game.socket.emit("module.sotc-clash-arrow", payload)`.
Les autres clients reçoivent le payload et appliquent la même modification localement sans re-broadcaster, pour éviter les boucles.
**Actions socket :**
- `add` : nouvelle flèche (+ mise à jour éventuelle de la flèche inverse)
- `remove` : suppression d'une flèche (+ dés-mutualisation de l'inverse)
- `clear` : reset total
---
## Notes d'implémentation
- Le layer PIXI est enregistré dans `CONFIG.Canvas.layers` au hook `init`, avant la construction du canvas
- Les listeners sur les marqueurs SIO sont attachés avec un délai de 50ms après `refreshToken` pour laisser SIO finir de recréer ses containers PIXI
- Un marqueur déjà hookié est marqué `marker._sotcca_hooked = true` pour éviter les doublons
- La flèche de preview utilise `alpha = 0.55` pour la distinguer des flèches persistantes (`alpha = 0.92`)