sotc-foundry-suite/sotc-clash-arrow/sotc-clash-arrow-spec.md

160 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`)