160 lines
6.3 KiB
Markdown
160 lines
6.3 KiB
Markdown
# 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`)
|