Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7de32431b6 | ||
|
|
099ce03774 | ||
|
|
344123b304 | ||
|
|
9b5c7834c0 | ||
|
|
86fc41accd | ||
|
|
a3cb1b0329 | ||
|
|
d63eeda299 | ||
|
|
be43913ae3 | ||
|
|
f9ef1d48a4 | ||
|
|
5be1365038 | ||
|
|
89413c17a4 | ||
|
|
15f8e927f0 | ||
|
|
7382723e75 | ||
|
|
3a84ef09f6 | ||
|
|
edd1819c86 | ||
|
|
9ab2c6b01a | ||
|
|
0ef0de45ff | ||
|
|
c7e6660d9d | ||
|
|
9f88a66b72 | ||
|
|
61b7815381 | ||
|
|
44fbc592ad | ||
|
|
ef01651b1d | ||
|
|
8dd5d02994 | ||
|
|
ef86b2637c | ||
|
|
2f78d1a1ea | ||
|
|
7ec3d73915 | ||
|
|
88e2902068 | ||
|
|
70a97796a3 | ||
|
|
d499f60386 | ||
|
|
1c7e05bae6 | ||
|
|
06603ceff7 | ||
|
|
ca74ff4a88 |
@@ -61,3 +61,16 @@ make build / watch / lint # Makefile wrappers for above
|
|||||||
- **No tests**: `npm test` is a stub (exits 1). No test framework installed.
|
- **No tests**: `npm test` is a stub (exits 1). No test framework installed.
|
||||||
- **No CI**: `.github/` is gitignored; no workflows configured.
|
- **No CI**: `.github/` is gitignored; no workflows configured.
|
||||||
- **HotReload**: `system.json` flags hotReload for css, scss, hbs, json extensions.
|
- **HotReload**: `system.json` flags hotReload for css, scss, hbs, json extensions.
|
||||||
|
|
||||||
|
|
||||||
|
## Constraints and remarks
|
||||||
|
|
||||||
|
Foundryv14 only : foundryvtt.com/api/
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
With chrome-devtools on port 9222
|
||||||
|
|
||||||
|
World is called "Vermine", thru https://localhost:31000, with Gamemaster user logged in (no password needed)
|
||||||
|
|
||||||
|
Always try to re-use existing chrome-devtools session.
|
||||||
|
|||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
# Plan de test — Combat Vermine2047 (rejouable)
|
||||||
|
|
||||||
|
Base : règles de combat (Kit de Survie p.44-52, `regles/2047_KitDeSurvie-DEF.txt`) et moteur `VermineExchange`/`CombatDialog`/`VermineCombatTracker`.
|
||||||
|
Ce plan décrit le **setup reproductible**, les **scénarios**, les **valeurs attendues** (déterministes : pools, difficultés, formules) et la **procédure** de validation. Les jets de dés restent aléatoires ; on valide la mécanique, pas un résultat chiffré précis.
|
||||||
|
|
||||||
|
## Prérequis
|
||||||
|
- Monde Vermine2047 sur `https://localhost:31000`, connecté en **Gamemaster**.
|
||||||
|
- Scène « AA » (`49tnX0ynTzztVkxy`) avec tokens PJ / PNJ (+ token Créature ajouté).
|
||||||
|
- Dés en main : chaque jet via l'UI crée une carte de chat (animations 3D possibles).
|
||||||
|
|
||||||
|
## Acteurs (setup reproductible)
|
||||||
|
|
||||||
|
| Acteur | Type | ID | Réglages |
|
||||||
|
|--------|------|----|----------|
|
||||||
|
| **Personnage** | PJ (character) | `f6qo5ubEnrSrtcfM` | Vigueur 3, Précision 2, Réflexes 2 · Mêlée 3, Corps-à-corps 2, Armes à feu 3 · Equipé : TEST Epée (lame 3), TEST Fusil (balle 4, mun. 10, 5/50 m), TEST Gilet (Protection 2, Mobilité 1) |
|
||||||
|
| **PNJ** | PNJ (npc) | `EsksoLF7SV6yx7ZM` | Menace 2 (Attaque 4), Expérience 3 (contact « 5,7 ou 9 », action 4), Rôle 3 (Protection 3) · Equipé : TEST Hache (lame 3), TEST Armure (Protection 3) |
|
||||||
|
| **Créature** | Créature | `4CCdIWBHSqgApmlR` | Attaque 7, Dommages 5, Vigueur 4, Protection 2, Statut combat Difficulté 7 · Equipée : TEST Morsure (choc 4 + Vigueur) |
|
||||||
|
|
||||||
|
### Pools de dés attendus (SkillLevels : lvl2→pool1/reroll1, lvl3→pool2/reroll1)
|
||||||
|
| Jet | Pool | Difficulté |
|
||||||
|
|-----|------|-----------|
|
||||||
|
| PJ contact (Épée, Mêlée 3) | 3 + 2 = **5D** | 5 / 7 / 9 |
|
||||||
|
| PJ distance (Fusil, AF 3) | 2 + 2 = **4D** | Portée : courte 5 / moyenne 7 / longue 9 |
|
||||||
|
| PJ esquive | 2 + 1 = **3D** | = difficulté attaque |
|
||||||
|
| PJ parade | 3 + 2 = **5D** | = difficulté attaque |
|
||||||
|
| PNJ attaque (Menace 2) | **4D** | 5 / 7 / 9 |
|
||||||
|
| PNJ défense | **4D** (action) | = difficulté attaque |
|
||||||
|
| Créature attaque (Morsure) | **7D** | = statut combat (7) |
|
||||||
|
| Créature défense | **passive** (pas de jet, difficulté verrouillée 7) | — |
|
||||||
|
|
||||||
|
### Seuils de blessure (netDamage)
|
||||||
|
| Acteur | Légère | Grave | Mortelle |
|
||||||
|
|--------|--------|-------|----------|
|
||||||
|
| PJ | ≥ 1 | ≥ 4 | ≥ 8 |
|
||||||
|
| PNJ | ≥ 2 | ≥ 5 | ≥ 8 |
|
||||||
|
| Créature | ≥ 2 | ≥ 4 | ≥ 6 |
|
||||||
|
|
||||||
|
### Formules de résolution (vérifiées par le moteur)
|
||||||
|
- Dégâts bruts = **Dommages de base + réussites de l'attaquant**.
|
||||||
|
- Dégâts nets = max(0, bruts − Protection du défenseur). La Protection est la somme des **défenses équipées** (PNJ : TEST Armure 3 ; la « Protection » de rôle n'est PAS une défense équipée). Parade : seule la protection utilisée compte.
|
||||||
|
- Blessure : catégorie dont le seuil est ≤ dégâts nets (la plus haute).
|
||||||
|
- Défense : ≥ réussites attaquant → paré/esquivé (0 dégât) ; 1+ de plus → touché.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scénarios
|
||||||
|
|
||||||
|
### S1 — Création du combat et initiative
|
||||||
|
1. Créer un combat « TEST COMBAT » ; ajouter les combatants PJ + PNJ + Créature.
|
||||||
|
2. `rollAll()` : toutes les initiatives sont jetées (formule `Xd10cs>=7`).
|
||||||
|
- **Attendu** : combatantCount = 3 ; chaque combatant a une initiative non nulle ; `turn`/`round` cohérents après `startCombat`.
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S2 — Attaque PJ → PNJ au contact (Épée)
|
||||||
|
1. Cibler le token PNJ. Ouvrir l'attaque via l'icône « Attaque » de la fiche PJ (équipement → TEST Epée).
|
||||||
|
2. Vérifier le pool **5D** (Vigueur 3 + Mêlée) et les difficultés **5/7/9**.
|
||||||
|
3. Lancer (difficulté 7). Défense PNJ : **Esquive** (4D).
|
||||||
|
4. « Résoudre » : dégâts bruts = 3 + réussites PJ ; nets = bruts − 3 (Protection PNJ).
|
||||||
|
- **Attendu** : pool affiché 5D, difficulté 7 ; résolution = formules ci-dessus ; si touché → blessure Légère (≥2) / Grave (≥5) / Mortelle (≥8).
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S3 — Attaque PJ → Créature à distance (Fusil)
|
||||||
|
1. Cibler le token Créature. Attaque via TEST Fusil.
|
||||||
|
2. Vérifier le pool **4D**, difficulté de Portée (choisir Moyenne = 7).
|
||||||
|
3. Lancer. La créature **ne peut pas défendre** (défense passive ; pas de bouton Esquive/Parade sur sa ligne).
|
||||||
|
- **Attendu** : pool 4D, difficulté 7 ; carte créature = « Défense passive » ; résolution = 4 + réussites − 2 (Protection créature) ; seuils créature 2/4/6.
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S4 — Attaque PNJ → PJ (Hache)
|
||||||
|
1. Cibler le token PJ. Attaque via la Hache du PNJ.
|
||||||
|
2. Vérifier le pool **4D** (Menace 2) et les difficultés **5/7/9**.
|
||||||
|
3. Défense PJ : **Parade** (5D, Vigueur + Mêlée) — choisir Parade.
|
||||||
|
- **Attendu** : pool 4D ; parade 5D ; si touché → 3 + réussites − 2 (TEST Gilet) ; seuils PJ 1/4/8.
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S5 — Attaque Créature → PJ (Morsure)
|
||||||
|
1. Cibler le token PJ. Attaque via TEST Morsure de la créature.
|
||||||
|
2. Vérifier le pool **7D**, difficulté **verrouillée 7** (statut combat).
|
||||||
|
3. Défense PJ : **Esquive** (3D).
|
||||||
|
- **Attendu** : pool 7D, difficulté 7 verrouillée ; dégâts de base = 4 + Vigueur 4 = **8** ; nets = 8 + réussites − 2 ; seuils PJ 1/4/8.
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S6 — Traits de combat
|
||||||
|
- **Rafale (3)** : case « Tir en rafale » sur le Fusil → difficulté +2 (7→9) ; si jet réussi, +3 réussites affichées (« réussites automatiques ») ; si échec, pas de bonus.
|
||||||
|
- **Rapide (2)** : arme avec Rapide → note « le défenseur subit 2 Handicap(s) » ; la défense du PNJ s'ouvre avec handicap **II** pré-rempli.
|
||||||
|
- **Lourd (4)** : équiper un objet Lourd 4 au PJ (Vigueur 3 < 4) → malus **-1D** dans le pool (dialogue + ligne « Malus Lourd »).
|
||||||
|
- **Portée (8)** : arme Portée 8, Vigueur 4 → « Portée effective : 32 m » affichée.
|
||||||
|
- **Incapacitant (7)** : sur la carte d'échange, bouton « Jet de Santé (7) » → ouvre un jet de Santé vs 7.
|
||||||
|
- **Zone (6)** : note « Zone (6 m) » + bouton « Appliquer aux cibles de la zone ».
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S7 — Tracker (gestion)
|
||||||
|
1. Bouton **+** (createCombat) : crée un nouveau combat.
|
||||||
|
2. Clic droit sur un combatant → menu contextuel (Mettre à jour / Effacer / Relancer / **Supprimer**) ; le bouton poubelle par combatant supprime aussi.
|
||||||
|
3. Poubelle d'en-tête (deleteCombat) : supprime le combat.
|
||||||
|
4. Footer : **Commencer** (startCombat), tours (←/→), rounds (←/→), **Terminer** (endCombat).
|
||||||
|
- **Validation** : ⬜
|
||||||
|
|
||||||
|
### S8 — Nettoyage
|
||||||
|
- Supprimer les combats de test, les items « TEST * », restaurer les compétences PJ (0), retirer le token Créature.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rapport d'exécution
|
||||||
|
Chaque scénario : note **PASS** si les valeurs attendues sont vérifiées, **FAIL** sinon (avec l'écart). Les chiffres aléatoires (dés) sont relevés tels quels.
|
||||||
|
|
||||||
|
### S1 — Combat & initiative — **PASS**
|
||||||
|
3 combatants ajoutés ; initiatives jetées (PJ 1, PNJ 0, Créature 0) ; `startCombat` → round 1 ; ordre du tour = initiative décroissante (Personnage, PNJ, Créature).
|
||||||
|
|
||||||
|
### S2 — PJ→PNJ contact (Épée) — **PASS**
|
||||||
|
- Dialogue : pool **5D** (Vigueur 3 + Mêlée 3), difficultés **[5,7,9]**, dégâts de base 3.
|
||||||
|
- Attaque (difficulté 7) → 2 réussites. Défense PNJ **Esquive 4D** (action) → 0 réussite.
|
||||||
|
- Résolution : **Touché**, bruts 3+2=5, nets 5 (PNJ sans défense équipée → 0 protection) → **Blessure grave** (seuil 5).
|
||||||
|
- Application : `majorWound` 0→1, « Blessure appliquée ».
|
||||||
|
- Note : la « Protection 3 » du rôle PNJ n'est pas une défense équipée → non déduite.
|
||||||
|
|
||||||
|
### S3 — PJ→Créature distance (Fusil) — **PASS**
|
||||||
|
- Dialogue : pool **4D** (Précision 2 + AF 3), difficulté **verrouillée 7** (cible créature).
|
||||||
|
- Créature : pas de bouton défense → « Défense passive ».
|
||||||
|
- Résolution : attaque 3 réussites → **Touché**, bruts 4+3=7 − protection 2 = **5** → **Blessure grave** (seuil créature 4).
|
||||||
|
|
||||||
|
### S4 — PNJ→PJ (Hache) — **PASS**
|
||||||
|
- Dialogue PNJ : pool **4D** (Menace 2), difficultés **[5,7,9]**.
|
||||||
|
- Défense PJ **Parade** : pool **5D** (Vigueur 3 + Mêlée 3), difficulté 7, compétence Mêlée.
|
||||||
|
- Résolution : attaque 2, défense 2 → **Paré, aucun dégât** (réussites défense ≥ attaque).
|
||||||
|
|
||||||
|
### S5 — Créature→PJ (Morsure) — **PASS**
|
||||||
|
- Dialogue : pool **7D** (attaque), difficulté **verrouillée 7** (statut combat), dégâts de base **8** (4 + Vigueur 4).
|
||||||
|
- Défense PJ **Esquive** : pool **3D** (Réflexes 2 + Corps-à-corps 2), difficulté 7.
|
||||||
|
- Résolution (défense 0) : **Touché**, bruts 8+4=12 − protection 2 = **10** → **Blessure mortelle** (seuil 8).
|
||||||
|
- Bonus : le **malus de blessure** d'une créature blessée est bien appliqué (deadlyWound=1 → −3D, pool 7→4).
|
||||||
|
|
||||||
|
### S6 — Traits de combat — **PASS**
|
||||||
|
- **Rafale (3)** : case → difficulté +2 (7→9) ; jet raté → **aucun bonus** (total 0) ; jet réussi → **+3 réussites** (total = brut + 3, ligne « réussites automatiques »).
|
||||||
|
- **Rapide (2)** : note sur la carte ; dialogue de défense → handicap **II** pré-rempli.
|
||||||
|
- **Lourd (4)** : Vigueur 3 < 4 → malus **−1D** (pool breakdown « Malus Lourd »).
|
||||||
|
- **Portée (8)** : portée effective = 8 × Vigueur 3 = **24 m**.
|
||||||
|
- **Incapacitant (7)** / **Zone (6)** : boutons « Jet de Santé » / « Appliquer aux cibles de la zone » présents sur la carte.
|
||||||
|
|
||||||
|
### S7 — Tracker — **PASS** (onglet combat actif requis)
|
||||||
|
Bouton **+** → nouveau combat ; suppression combatant (bouton poubelle + menu contextuel clic-droit) ; footer **Commencer** → round 1 ; boutons tours/rounds ; **Terminer** → round 0 ; poubelle d'en-tête → suppression du combat.
|
||||||
|
|
||||||
|
### S8 — Nettoyage — **PASS**
|
||||||
|
Combats de test supprimés, items « TEST * » retirés, compétences PJ restaurées, token Créature retiré.
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Plan d'automatisation des Traits — Suivi
|
||||||
|
|
||||||
|
Base : `regles/regles_traits.txt` (et KitDeSurvie p.70-71, 39).
|
||||||
|
Implémenté déjà : `cassant`, `coque` (absorption), `etanche` (immersion) dans `module/system/reliability.mjs`.
|
||||||
|
Décisions utilisateur : Fétiche +1D sur les **deux** Réserves · Malus/Durée = **saisie manuelle** · Handicap de Mobilité = **note de rappel** · Items génériques = **flag équipé OK**.
|
||||||
|
|
||||||
|
## Légende
|
||||||
|
- [x] fait
|
||||||
|
- [ ] à faire
|
||||||
|
- [~] en cours
|
||||||
|
|
||||||
|
## Tâche 0 — Prérequis communs
|
||||||
|
- [x] `item.mjs` : champ `equipped` sur items génériques + toggle fiche `item-item-sheet.hbs`
|
||||||
|
- [x] nouveau `module/system/traits.mjs` : helpers partagés (`equippedItems`, `lourdMalus`, `mobilityNote`, `feticheItem`…)
|
||||||
|
|
||||||
|
## Phase 1 — Réparations (reliability.mjs + dialogue)
|
||||||
|
- [x] **Bien conçu** : succès auto en réparation classique (restauration complète) + 1ʳᵉ réparation de fortune rend +1 Fiabilité (compteur `traits.bien_conçu.value`)
|
||||||
|
- [x] **Pratique** : Handicap +I aux réparations (required +1, note sur la carte)
|
||||||
|
- [x] **Coque réparable** : épuisement → `{ value: 0, max: n }` ; mode « Réparer la coque » dans le dialogue (jet vs `coque.max`) ; affichage `(0/n)` sur la fiche
|
||||||
|
- [x] **Étanche** : rien de plus (immersion gérée ; gaz/maladie hors scope)
|
||||||
|
|
||||||
|
## Phase 2 — Combat (combatDialog.mjs, exchange.mjs)
|
||||||
|
- [x] **Lourd** : malus auto (`lourdMalus()`) dans dialogue combat + pool breakdown (ligne « Malus Lourd »)
|
||||||
|
- [x] **Rapide (n)** : handicap (n) défenseur pré-rempli (locks.handicap) + note sur carte d'échange
|
||||||
|
- [x] **Rafale (n)** : case « Tir en rafale » → difficulté +2, +n réussites si réussi (`bonusOnSuccess`)
|
||||||
|
- [x] **Portée** : portée effective × Vigueur (n × vigor) affichée (dialogue + carte)
|
||||||
|
- [x] **Maniable / Mobilité** : note de rappel dialogue combat + carte (handicap de mobilité cumulé, Maniable l'annule)
|
||||||
|
- [x] **Incapacitant (n)** : bouton « Jet de Santé (n) » sur carte d'échange (RollDialog santé vs n)
|
||||||
|
- [x] `getLabel()` : libellés caractéristiques/compétences localisés dans le tchat (bonus du jet de Santé)
|
||||||
|
|
||||||
|
## Phase 3 — Jets d'action (rollDialog.mjs + roll-dialog.hbs)
|
||||||
|
- [x] **Ponctuel (n)** : outil sélectionné → annule jusqu'à n Handicaps ; consommé au jet (décrément, Trait supprimé à 0) ; note carte
|
||||||
|
- [x] **Pratique (jets)** : outil sélectionné → +2D au lieu de +1D (badge « +2D » dans le dialogue)
|
||||||
|
- [x] **Intimidant** : case « Brandit (X) » → −1D Psychologie si compétence psychologie, sinon Relance 1D ; note carte
|
||||||
|
- [x] **Malus (n)** : champ « Malus temporaires » (saisie manuelle) déduit du pool + ligne carte
|
||||||
|
- [x] **Durée (n)** : note « effet pendant n tours de combat » sur carte de jet d'arme
|
||||||
|
- [x] Fix `roll()` : transmission des paramètres étendus (`ponctuelUsed`, `intimidant`, `effectiveRange`, `mobilityHandicap`) au message — corrige aussi l'affichage Portée/Mobilité en Phase 2
|
||||||
|
|
||||||
|
## Phase 4 — Acteur (character.mjs + fiche)
|
||||||
|
- [x] **Fétiche** : config « ou »→« et » ; +1D Effort ET Sang-Froid (max dérivé) ; alerte 2+ fétiches (bandeau fiche) ; action « Perdre le fétiche » (−2D valeurs, +1 perte max persistant `experience.feticheLoss`, retire le Trait)
|
||||||
|
- [x] **Zone (n)** : note carte d'échange + bouton « Appliquer aux cibles de la zone » (#onApplyZone : résout et applique la blessure à toutes les cibles)
|
||||||
|
|
||||||
|
## Transverse
|
||||||
|
- [x] i18n fr/en pour la Phase 1
|
||||||
|
- [x] ESLint (0 erreur sur les fichiers modifiés) + tests navigateur Phase 1 (coque, réparation coque, Bien conçu classique/fortune, Pratique, dialogue, affichage fiche)
|
||||||
|
- [x] ESLint (0 erreur sur les fichiers modifiés) + tests navigateur Phase 2 (Lourd, Rapide+défense, Rafale conditionnel, Portée, Mobilité, Incapacitant, toggle equip)
|
||||||
|
- [x] ESLint (0 erreur sur les fichiers modifiés) + tests navigateur Phase 3 (Pratique +2D, Ponctuel annulation+consommation, Intimidant psychologie/relance, Malus temporaires, Durée, transmission roll())
|
||||||
|
- [x] ESLint (0 erreur sur les fichiers modifiés) + tests navigateur Phase 4 (Fétiche helpers + UI, Zone note/bouton + #onApplyZone) — modèle réserves + champ feticheLoss effectifs au reload
|
||||||
+107
-27
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
|
"NAME": "Name",
|
||||||
|
"NONE": "None",
|
||||||
|
"ORIGIN": "Origin",
|
||||||
|
"PROFILE": "Profile",
|
||||||
|
"THEME": "Concept",
|
||||||
|
"TOTEM": "Totem",
|
||||||
"SETTINGS": {
|
"SETTINGS": {
|
||||||
"world": {
|
"world": {
|
||||||
"game_mode": {
|
"game_mode": {
|
||||||
"name": "Game Mode Selection",
|
"name": "Game Mode Selection",
|
||||||
"hint": "Just like some video games offer different modes, Vermine 2047 lets players choose their Game Mode and set the degree of realism, supernatural and dangerosity of the universe themselves."
|
"hint": "Just like some video games offer different modes, Vermine 2047 lets players choose their Game Mode and set the degree of realism, supernatural and dangerosity of the universe themselves."
|
||||||
},
|
|
||||||
"first_run": {
|
|
||||||
"name": "First Run Display",
|
|
||||||
"hint": "To help beginners get started with the Vermine 2047 system."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -63,23 +65,7 @@
|
|||||||
"roll_dice": "Roll the dice"
|
"roll_dice": "Roll the dice"
|
||||||
},
|
},
|
||||||
"TOURS": {
|
"TOURS": {
|
||||||
"ChatMessage": "<h3>Welcome to Vermine!</h3><p>If you're using this system for the first time, our shaman will guide you to your initiation rite</p> <button id=\"vermine-tour-chat-button\"> <i class=\"fas fa-person-hiking\"></i> I'm going but I'm scared </button>",
|
"ChatMessage": "<h3>Welcome to Vermine 2047!</h3><hr><p class=\"vermine-credits\"><strong>Vermine 2047</strong> — a game by Julien Blondel<br>© 2023 Studio Agate — TOTEM System — © 2023 Julien Blondel — All rights reserved.<br>Foundry VTT system developed by <a href=\"https://www.uberwald.me\">LeRatierBretonnier</a>, based on an original work by Rwanoux.</p>"
|
||||||
"Welcome": {
|
|
||||||
"Title": "Welcome to Vermine",
|
|
||||||
"Description": "Overview of the Vermine system",
|
|
||||||
"WelcomeTitle": "Welcome",
|
|
||||||
"WelcomeContent": "<p>Welcome to Vermine, survivor! Take a tour of our action zone to find your bearings, even if it's your first day among us.</p><p>At the top of the right column, you have everything you're allowed to do here: talk, see other tronches of your commu, manage your matos and that of your potes, study the maps, read the journals of the clamsés, go get us some food with your surin. Let's get into the thick of it.</p>",
|
|
||||||
"ActorTabTitle": "Populate your universe",
|
|
||||||
"ActorTabContent": "<p>Well installed, survivor? Let's not turn around the pot, if you want to stay with us, you'll have to tell us who you are, where you come from, and who you trust. Once you're ready for that, click on the \"Create an actor\" button</p>",
|
|
||||||
"CharacterCreateTitle": "Create a group",
|
|
||||||
"CharacterCreateContent": "<p>This button creates a group file, because that's where we start, in this trou! Your tronche will come later, when you create your character file. As you progress, you might want to add some info on what you see around the camp.</p>",
|
|
||||||
"CompendiumTitle": "Maps",
|
|
||||||
"CompendiumContent": "<p>You will probably need a map of the Iron Lands or a sector of the Forge. You can create your own scene for that, or you can use one of ours. Open one of these compendiums and drag a map into your scene tab (the tab that looks like a folded map).</p>",
|
|
||||||
"OracleToolTitle": "Totem",
|
|
||||||
"OracleToolContent": "<p>No game of Vermine or Starforged would be complete without oracles. This button will open a window with all the oracles of the chosen game. The oracles are also available from the character sheet.</p>",
|
|
||||||
"ToursTitle": "Next steps",
|
|
||||||
"ToursContent": "<p>You're not doing too badly! I'll leave you there, but if you have any questions, you can come find me. If it's about the mechanics, sign up for our group of speech, over there <a href=\"https://discord.gg/FqGHYvXg\">Totem Foundry Discord</a>. Otherwise, you can join the central place over here <a href=\"https://discord.gg/qejqmSxr\">Vermine Discord</a></p><p>Enjoy!</p>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"DIFFICULTY_LEVELS": {
|
"DIFFICULTY_LEVELS": {
|
||||||
"obvious": "Obvious",
|
"obvious": "Obvious",
|
||||||
@@ -137,6 +123,7 @@
|
|||||||
"rerolls": "Rerolls",
|
"rerolls": "Rerolls",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"reaction": "Reaction",
|
"reaction": "Reaction",
|
||||||
|
"reaction_bonus": "Reaction bonus",
|
||||||
"pools": "Reserves",
|
"pools": "Reserves",
|
||||||
"gear": "Gear",
|
"gear": "Gear",
|
||||||
"protection": "Protection",
|
"protection": "Protection",
|
||||||
@@ -163,18 +150,53 @@
|
|||||||
"pools": "Reserves",
|
"pools": "Reserves",
|
||||||
"dice_pool": "Dice pool",
|
"dice_pool": "Dice pool",
|
||||||
"pool_malus": "Wound penalty",
|
"pool_malus": "Wound penalty",
|
||||||
|
"pool_wound": "Wound penalty",
|
||||||
|
"pool_lourd": "Heavy penalty",
|
||||||
|
"lourd_malus_note": "Heavy penalty: -{malus}D (Vigor too low for equipped Heavy items)",
|
||||||
|
"effective_range": "Effective range",
|
||||||
|
"traits_mobility_note": "Mobility Handicap: +{handicap} (equipped non-Maniable protection)",
|
||||||
|
"rafale_check": "Burst fire (Difficulty +2, +{rafaleValue} successes if successful)",
|
||||||
|
"rapide_note": "Fast ({handicap}): the defender suffers {handicap} Handicap(s)",
|
||||||
|
"incapacitant_roll": "Health roll",
|
||||||
|
"intimidant": "Intimidating",
|
||||||
|
"intimidant_check": "Wield ({intimidantName}) — Intimidating",
|
||||||
|
"intimidant_psychology": "1D penalty (Psychology)",
|
||||||
|
"intimidant_reroll": "1D reroll",
|
||||||
|
"temporary_malus": "Temporary penalties",
|
||||||
|
"pool_temp_malus": "Temporary penalties",
|
||||||
|
"pool_psychology": "Psychology penalty",
|
||||||
|
"ponctuel": "One-shot",
|
||||||
|
"ponctuel_used": "cancels {value} Handicap(s)",
|
||||||
|
"duree": "Duration",
|
||||||
|
"duree_note": "effect for {value} combat turns",
|
||||||
|
"fetiche": "Fetish",
|
||||||
|
"fetiche_bonus": "Effort and Composure reserves +1D",
|
||||||
|
"fetiche_multiple": "A character can only possess a single Fetish object",
|
||||||
|
"fetiche_none": "No equipped Fetish object",
|
||||||
|
"fetiche_lost": "Fetish lost: reserves −2D, max −1D",
|
||||||
|
"fetiche_lost_penalty": "Fetish lost: reserves reduced by 1D",
|
||||||
|
"lose_fetiche": "Lose the fetish",
|
||||||
|
"zone_note": "Area ({value} m): everyone in the area suffers the damage",
|
||||||
|
"zone_apply": "Apply to targets in the area",
|
||||||
|
"zone_applied": "Area: {count} wound(s) applied",
|
||||||
"auto_successes": "automatic successes",
|
"auto_successes": "automatic successes",
|
||||||
"self_control": "Self-control",
|
"self_control": "Self-control",
|
||||||
"effort": "Effort",
|
"effort": "Effort",
|
||||||
"equip": "Equip",
|
"equip": "Equip",
|
||||||
"unequip": "Unequip",
|
"unequip": "Unequip",
|
||||||
"group": "Group",
|
|
||||||
"abilities": "Characteristics",
|
"abilities": "Characteristics",
|
||||||
"ability": "Characteristic",
|
"ability": "Characteristic",
|
||||||
|
"skills": "Skills",
|
||||||
|
"skill": "Skill",
|
||||||
"skills_title": "Skills",
|
"skills_title": "Skills",
|
||||||
"skill_title": "Skill",
|
"skill_title": "Skill",
|
||||||
"skill_mastery": "Mastery",
|
"skill_mastery": "Mastery",
|
||||||
|
"specificLevel": "Specific level",
|
||||||
|
"competence": "Skill",
|
||||||
|
"modes": "Game modes",
|
||||||
|
"traits_selector": "Trait selector",
|
||||||
"bonus": "Bonus",
|
"bonus": "Bonus",
|
||||||
|
"bonus_supplementary": "Supplementary bonus",
|
||||||
"penalty": "Penalty",
|
"penalty": "Penalty",
|
||||||
"reroll": "Reroll",
|
"reroll": "Reroll",
|
||||||
"equipment": "Equipment",
|
"equipment": "Equipment",
|
||||||
@@ -183,6 +205,9 @@
|
|||||||
"techniques": "Techniques",
|
"techniques": "Techniques",
|
||||||
"difficulty": "Difficulty",
|
"difficulty": "Difficulty",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
|
"add": "Add",
|
||||||
|
"delete": "Delete",
|
||||||
|
"learning": "Learning",
|
||||||
"tooling": "Equipment",
|
"tooling": "Equipment",
|
||||||
"quantity": "Quantity",
|
"quantity": "Quantity",
|
||||||
"qty": "Qty",
|
"qty": "Qty",
|
||||||
@@ -211,6 +236,47 @@
|
|||||||
"rarity_sm": "Rar.",
|
"rarity_sm": "Rar.",
|
||||||
"reliability": "Reliability",
|
"reliability": "Reliability",
|
||||||
"reliability_sm": "Fiab.",
|
"reliability_sm": "Fiab.",
|
||||||
|
"reliability_test": "Reliability test",
|
||||||
|
"reliability_repair": "Repair",
|
||||||
|
"reliability_severity": "Damage severity",
|
||||||
|
"reliability_severity_mineurs": "Minor (1D)",
|
||||||
|
"reliability_severity_graves": "Grave (3D)",
|
||||||
|
"reliability_severity_critiques": "Critical (5D)",
|
||||||
|
"reliability_immersion": "Prolonged immersion",
|
||||||
|
"reliability_damages": "Damage taken",
|
||||||
|
"reliability_state": "State",
|
||||||
|
"reliability_destroyed": "OBJECT DESTROYED / UNUSABLE",
|
||||||
|
"reliability_repair_mode": "Repair type",
|
||||||
|
"reliability_repair_classic": "Classic",
|
||||||
|
"reliability_repair_fortune": "Field repair",
|
||||||
|
"reliability_skill": "Skill",
|
||||||
|
"reliability_run": "Roll",
|
||||||
|
"reliability_successes": "Successes",
|
||||||
|
"reliability_result": "Result",
|
||||||
|
"reliability_gained": "Reliability restored: +{gained}",
|
||||||
|
"reliability_fixed": "Breakdown fixed (object functional)",
|
||||||
|
"reliability_failed": "Failure — no reliability point restored",
|
||||||
|
"reliability_need_actor": "Repair impossible: the object must belong to an actor",
|
||||||
|
"reliability_already_destroyed": "Object already destroyed or unusable",
|
||||||
|
"reliability_coque_absorbed": "Hull absorbs {absorbed} damage(s), {remaining} left",
|
||||||
|
"reliability_coque_lost": "Hull: trait exhausted — repairable (Difficulty {max})",
|
||||||
|
"reliability_coque_restored": "Hull repaired: {max} points",
|
||||||
|
"reliability_repair_coque": "Repair the hull (Difficulty {max})",
|
||||||
|
"reliability_repair_coque_label": "Hull",
|
||||||
|
"reliability_bien_concu": "Well-made: automatic success — reliability fully restored",
|
||||||
|
"reliability_bien_concu_fortune": "Well-made: +1 reliability point (first field repair)",
|
||||||
|
"reliability_auto_success": "Automatic success (Well-made)",
|
||||||
|
"reliability_pratique": "Pratique: +I Handicap",
|
||||||
|
"reliability_cassant": "Fragile: reliability losses increased by 1 point",
|
||||||
|
"reliability_etanche": "Watertight: immersion damage ignored",
|
||||||
|
"reliability_fortune_skill_bonus": "+1D — the character has the required skill ({skill})",
|
||||||
|
"reliability_fortune_skill_missing": "Required skill ({skill}) not mastered: rarity Handicap +{rarity}",
|
||||||
|
"reliability_fortune_hint": "Field repair: +1D if the character has the required skill, otherwise the rarity Handicap of that skill applies.",
|
||||||
|
"damages_state_1": "Damaged",
|
||||||
|
"damages_state_2": "Damaged",
|
||||||
|
"damages_state_3": "Faulty",
|
||||||
|
"damages_state_4": "Faulty",
|
||||||
|
"damages_state_5": "Out of service",
|
||||||
"wounds": {
|
"wounds": {
|
||||||
"name": "Wounds",
|
"name": "Wounds",
|
||||||
"threshold": "Threshold",
|
"threshold": "Threshold",
|
||||||
@@ -263,6 +329,9 @@
|
|||||||
"CharacterNamePlaceholder": "Character Name",
|
"CharacterNamePlaceholder": "Character Name",
|
||||||
"ProfilePlaceholder": "Profile",
|
"ProfilePlaceholder": "Profile",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
|
"all_characters": "All characters",
|
||||||
|
"no_group": "No group",
|
||||||
|
"include_in_phase": "Include in this phase",
|
||||||
"bonuses": "Bonuses",
|
"bonuses": "Bonuses",
|
||||||
"choose_ability": "Choose Ability",
|
"choose_ability": "Choose Ability",
|
||||||
"choose_skill": "Choose Skill",
|
"choose_skill": "Choose Skill",
|
||||||
@@ -289,7 +358,6 @@
|
|||||||
"pool_specialty": "Specialty",
|
"pool_specialty": "Specialty",
|
||||||
"pool_help": "Help",
|
"pool_help": "Help",
|
||||||
"pool_tooling": "Equipment",
|
"pool_tooling": "Equipment",
|
||||||
"pool_group": "Group",
|
|
||||||
"pool_selfControl": "Self-Control",
|
"pool_selfControl": "Self-Control",
|
||||||
"pool_pools": "Pools",
|
"pool_pools": "Pools",
|
||||||
"pool_total": "Total",
|
"pool_total": "Total",
|
||||||
@@ -303,6 +371,9 @@
|
|||||||
"die_adapted": "adapted",
|
"die_adapted": "adapted",
|
||||||
"die_successes": "successes",
|
"die_successes": "successes",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"stat": "Stat",
|
||||||
|
"adjustment": "Adjustment",
|
||||||
|
"adjustments": "Adjustments",
|
||||||
"totem_dice": "Totem Dice",
|
"totem_dice": "Totem Dice",
|
||||||
"totem_hint": "Check to use totem dice (double success possible)",
|
"totem_hint": "Check to use totem dice (double success possible)",
|
||||||
"totem_neutral": "Neutral",
|
"totem_neutral": "Neutral",
|
||||||
@@ -399,7 +470,7 @@
|
|||||||
"mode_survie": "Survival",
|
"mode_survie": "Survival",
|
||||||
"mode_cauchemar": "Nightmare",
|
"mode_cauchemar": "Nightmare",
|
||||||
"mode_apocalypse": "Apocalypse",
|
"mode_apocalypse": "Apocalypse",
|
||||||
"evolution_buy_hint": "The purchase consumes and erases all accumulated Evolution dice. Limits: Survival 5D, Nightmare 6D, Apocalypse 7D total. Level 3/4 Mutations are forbidden in Survival; only one level 4 Mutation.",
|
"evolution_buy_hint": "The purchase consumes a number of Evolution dice equal to the chosen level (the remainder is kept). Limits: Survival 5D, Nightmare 6D, Apocalypse 7D total. Level 3/4 Mutations are forbidden in Survival; only one level 4 Mutation.",
|
||||||
"buy_evolution_done": "Adaptation/Mutation acquired. Evolution dice erased.",
|
"buy_evolution_done": "Adaptation/Mutation acquired. Evolution dice erased.",
|
||||||
"error_evolution_no_name": "Provide a name for the Adaptation/Mutation.",
|
"error_evolution_no_name": "Provide a name for the Adaptation/Mutation.",
|
||||||
"error_evolution_invalid_level": "Invalid level (1 to 4).",
|
"error_evolution_invalid_level": "Invalid level (1 to 4).",
|
||||||
@@ -444,6 +515,8 @@
|
|||||||
"characteristic_hint": "Difficulty 9 + Handicap = current value. Max 3 (except Superhuman).",
|
"characteristic_hint": "Difficulty 9 + Handicap = current value. Max 3 (except Superhuman).",
|
||||||
"reserve_hint": "Difficulty = current value + age Handicap. Max 10.",
|
"reserve_hint": "Difficulty = current value + age Handicap. Max 10.",
|
||||||
"evolution_hint": "Difficulty by age + Handicap = accumulated Evolution dice. Adapted totem +1D, Human -1D.",
|
"evolution_hint": "Difficulty by age + Handicap = accumulated Evolution dice. Adapted totem +1D, Human -1D.",
|
||||||
|
"socle_competences_hint": "Socle de compétences atout: +{level} free Experience die (skill already possessed).",
|
||||||
|
"socle_competences_message": "Socle de compétences: +{level} free Experience die. Reminder: a Group companion who learns or develops with him a skill he possesses also gains 1 Experience die.",
|
||||||
"totem_ability_hint": "Prerequisites: same Totem as the character, Group level ≥ Capacity level, game mode compatible (Survival N1, Nightmare N2, Apocalypse N3).",
|
"totem_ability_hint": "Prerequisites: same Totem as the character, Group level ≥ Capacity level, game mode compatible (Survival N1, Nightmare N2, Apocalypse N3).",
|
||||||
"background_hint": "Difficulty 9. Handicap (I) if the Background is marked with an asterisk (*). GM authorization required.",
|
"background_hint": "Difficulty 9. Handicap (I) if the Background is marked with an asterisk (*). GM authorization required.",
|
||||||
"reputation_hint": "Reduce = more famous (Difficulty 10 − target score, 1 pt max per phase). Increase = fade into obscurity (requires a major Objective, Difficulty = current Reputation, +1 per Success).",
|
"reputation_hint": "Reduce = more famous (Difficulty 10 − target score, 1 pt max per phase). Increase = fade into obscurity (requires a major Objective, Difficulty = current Reputation, +1 per Success).",
|
||||||
@@ -523,8 +596,9 @@
|
|||||||
"prohibits": "Prohibits",
|
"prohibits": "Prohibits",
|
||||||
"prohibits_placeholder": "eg: Fire, Water",
|
"prohibits_placeholder": "eg: Fire, Water",
|
||||||
"rarity_0": "Common",
|
"rarity_0": "Common",
|
||||||
"rarity_1": "Rare",
|
"rarity_1": "Uncommon",
|
||||||
"rarity_2": "Very Rare",
|
"rarity_2": "Rare",
|
||||||
|
"rarity_3": "Very rare",
|
||||||
"reserves": "Reserves",
|
"reserves": "Reserves",
|
||||||
"ritual": "Ritual",
|
"ritual": "Ritual",
|
||||||
"sexes": {
|
"sexes": {
|
||||||
@@ -542,6 +616,7 @@
|
|||||||
},
|
},
|
||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"close": "Close",
|
||||||
"playMode": "Play Mode",
|
"playMode": "Play Mode",
|
||||||
"editMode": "Edit Mode"
|
"editMode": "Edit Mode"
|
||||||
},
|
},
|
||||||
@@ -979,9 +1054,14 @@
|
|||||||
"female": "Feminine"
|
"female": "Feminine"
|
||||||
},
|
},
|
||||||
"COMBAT": {
|
"COMBAT": {
|
||||||
|
"Begin": "Start Combat",
|
||||||
|
"Delete": "Delete",
|
||||||
"Encounter": "Encounter",
|
"Encounter": "Encounter",
|
||||||
|
"End": "End Combat",
|
||||||
"None": "None",
|
"None": "None",
|
||||||
"Round": "Round"
|
"NotStarted": "Not Started",
|
||||||
|
"Round": "Round",
|
||||||
|
"TurnEnd": "End Turn"
|
||||||
},
|
},
|
||||||
"SIZE_LEVELS": {
|
"SIZE_LEVELS": {
|
||||||
"tiny": "Insect",
|
"tiny": "Insect",
|
||||||
|
|||||||
+87
-24
@@ -27,10 +27,6 @@
|
|||||||
"game_mode": {
|
"game_mode": {
|
||||||
"name": "Choix du mode de jeu",
|
"name": "Choix du mode de jeu",
|
||||||
"hint": "À l’image de certains jeux vidéo proposant différents, Vermine 2047 permet aux joueurs de choisir leur Mode de jeu et de fixer eux-mêmes le degré de réalisme, de surnaturel et de dangerosité de l’univers."
|
"hint": "À l’image de certains jeux vidéo proposant différents, Vermine 2047 permet aux joueurs de choisir leur Mode de jeu et de fixer eux-mêmes le degré de réalisme, de surnaturel et de dangerosité de l’univers."
|
||||||
},
|
|
||||||
"first_run": {
|
|
||||||
"name": "Affichage du tour d'accueil",
|
|
||||||
"hint": "Pour aider les débutantEs à bien commencer avec le système Vermine 2047."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -69,23 +65,7 @@
|
|||||||
"roll_dice": "Jeter les dés"
|
"roll_dice": "Jeter les dés"
|
||||||
},
|
},
|
||||||
"TOURS": {
|
"TOURS": {
|
||||||
"ChatMessage": "<h3>Bienvenue dans Vermine!</h3><p>Si vous utilisez ce système pour la première fois, notre shaman va vous guider vers votre rite d'initiation</p> <button id=\"vermine-tour-chat-button\"> <i class=\"fas fa-person-hiking\"></i> J'y vais mais j'ai peur </button>",
|
"ChatMessage": "<h3>Bienvenue dans Vermine 2047 !</h3><hr><p class=\"vermine-credits\"><strong>Vermine 2047</strong> — un jeu de Julien Blondel<br>© 2023 Studio Agate — TOTEM System — © 2023 Julien Blondel — Tous droits réservés.<br>Système Foundry VTT développé par <a href=\"https://www.uberwald.me\">LeRatierBretonnier</a>, sur un travail original de Rwanoux.</p>"
|
||||||
"Welcome": {
|
|
||||||
"Title": "Bienvenue dans Vermine",
|
|
||||||
"Description": "Aperçu du système Vermine",
|
|
||||||
"WelcomeTitle": "Bienvenue",
|
|
||||||
"WelcomeContent": "<p>Bienvenue dans Vermine, survivantE ! Faire le tour de notre zone d'action te permettre de retrouver tes marques, même si c'est ton premier jour parmi nous.</p><p>En haut de la colonne de droite, tu as tout ce que tu as le droit de faire chez nous : parler, voir les autres tronches de ta commu, gérer ton matos et celui de tes potes, étudier les cartes, lire les journaux des clamsés, aller nous chercher de la bouffe à coups de surin. Entrons dans le vif du sujet.</p>",
|
|
||||||
"ActorTabTitle": "Peupler son univers",
|
|
||||||
"ActorTabContent": "<p>Bien installéE, survivantE ? Tournons pas au tour du pot, si tu veux rester avec nous, va falloir nous dire qui tu es, d'où tu viens, et à qui tu fais confiance. Une fois que t'es prêtE pour ca, clique donc sur le bouton \"Créer un acteur\"</p>",
|
|
||||||
"CharacterCreateTitle": "Créer un groupe",
|
|
||||||
"CharacterCreateContent": "<p>Ce bouton crée une fiche de groupe, parce que c'est par là qu'on commence, dans ce trou ! Ta tronche viendra ensuite, quand tu créeras ta fiche de personnage. Au fur et à mesure que tu progresses, tu voudras peut-être rajouter des infos sur ce que tu vois trainer autour du camp.</p>",
|
|
||||||
"CompendiumTitle": "Cartes",
|
|
||||||
"CompendiumContent": "<p>Vous aurez probablement besoin d'une carte des Terres de Fer ou d'un secteur de la Forge. Vous pouvez créer votre propre scène pour cela, ou vous pouvez utiliser l'une des nôtres. Ouvrez l'un de ces compendiums et faites glisser une carte dans votre zone de scènes (l'onglet qui ressemble à une carte pliée).</p>",
|
|
||||||
"OracleToolTitle": "Totem",
|
|
||||||
"OracleToolContent": "<p>Aucune partie d'Vermine ou Starforged ne serait complète sans oracles. Ce bouton fera apparaître une fenêtre avec tous les oracles du jeu choisi. Les oracles sont également disponibles depuis la fiche de personnage.</p>",
|
|
||||||
"ToursTitle": "Prochaines étapes",
|
|
||||||
"ToursContent": "<p>Tu t'en sors pas si mal ! Moi je vais te laisser là, mais si tu as d'autres questions, tu peux venir me trouver. Si ca concerne la mécanique, inscris-toi dans notre groupe de parole, par là <a href=\"https://discord.gg/FqGHYvXg\">Totem Foundry Discord</a>. Sinon, tu peux rejoindre la place centrale par ici <a href=\"https://discord.gg/qejqmSxr\">Vermine Discord</a></p><p>Enjoy!</p>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"DIFFICULTY_LEVELS": {
|
"DIFFICULTY_LEVELS": {
|
||||||
"obvious": "Evidente",
|
"obvious": "Evidente",
|
||||||
@@ -143,6 +123,7 @@
|
|||||||
"rerolls": "Relances",
|
"rerolls": "Relances",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"reaction": "Réaction",
|
"reaction": "Réaction",
|
||||||
|
"reaction_bonus": "Bonus de Réaction",
|
||||||
"pools": "Réserves",
|
"pools": "Réserves",
|
||||||
"gear": "Matériel",
|
"gear": "Matériel",
|
||||||
"gear_hindrance": "Handicap Matériel",
|
"gear_hindrance": "Handicap Matériel",
|
||||||
@@ -170,6 +151,35 @@
|
|||||||
"reserves": "Réserves",
|
"reserves": "Réserves",
|
||||||
"dice_pool": "Pool de dés",
|
"dice_pool": "Pool de dés",
|
||||||
"pool_malus": "Malus Blessure",
|
"pool_malus": "Malus Blessure",
|
||||||
|
"pool_wound": "Malus Blessure",
|
||||||
|
"pool_lourd": "Malus Lourd",
|
||||||
|
"lourd_malus_note": "Malus Lourd : -{malus}D (Vigueur insuffisante pour les objets Lourds équipés)",
|
||||||
|
"effective_range": "Portée effective",
|
||||||
|
"traits_mobility_note": "Handicap de Mobilité : +{handicap} (protections équipées non Maniables)",
|
||||||
|
"rafale_check": "Tir en rafale (Difficulté +2, +{rafaleValue} Réussites si réussi)",
|
||||||
|
"rapide_note": "Rapide ({handicap}) : le défenseur subit {handicap} Handicap(s)",
|
||||||
|
"incapacitant_roll": "Jet de Santé",
|
||||||
|
"intimidant": "Intimidant",
|
||||||
|
"intimidant_check": "Brandit ({intimidantName}) — Intimidant",
|
||||||
|
"intimidant_psychology": "Malus 1D (Psychologie)",
|
||||||
|
"intimidant_reroll": "Relance 1D",
|
||||||
|
"temporary_malus": "Malus temporaires",
|
||||||
|
"pool_temp_malus": "Malus temporaires",
|
||||||
|
"pool_psychology": "Malus Psychologie",
|
||||||
|
"ponctuel": "Ponctuel",
|
||||||
|
"ponctuel_used": "annule {value} Handicap(s)",
|
||||||
|
"duree": "Durée",
|
||||||
|
"duree_note": "effet pendant {value} tours de combat",
|
||||||
|
"fetiche": "Fétiche",
|
||||||
|
"fetiche_bonus": "Réserves d'Effort et de Sang-Froid +1D",
|
||||||
|
"fetiche_multiple": "Un même personnage ne peut posséder qu'un seul objet Fétiche",
|
||||||
|
"fetiche_none": "Aucun objet Fétiche équipé",
|
||||||
|
"fetiche_lost": "Fétiche perdu : Réserves −2D, max −1D",
|
||||||
|
"fetiche_lost_penalty": "Fétiche perdu : Réserves réduites de 1D",
|
||||||
|
"lose_fetiche": "Perdre le fétiche",
|
||||||
|
"zone_note": "Zone ({value} m) : tous les présents dans la zone subissent les Dommages",
|
||||||
|
"zone_apply": "Appliquer aux cibles de la zone",
|
||||||
|
"zone_applied": "Zone : {count} blessure(s) appliquée(s)",
|
||||||
"auto_successes": "réussites automatiques",
|
"auto_successes": "réussites automatiques",
|
||||||
"self_control": "Sang-Froid",
|
"self_control": "Sang-Froid",
|
||||||
"effort": "Effort",
|
"effort": "Effort",
|
||||||
@@ -219,7 +229,6 @@
|
|||||||
"pool_specialty": "Spécialité",
|
"pool_specialty": "Spécialité",
|
||||||
"pool_help": "Entraide",
|
"pool_help": "Entraide",
|
||||||
"pool_tooling": "Équipement",
|
"pool_tooling": "Équipement",
|
||||||
"pool_group": "Groupe",
|
|
||||||
"pool_selfControl": "Sang-Froid",
|
"pool_selfControl": "Sang-Froid",
|
||||||
"pool_pools": "Réserves",
|
"pool_pools": "Réserves",
|
||||||
"pool_total": "Total",
|
"pool_total": "Total",
|
||||||
@@ -245,7 +254,6 @@
|
|||||||
"ConfrontationHint": "Résolvez la confrontation en sélectionnant des dés",
|
"ConfrontationHint": "Résolvez la confrontation en sélectionnant des dés",
|
||||||
"PurposeTrait": "Trait de but",
|
"PurposeTrait": "Trait de but",
|
||||||
"SpleenTrait": "Trait de rate",
|
"SpleenTrait": "Trait de rate",
|
||||||
"group": "Groupe",
|
|
||||||
"abilities": "Caractéristiques",
|
"abilities": "Caractéristiques",
|
||||||
"ability": "Caractéristique",
|
"ability": "Caractéristique",
|
||||||
"skills": "Compétences",
|
"skills": "Compétences",
|
||||||
@@ -254,6 +262,7 @@
|
|||||||
"skill_title": "Compétence",
|
"skill_title": "Compétence",
|
||||||
"skill_mastery": "Maîtrise",
|
"skill_mastery": "Maîtrise",
|
||||||
"bonus": "Bonus",
|
"bonus": "Bonus",
|
||||||
|
"bonus_supplementary": "Bonus supplémentaire",
|
||||||
"penalty": "Malus",
|
"penalty": "Malus",
|
||||||
"reroll": "Relance",
|
"reroll": "Relance",
|
||||||
"equipment": "Equipement",
|
"equipment": "Equipement",
|
||||||
@@ -264,6 +273,9 @@
|
|||||||
"techniques": "Techniques",
|
"techniques": "Techniques",
|
||||||
"difficulty": "Difficulté",
|
"difficulty": "Difficulté",
|
||||||
"help": "Entraide",
|
"help": "Entraide",
|
||||||
|
"add": "Ajouter",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"learning": "Apprentissage",
|
||||||
"tooling": "Equipement",
|
"tooling": "Equipement",
|
||||||
"quantity": "Quantité",
|
"quantity": "Quantité",
|
||||||
"qty": "Qté",
|
"qty": "Qté",
|
||||||
@@ -292,10 +304,57 @@
|
|||||||
"rarity_sm": "Rar.",
|
"rarity_sm": "Rar.",
|
||||||
"reliability": "Fiabilité",
|
"reliability": "Fiabilité",
|
||||||
"reliability_sm": "Fiab.",
|
"reliability_sm": "Fiab.",
|
||||||
|
"reliability_test": "Test de Fiabilité",
|
||||||
|
"reliability_repair": "Réparer",
|
||||||
|
"reliability_severity": "Gravité des Dommages",
|
||||||
|
"reliability_severity_mineurs": "Mineurs (1D)",
|
||||||
|
"reliability_severity_graves": "Graves (3D)",
|
||||||
|
"reliability_severity_critiques": "Critiques (5D)",
|
||||||
|
"reliability_immersion": "Immersion prolongée",
|
||||||
|
"reliability_damages": "Dommages subis",
|
||||||
|
"reliability_state": "État",
|
||||||
|
"reliability_destroyed": "OBJET DÉTRUIT / INUTILISABLE",
|
||||||
|
"reliability_repair_mode": "Type de réparation",
|
||||||
|
"reliability_repair_classic": "Classique",
|
||||||
|
"reliability_repair_fortune": "De fortune",
|
||||||
|
"reliability_skill": "Compétence",
|
||||||
|
"reliability_run": "Lancer",
|
||||||
|
"reliability_successes": "Réussites",
|
||||||
|
"reliability_result": "Résultat",
|
||||||
|
"reliability_gained": "Fiabilité restaurée : +{gained}",
|
||||||
|
"reliability_fixed": "Panne réparée (objet fonctionnel)",
|
||||||
|
"reliability_failed": "Échec — aucun point de Fiabilité rendu",
|
||||||
|
"reliability_need_actor": "Réparation impossible : l'objet doit appartenir à un acteur",
|
||||||
|
"reliability_already_destroyed": "Objet déjà détruit ou inutilisable",
|
||||||
|
"reliability_coque_absorbed": "Coque absorbe {absorbed} Dommage(s), {remaining} restant(s)",
|
||||||
|
"reliability_coque_lost": "Coque : Trait épuisé — réparable (Difficulté {max})",
|
||||||
|
"reliability_coque_restored": "Coque restaurée : {max} points",
|
||||||
|
"reliability_repair_coque": "Réparer la coque (Difficulté {max})",
|
||||||
|
"reliability_repair_coque_label": "Coque",
|
||||||
|
"reliability_bien_concu": "Bien conçu : Réussite automatique — Fiabilité entièrement restaurée",
|
||||||
|
"reliability_bien_concu_fortune": "Bien conçu : +1 point de Fiabilité (1ʳᵉ réparation de fortune)",
|
||||||
|
"reliability_auto_success": "Réussite automatique (Bien conçu)",
|
||||||
|
"reliability_pratique": "Pratique : Handicap +I",
|
||||||
|
"reliability_cassant": "Cassant : pertes de Fiabilité majorées d'1 point",
|
||||||
|
"reliability_etanche": "Étanche : Dommages d'immersion ignorés",
|
||||||
|
"reliability_fortune_skill_bonus": "+1D — le personnage possède la Compétence requise ({skill})",
|
||||||
|
"reliability_fortune_skill_missing": "Compétence requise ({skill}) non maîtrisée : Handicap de Rareté +{rarity}",
|
||||||
|
"reliability_fortune_hint": "Réparation de fortune : +1D si le personnage possède la Compétence requise, sinon Handicap de Rareté de cette Compétence.",
|
||||||
|
"damages_state_1": "Endommagé",
|
||||||
|
"damages_state_2": "Endommagé",
|
||||||
|
"damages_state_3": "Défectueux",
|
||||||
|
"damages_state_4": "Défectueux",
|
||||||
|
"damages_state_5": "Hors d'usage",
|
||||||
"choose_ability": "Choisissez une caractéristique",
|
"choose_ability": "Choisissez une caractéristique",
|
||||||
"choose_skill": "Choisissez une compétence",
|
"choose_skill": "Choisissez une compétence",
|
||||||
"none": "Aucun",
|
"none": "Aucun",
|
||||||
|
"all_characters": "Tous les personnages",
|
||||||
|
"no_group": "Sans groupe",
|
||||||
|
"include_in_phase": "Inclure dans cette phase",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
"stat": "Statistique",
|
||||||
|
"adjustment": "Ajustement",
|
||||||
|
"adjustments": "Ajustements",
|
||||||
"bonuses": "Bonus",
|
"bonuses": "Bonus",
|
||||||
"handicap": "Handicap",
|
"handicap": "Handicap",
|
||||||
"score": "Score",
|
"score": "Score",
|
||||||
@@ -382,7 +441,7 @@
|
|||||||
"mode_survie": "Survie",
|
"mode_survie": "Survie",
|
||||||
"mode_cauchemar": "Cauchemar",
|
"mode_cauchemar": "Cauchemar",
|
||||||
"mode_apocalypse": "Apocalypse",
|
"mode_apocalypse": "Apocalypse",
|
||||||
"evolution_buy_hint": "L'achat consomme et efface tous les Dés d'Évolution accumulés. Limites : Survie 5D, Cauchemar 6D, Apocalypse 7D au total. Mutations 3D/4D interdites en Mode Survie ; une seule Mutation à 4D.",
|
"evolution_buy_hint": "L'achat consomme un nombre de Dés d'Évolution égal au niveau choisi (le reste est conservé). Limites : Survie 5D, Cauchemar 6D, Apocalypse 7D au total. Mutations 3D/4D interdites en Mode Survie ; une seule Mutation à 4D.",
|
||||||
"buy_evolution_done": "Adaptation/Mutation acquise. Dés d'Évolution effacés.",
|
"buy_evolution_done": "Adaptation/Mutation acquise. Dés d'Évolution effacés.",
|
||||||
"error_evolution_no_name": "Indiquez un nom pour l'Adaptation/Mutation.",
|
"error_evolution_no_name": "Indiquez un nom pour l'Adaptation/Mutation.",
|
||||||
"error_evolution_invalid_level": "Niveau invalide (1 à 4).",
|
"error_evolution_invalid_level": "Niveau invalide (1 à 4).",
|
||||||
@@ -427,6 +486,8 @@
|
|||||||
"characteristic_hint": "Difficulté 9 + Handicap = valeur actuelle. Max 3 (Surhumain excepté).",
|
"characteristic_hint": "Difficulté 9 + Handicap = valeur actuelle. Max 3 (Surhumain excepté).",
|
||||||
"reserve_hint": "Difficulté = valeur actuelle + Handicap d'âge. Max 10.",
|
"reserve_hint": "Difficulté = valeur actuelle + Handicap d'âge. Max 10.",
|
||||||
"evolution_hint": "Difficulté selon l'âge + Handicap = Dés d'Évolution accumulés. Totem Adapté +1D, Humain -1D.",
|
"evolution_hint": "Difficulté selon l'âge + Handicap = Dés d'Évolution accumulés. Totem Adapté +1D, Humain -1D.",
|
||||||
|
"socle_competences_hint": "Atout Socle de compétences : +{level}D d'Expérience gratuit (compétence déjà possédée).",
|
||||||
|
"socle_competences_message": "Socle de compétences : +{level}D d'Expérience gratuit. Rappel : un camarade du Groupe qui apprend ou développe avec lui une Compétence qu'il possède gagne aussi 1D d'Expérience.",
|
||||||
"totem_ability_hint": "Prérequis : même Totem que le personnage, Niveau de Groupe ≥ Niveau de la Capacité, Mode de jeu compatible (Survie N1, Cauchemar N2, Apocalypse N3).",
|
"totem_ability_hint": "Prérequis : même Totem que le personnage, Niveau de Groupe ≥ Niveau de la Capacité, Mode de jeu compatible (Survie N1, Cauchemar N2, Apocalypse N3).",
|
||||||
"background_hint": "Difficulté 9. Handicap (I) si l'Historique est marqué d'un astérisque (*). Autorisation du MJ requise.",
|
"background_hint": "Difficulté 9. Handicap (I) si l'Historique est marqué d'un astérisque (*). Autorisation du MJ requise.",
|
||||||
"reputation_hint": "Réduire = plus célèbre (Difficulté 10 − score visé, 1 pt max/phase). Augmenter = se faire oublier (exige un Objectif majeur, Difficulté = Réputation actuelle, +1 par Réussite).",
|
"reputation_hint": "Réduire = plus célèbre (Difficulté 10 − score visé, 1 pt max/phase). Augmenter = se faire oublier (exige un Objectif majeur, Difficulté = Réputation actuelle, +1 par Réussite).",
|
||||||
@@ -550,6 +611,7 @@
|
|||||||
"RollTool": "Outil de lancer de dés",
|
"RollTool": "Outil de lancer de dés",
|
||||||
"roll": "Lancer",
|
"roll": "Lancer",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
|
"close": "Fermer",
|
||||||
"Sheet": {
|
"Sheet": {
|
||||||
"character": "Personnage",
|
"character": "Personnage",
|
||||||
"npc": "PNJ",
|
"npc": "PNJ",
|
||||||
@@ -997,6 +1059,7 @@
|
|||||||
},
|
},
|
||||||
"COMBAT": {
|
"COMBAT": {
|
||||||
"Begin": "Commencer",
|
"Begin": "Commencer",
|
||||||
|
"Delete": "Supprimer",
|
||||||
"Encounter": "Rencontre",
|
"Encounter": "Rencontre",
|
||||||
"End": "Terminer",
|
"End": "Terminer",
|
||||||
"None": "Aucun",
|
"None": "Aucun",
|
||||||
|
|||||||
+59
-3
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
input:not([type="radio"]):not([type="checkbox"]),
|
input:not([type="radio"]):not([type="checkbox"]),
|
||||||
select, textarea {
|
select, textarea {
|
||||||
color: @color-text-light-0;
|
color: #333;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
padding: 1px 4px;
|
padding: 1px 4px;
|
||||||
}
|
}
|
||||||
@@ -670,6 +670,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fetiche-block {
|
||||||
|
gap: 12px;
|
||||||
|
margin: 6px 0;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: 1px dashed @color-hemolymph;
|
||||||
|
background: fade(@color-hemolymph, 8%);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
.fetiche-info {
|
||||||
|
gap: 2px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-12;
|
||||||
|
color: @theme-color-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fetiche-bonus {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
color: @color-acid-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fetiche-warning {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-weight: 700;
|
||||||
|
color: @color-hemolymph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: none;
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
padding: 4px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: fade(@color-hemolymph, 20%);
|
||||||
|
border: 1px solid @color-hemolymph;
|
||||||
|
border-radius: 3px;
|
||||||
|
.transition();
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: @color-hemolymph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.items-list {
|
.items-list {
|
||||||
.item-name .item-image {
|
.item-name .item-image {
|
||||||
flex: 0 0 30px;
|
flex: 0 0 30px;
|
||||||
@@ -706,13 +762,13 @@
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
&.adaptation {
|
&.adaptation {
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
background: fade(@color-acid-green, 25%);
|
background: fade(@color-acid-green, 25%);
|
||||||
border: 1px solid @color-acid-green;
|
border: 1px solid @color-acid-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mutation {
|
&.mutation {
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
background: fade(@color-hemolymph, 30%);
|
background: fade(@color-hemolymph, 30%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-hemolymph;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
.community-size-info {
|
.community-size-info {
|
||||||
margin: 6px 0 0;
|
margin: 6px 0 0;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +94,8 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.7;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jauges de Domaines
|
// Jauges de Domaines
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
.gauge-value {
|
.gauge-value {
|
||||||
font-family: "DistressBlack", sans-serif;
|
font-family: "DistressBlack", sans-serif;
|
||||||
font-size: @font-size-16;
|
font-size: @font-size-16;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gauge-state {
|
.gauge-state {
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|
||||||
&.high { color: @color-acid-green; }
|
&.high { color: @color-acid-green; }
|
||||||
&.normal { color: @color-text-light-0; }
|
&.normal { color: @color-text-dark-secondary; }
|
||||||
&.low { color: @color-honeycomb; }
|
&.low { color: @color-honeycomb; }
|
||||||
&.crisis { color: @color-hemolymph; }
|
&.crisis { color: @color-hemolymph; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,4 +208,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Ajustements manuels des valeurs calculées ────────────────────────
|
||||||
|
.creature-adjustments {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-bottom: 1px solid @color-border-dark-3;
|
||||||
|
font-size: @font-size-12;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th { text-transform: uppercase; }
|
||||||
|
|
||||||
|
td:nth-child(2) {
|
||||||
|
width: 80px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(3) { text-align: center; }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -71,13 +71,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.npc-roll-btn {
|
.npc-roll-btn {
|
||||||
color: @color-text-light-primary;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.7;
|
opacity: 0.75;
|
||||||
transition: opacity 0.15s ease;
|
transition: opacity 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
color: @color-text-light-1;
|
color: @theme-color-dark;
|
||||||
text-shadow: 0 0 6px @theme-color-highlight;
|
text-shadow: 0 0 6px @theme-color-highlight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|
||||||
.totem-influence-label {
|
.totem-influence-label {
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
|
|
||||||
strong.totem-human { color: @totem-human-color; }
|
strong.totem-human { color: @totem-human-color; }
|
||||||
strong.totem-adapted { color: @totem-adapted-color; }
|
strong.totem-adapted { color: @totem-adapted-color; }
|
||||||
@@ -144,9 +144,9 @@
|
|||||||
flex: none;
|
flex: none;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
background: fade(@color-hemolymph, 20%);
|
background: fade(@color-text-light-0, 30%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-text-dark-secondary;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
.transition();
|
.transition();
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
|
|
||||||
.unit {
|
.unit {
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+306
-43
@@ -12,29 +12,53 @@ body.system-vermine2047 {
|
|||||||
color: @theme-color-secondary;
|
color: @theme-color-secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override Foundry Core default CSS custom properties
|
// Les checkbox/radio du système ne s'étirent jamais dans les conteneurs
|
||||||
// for better readability on the dark Vermine2047 theme.
|
// flex : ils gardent leur taille hexagonale (1rem / 1.5rem). Les cases de
|
||||||
|
// blessure en `div.hexa` (position:absolute) ne sont pas affectées.
|
||||||
// AppV2 windows (sheets) use .themed.theme-dark on their content
|
.system-vermine2047 {
|
||||||
.system-vermine2047 .themed.theme-dark {
|
input[type="checkbox"],
|
||||||
--color-text-primary: #888888;
|
input[type="radio"] {
|
||||||
--color-text-emphatic: #999999;
|
flex: 0 0 auto;
|
||||||
--color-text-secondary: #777777;
|
}
|
||||||
--color-text-subtle: #666666;
|
|
||||||
--color-text-light-highlight: #888888;
|
|
||||||
--color-text-light-heading: #777777;
|
|
||||||
--color-text-light-primary: #666666;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old-style Application windows (settings-config, dialogs) inherit from body.theme-dark
|
// Override Foundry Core default CSS custom properties
|
||||||
|
// for better readability on the light Vermine2047 theme.
|
||||||
|
|
||||||
|
// Core windows (FilePicker, dialogs, compendium, RollTable, Journal, …) and chat
|
||||||
|
// messages sit on the light box_background texture: force dark readable text.
|
||||||
|
// The dark sidebar keeps Foundry's default light-on-dark text colors.
|
||||||
body.system-vermine2047.theme-dark {
|
body.system-vermine2047.theme-dark {
|
||||||
--color-text-primary: #888888;
|
.window-content,
|
||||||
--color-text-emphatic: #999999;
|
.roll-table-sheet,
|
||||||
--color-text-secondary: #777777;
|
.journal-sheet,
|
||||||
--color-text-subtle: #666666;
|
.document-sheet,
|
||||||
--color-text-light-highlight: #888888;
|
.settings-sheet {
|
||||||
--color-text-light-heading: #777777;
|
color: @color-text-dark-primary;
|
||||||
--color-text-light-primary: #666666;
|
--color-text-primary: @color-text-dark-primary;
|
||||||
|
--color-text-emphatic: @color-text-dark-header;
|
||||||
|
--color-text-secondary: @color-text-dark-secondary;
|
||||||
|
--color-text-subtle: #666666;
|
||||||
|
--color-text-light-highlight: @color-acid-green;
|
||||||
|
--color-text-light-heading: @color-text-dark-header;
|
||||||
|
--color-text-light-primary: @color-text-dark-secondary;
|
||||||
|
--color-text-dark-primary: @color-text-dark-primary;
|
||||||
|
--color-text-dark-secondary: @color-text-dark-secondary;
|
||||||
|
--color-text-dark-header: @color-text-dark-header;
|
||||||
|
--text-color: @color-text-dark-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-log .message,
|
||||||
|
.chat-log .message {
|
||||||
|
.message-content {
|
||||||
|
color: @color-text-dark-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.message-header {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 5px #000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings-config button,
|
#settings-config button,
|
||||||
@@ -44,31 +68,13 @@ body.system-vermine2047.theme-dark {
|
|||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core sheets the system doesn't restyle (RollTable, Journal, …) inherit the
|
|
||||||
// gray text overrides above, which are unreadable on their dark backgrounds.
|
|
||||||
// Restore Foundry's default light-on-dark text colors for those apps (dark theme only).
|
|
||||||
body.system-vermine2047.theme-dark {
|
|
||||||
.roll-table-sheet,
|
|
||||||
.journal-sheet,
|
|
||||||
.document-sheet,
|
|
||||||
.settings-sheet {
|
|
||||||
--color-text-primary: var(--color-light-2);
|
|
||||||
--color-text-emphatic: var(--color-light-1);
|
|
||||||
--color-text-secondary: var(--color-light-3);
|
|
||||||
--color-text-subtle: var(--color-light-5);
|
|
||||||
--color-text-light-highlight: #f0f0e0;
|
|
||||||
--color-text-light-heading: #c9c7b8;
|
|
||||||
--color-text-light-primary: #b5b3a4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body.system-vermine2047.theme-dark .form-group > label {
|
body.system-vermine2047.theme-dark .form-group > label {
|
||||||
color: #777777;
|
color: @color-text-dark-secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings-config p,
|
#settings-config p,
|
||||||
#settings-config .hint {
|
#settings-config .hint {
|
||||||
color: #777777;
|
color: @color-text-dark-secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@@ -621,6 +627,25 @@ ol.chat-log {
|
|||||||
&.visible { max-height: 15rem; }
|
&.visible { max-height: 15rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reroll-from-effort {
|
||||||
|
h4.flexcol { gap: 2px; }
|
||||||
|
.effort-pool {
|
||||||
|
font-size: @font-size-12;
|
||||||
|
opacity: 0.8;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Mentions de bienvenue / crédits ──────────────────────────────
|
||||||
|
.vermine-credits {
|
||||||
|
font-size: @font-size-12;
|
||||||
|
opacity: 0.8;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
|
||||||
|
a { color: inherit; }
|
||||||
|
}
|
||||||
|
|
||||||
// ── Dice results ────────────────────────────────────────────────
|
// ── Dice results ────────────────────────────────────────────────
|
||||||
ul.roll-results {
|
ul.roll-results {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -837,6 +862,40 @@ ol.chat-log {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.weapon-traits {
|
||||||
|
gap: 4px 6px;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 4px;
|
||||||
|
border-top: 1px dashed fade(@color-hemolymph, 35%);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.weapon-traits-label {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-trait {
|
||||||
|
flex: none;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 1px 6px;
|
||||||
|
border: 1px solid @color-border-dark-4;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: fade(@color-acid-green, 10%);
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.duree-note {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-style: italic;
|
||||||
|
color: @theme-color-accent;
|
||||||
|
|
||||||
|
i { color: @color-hemolymph; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Échange de coups ─────────────────────────────────────────────
|
// ── Échange de coups ─────────────────────────────────────────────
|
||||||
@@ -860,6 +919,19 @@ ol.chat-log {
|
|||||||
i { color: @color-hemolymph; }
|
i { color: @color-hemolymph; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.exchange-reminder {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-style: italic;
|
||||||
|
color: @theme-color-accent;
|
||||||
|
padding: 2px 0;
|
||||||
|
|
||||||
|
i { color: @color-hemolymph; }
|
||||||
|
}
|
||||||
|
|
||||||
.exchange-target {
|
.exchange-target {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -907,7 +979,7 @@ ol.chat-log {
|
|||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
padding: 3px 12px;
|
padding: 3px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
background: fade(@color-hemolymph, 25%);
|
background: fade(@color-hemolymph, 25%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-hemolymph;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -918,6 +990,31 @@ ol.chat-log {
|
|||||||
background: @color-hemolymph;
|
background: @color-hemolymph;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.exchange-incapacitant,
|
||||||
|
button.exchange-zone {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
padding: 3px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: fade(@color-acid-green, 20%);
|
||||||
|
border: 1px solid @color-acid-green;
|
||||||
|
border-radius: 3px;
|
||||||
|
.transition();
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: @color-acid-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.exchange-resolution {
|
div.exchange-resolution {
|
||||||
@@ -962,7 +1059,7 @@ ol.chat-log {
|
|||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
padding: 3px 12px;
|
padding: 3px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
background: fade(@color-hemolymph, 25%);
|
background: fade(@color-hemolymph, 25%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-hemolymph;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -1084,7 +1181,7 @@ ol.chat-log {
|
|||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
padding: 5px 14px;
|
padding: 5px 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
background: fade(@color-hemolymph, 20%);
|
background: fade(@color-hemolymph, 20%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-hemolymph;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -1151,3 +1248,169 @@ ol.chat-log {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Test de Fiabilité / Réparations ──────────────────────────────────
|
||||||
|
.vermine-reliability-message {
|
||||||
|
padding: 4px 10px 10px;
|
||||||
|
|
||||||
|
.reliability-header {
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid @color-border-dark-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-13;
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
color: @theme-color-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-meta {
|
||||||
|
gap: 4px 12px;
|
||||||
|
margin: 4px 0 6px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
.reliability-meta-item {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
color: @color-text-dark-primary;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-results {
|
||||||
|
gap: 6px;
|
||||||
|
margin: 6px 0;
|
||||||
|
|
||||||
|
.reliability-die {
|
||||||
|
flex: none;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
font-size: @font-size-16;
|
||||||
|
border: 1px solid @color-border-dark-4;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.08);
|
||||||
|
color: @color-text-dark-primary;
|
||||||
|
|
||||||
|
&.damage {
|
||||||
|
border-color: @color-hemolymph;
|
||||||
|
background: fade(@color-hemolymph, 15%);
|
||||||
|
color: @color-hemolymph;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
border-color: @color-acid-green;
|
||||||
|
background: fade(@color-acid-green, 15%);
|
||||||
|
color: @color-acid-green;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-summary {
|
||||||
|
gap: 4px 16px;
|
||||||
|
margin: 4px 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-12;
|
||||||
|
color: @color-text-dark-primary;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-note {
|
||||||
|
margin: 2px 0;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-style: italic;
|
||||||
|
color: @color-text-dark-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-destroyed {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-family: "DistressBlack";
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-12;
|
||||||
|
text-align: center;
|
||||||
|
color: @color-hemolymph;
|
||||||
|
border: 1px solid @color-hemolymph;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: fade(@color-hemolymph, 12%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .reliability-actions {
|
||||||
|
gap: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 4px 0 8px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: "DistressBlack", sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
padding: 3px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: fade(@color-hemolymph, 15%);
|
||||||
|
border: 1px solid @color-hemolymph;
|
||||||
|
border-radius: 3px;
|
||||||
|
.transition();
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: @color-hemolymph;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: @font-size-12;
|
||||||
|
color: @color-text-dark-primary;
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-severities,
|
||||||
|
.reliability-modes {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-hint {
|
||||||
|
margin: 0;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-style: italic;
|
||||||
|
color: @color-text-dark-secondary;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/* ============================================
|
||||||
|
Combat & Combat Tracker (Vermine2047)
|
||||||
|
Styles custom par-dessus le CSS core Foundry v14
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
// ── Combat tracker sidebar ──────────────────────────────────────────
|
||||||
|
.combat-sidebar {
|
||||||
|
|
||||||
|
// Nom des combatants coloré : PJ (vert acide) / PNJ (hémolymphe)
|
||||||
|
.combat-tracker .name.player {
|
||||||
|
color: @color-acid-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combat-tracker .name.npc {
|
||||||
|
color: @color-hemolymph;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boutons d'attitude (offensif / actif / passif)
|
||||||
|
.combatant-attitudes {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--element-spacing-half);
|
||||||
|
|
||||||
|
.status {
|
||||||
|
color: var(--color-text-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.active {
|
||||||
|
color: var(--color-warm-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Footer des dialogues de jet (combat & action) ───────────────────
|
||||||
|
// Pin le footer en bas de la fenêtre : visible même si le contenu dépasse.
|
||||||
|
.application.vermine-roll {
|
||||||
|
.roll-dialog-content .sheet-footer {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 8px;
|
||||||
|
background: @theme-color-dark;
|
||||||
|
border-top: 1px solid fade(@color-hemolymph, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
-26
@@ -148,7 +148,7 @@ iframe {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
|
|
||||||
.dice-pool {
|
.dice-pool {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -201,7 +201,7 @@ iframe {
|
|||||||
.ability-score {
|
.ability-score {
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
|
|
||||||
#abilityScoreValue {
|
#abilityScoreValue {
|
||||||
@@ -290,11 +290,11 @@ iframe {
|
|||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @color-text-light-highlight;
|
color: @color-acid-green;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ iframe {
|
|||||||
|
|
||||||
> label.label {
|
> label.label {
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ iframe {
|
|||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-list {
|
.item-list {
|
||||||
@@ -343,13 +343,31 @@ iframe {
|
|||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @color-text-light-highlight;
|
color: @color-acid-green;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-bonus,
|
||||||
|
.tool-ponctuel {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bonus {
|
||||||
|
color: @color-acid-green;
|
||||||
|
background: fade(@color-acid-green, 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-ponctuel {
|
||||||
|
color: @theme-color-dark;
|
||||||
|
background: fade(@color-hemolymph, 18%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.full-width {
|
&.full-width {
|
||||||
@@ -374,14 +392,14 @@ iframe {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @color-text-light-highlight;
|
color: @color-acid-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
small {
|
small {
|
||||||
@@ -392,8 +410,8 @@ iframe {
|
|||||||
.totem-hint {
|
.totem-hint {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.6;
|
opacity: 0.8;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,6 +426,16 @@ iframe {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.traits-reminder {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: @font-size-11;
|
||||||
|
font-style: italic;
|
||||||
|
color: @theme-color-accent;
|
||||||
|
|
||||||
|
i { color: @color-hemolymph; }
|
||||||
|
}
|
||||||
|
|
||||||
.locked-value {
|
.locked-value {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -448,7 +476,7 @@ iframe {
|
|||||||
.weapon-detail {
|
.weapon-detail {
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
|
|
||||||
strong { text-transform: uppercase; }
|
strong { text-transform: uppercase; }
|
||||||
}
|
}
|
||||||
@@ -466,7 +494,7 @@ iframe {
|
|||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
input[type="radio"] { flex: none; }
|
input[type="radio"] { flex: none; }
|
||||||
@@ -494,7 +522,7 @@ iframe {
|
|||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
background: fade(@color-hemolymph, 20%);
|
background: fade(@color-hemolymph, 20%);
|
||||||
border: 1px solid fade(@color-hemolymph, 50%);
|
border: 1px solid fade(@color-hemolymph, 50%);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -550,12 +578,12 @@ iframe {
|
|||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
input[type="radio"] { flex: none; }
|
input[type="radio"] { flex: none; }
|
||||||
|
|
||||||
&:hover { color: @color-text-light-highlight; }
|
&:hover { color: @color-acid-green; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,8 +599,8 @@ iframe {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.75;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,7 +628,7 @@ iframe {
|
|||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: @font-size-13;
|
font-size: @font-size-13;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,7 +641,7 @@ iframe {
|
|||||||
|
|
||||||
.experience-group {
|
.experience-group {
|
||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-secondary;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
||||||
#exp-group {
|
#exp-group {
|
||||||
@@ -628,7 +656,7 @@ iframe {
|
|||||||
font-size: @font-size-11;
|
font-size: @font-size-11;
|
||||||
padding: 3px 12px;
|
padding: 3px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
background: fade(@color-acid-green, 15%);
|
background: fade(@color-acid-green, 15%);
|
||||||
border: 1px solid @color-acid-green;
|
border: 1px solid @color-acid-green;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -666,8 +694,8 @@ iframe {
|
|||||||
.total-value {
|
.total-value {
|
||||||
font-family: "DistressBlack", sans-serif;
|
font-family: "DistressBlack", sans-serif;
|
||||||
font-size: @font-size-28;
|
font-size: @font-size-28;
|
||||||
color: @color-acid-green;
|
color: @color-text-light-0;
|
||||||
text-shadow: 0 0 8px fade(@color-acid-green, 40%);
|
text-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.totem-selector {
|
.totem-selector {
|
||||||
@@ -779,7 +807,7 @@ iframe {
|
|||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-size: @font-size-14;
|
font-size: @font-size-14;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: @color-text-light-0;
|
color: @color-text-dark-header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,7 +826,7 @@ iframe {
|
|||||||
font-size: @font-size-12;
|
font-size: @font-size-12;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @color-text-light-0;
|
color: @theme-color-dark;
|
||||||
background: fade(@color-hemolymph, 15%);
|
background: fade(@color-hemolymph, 15%);
|
||||||
border: 1px solid @color-hemolymph;
|
border: 1px solid @color-hemolymph;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|||||||
+1
-2
@@ -66,11 +66,10 @@
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
.custom-select-style();
|
.custom-select-style();
|
||||||
color: @color-text-light-1;
|
color: @color-text-dark-primary;
|
||||||
font-family: "DistressBlack", sans-serif;
|
font-family: "DistressBlack", sans-serif;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-shadow: 0 0 3px rgba(0, 0, 0, 0.9);
|
|
||||||
|
|
||||||
option {
|
option {
|
||||||
background: @color-bg-option;
|
background: @color-bg-option;
|
||||||
|
|||||||
@@ -204,6 +204,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
flex: 0 0 auto;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
.transition();
|
.transition();
|
||||||
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
|
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
// 7. Styles des dialogs
|
// 7. Styles des dialogs
|
||||||
@import "dialogs";
|
@import "dialogs";
|
||||||
|
|
||||||
|
// 8. Styles du combat et du combat tracker
|
||||||
|
@import "combat";
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Notes:
|
// Notes:
|
||||||
// - L'ordre des imports est important
|
// - L'ordre des imports est important
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ export class VermineBaseItemSheet extends HandlebarsApplicationMixin(foundry.app
|
|||||||
editImage: VermineBaseItemSheet.#onEditImage,
|
editImage: VermineBaseItemSheet.#onEditImage,
|
||||||
toggleSheet: VermineBaseItemSheet.#onToggleSheet,
|
toggleSheet: VermineBaseItemSheet.#onToggleSheet,
|
||||||
clickDamage: VermineBaseItemSheet.#onClickDamage,
|
clickDamage: VermineBaseItemSheet.#onClickDamage,
|
||||||
openTraits: VermineBaseItemSheet.#onOpenTraits
|
openTraits: VermineBaseItemSheet.#onOpenTraits,
|
||||||
|
reliabilityTest: VermineBaseItemSheet.#onReliabilityTest,
|
||||||
|
reliabilityRepair: VermineBaseItemSheet.#onReliabilityRepair
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,4 +122,16 @@ export class VermineBaseItemSheet extends HandlebarsApplicationMixin(foundry.app
|
|||||||
const { TraitSelector } = await import("../../system/applications.mjs")
|
const { TraitSelector } = await import("../../system/applications.mjs")
|
||||||
new TraitSelector(this.document).render(true)
|
new TraitSelector(this.document).render(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onReliabilityTest() {
|
||||||
|
const { default: ReliabilityDialog } = await import("../../system/dialogs/reliabilityDialog.mjs")
|
||||||
|
const dialog = await ReliabilityDialog.create({ item: this.document, mode: "test" })
|
||||||
|
await dialog.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onReliabilityRepair() {
|
||||||
|
const { default: ReliabilityDialog } = await import("../../system/dialogs/reliabilityDialog.mjs")
|
||||||
|
const dialog = await ReliabilityDialog.create({ item: this.document, mode: "repair" })
|
||||||
|
await dialog.render(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ export default class VermineCharacterSheetV2 extends VermineBaseActorSheet {
|
|||||||
openLearning: VermineCharacterSheetV2.#onOpenLearning,
|
openLearning: VermineCharacterSheetV2.#onOpenLearning,
|
||||||
validatePhase: VermineCharacterSheetV2.#onValidatePhase,
|
validatePhase: VermineCharacterSheetV2.#onValidatePhase,
|
||||||
buyEvolution: VermineCharacterSheetV2.#onBuyEvolution,
|
buyEvolution: VermineCharacterSheetV2.#onBuyEvolution,
|
||||||
openTotemDice: VermineCharacterSheetV2.#onOpenTotemDice
|
openTotemDice: VermineCharacterSheetV2.#onOpenTotemDice,
|
||||||
|
loseFetiche: VermineCharacterSheetV2.#onLoseFetiche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +81,21 @@ export default class VermineCharacterSheetV2 extends VermineBaseActorSheet {
|
|||||||
case "stories":
|
case "stories":
|
||||||
context.tab = context.tabs.stories
|
context.tab = context.tabs.stories
|
||||||
break
|
break
|
||||||
case "combat":
|
case "combat": {
|
||||||
context.tab = context.tabs.combat
|
context.tab = context.tabs.combat
|
||||||
context.equippedWeapons = doc.itemTypes.weapon.filter(i => i.system.equipped)
|
context.equippedWeapons = doc.itemTypes.weapon.filter(i => i.system.equipped)
|
||||||
context.equippedDefenses = doc.itemTypes.defense.filter(i => i.system.equipped)
|
context.equippedDefenses = doc.itemTypes.defense.filter(i => i.system.equipped)
|
||||||
const { prepareActiveEffectCategories } = await import("../../system/effects.mjs")
|
const { prepareActiveEffectCategories } = await import("../../system/effects.mjs")
|
||||||
context.effects = prepareActiveEffectCategories(doc.effects)
|
context.effects = prepareActiveEffectCategories(doc.effects)
|
||||||
|
const { VermineTraits } = await import("../../system/traits.mjs")
|
||||||
|
const feticheItems = VermineTraits.equippedItems(doc, 'fetiche')
|
||||||
|
context.feticheItems = feticheItems
|
||||||
|
context.fetiche = feticheItems[0] ?? null
|
||||||
|
context.feticheMultiple = feticheItems.length > 1
|
||||||
|
const loss = doc.system.experience?.feticheLoss
|
||||||
|
context.feticheLost = Boolean(loss?.self_control || loss?.effort)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case "experience":
|
case "experience":
|
||||||
context.tab = context.tabs.experience
|
context.tab = context.tabs.experience
|
||||||
context.specialtyCount = doc.itemTypes.specialty.length
|
context.specialtyCount = doc.itemTypes.specialty.length
|
||||||
@@ -149,4 +158,35 @@ export default class VermineCharacterSheetV2 extends VermineBaseActorSheet {
|
|||||||
const dialog = await TotemDiceDialog.create({ actorId: this.document.id })
|
const dialog = await TotemDiceDialog.create({ actorId: this.document.id })
|
||||||
if (dialog) dialog.render(true)
|
if (dialog) dialog.render(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action « Perdre le fétiche » : applique -2D dans chaque Réserve et
|
||||||
|
* -1D de max persistant (expérience.feticheLoss), puis retire le Trait
|
||||||
|
* Fétiche de l'objet équipé.
|
||||||
|
*/
|
||||||
|
static async #onLoseFetiche() {
|
||||||
|
const doc = this.document
|
||||||
|
if (!game.user.isGM && !doc.isOwner) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_permission"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { VermineTraits } = await import("../../system/traits.mjs")
|
||||||
|
const items = VermineTraits.equippedItems(doc, 'fetiche')
|
||||||
|
if (!items.length) {
|
||||||
|
ui.notifications.warn(game.i18n.localize("VERMINE.fetiche_none"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const loss = doc.system.experience?.feticheLoss
|
||||||
|
const sc = Math.max(0, (doc.system.attributes.self_control.value || 0) - 2)
|
||||||
|
const ef = Math.max(0, (doc.system.attributes.effort.value || 0) - 2)
|
||||||
|
await doc.update({
|
||||||
|
"system.attributes.self_control.value": sc,
|
||||||
|
"system.attributes.effort.value": ef,
|
||||||
|
"system.experience.feticheLoss.self_control": (loss?.self_control || 0) + 1,
|
||||||
|
"system.experience.feticheLoss.effort": (loss?.effort || 0) + 1
|
||||||
|
})
|
||||||
|
await items[0].update({ "system.traits.fetiche": null })
|
||||||
|
ui.notifications.info(game.i18n.localize("VERMINE.fetiche_lost"))
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ export default class VermineNpcSheetV2 extends VermineBaseActorSheet {
|
|||||||
context.npcExperienceOptions = CONFIG.VERMINE.npcExperienceLevels
|
context.npcExperienceOptions = CONFIG.VERMINE.npcExperienceLevels
|
||||||
context.npcRoleOptions = CONFIG.VERMINE.npcRoleLevels
|
context.npcRoleOptions = CONFIG.VERMINE.npcRoleLevels
|
||||||
context.totemOptions = CONFIG.VERMINE.totems
|
context.totemOptions = CONFIG.VERMINE.totems
|
||||||
context.originOptions = CONFIG.VERMINE.origins
|
|
||||||
break
|
break
|
||||||
case "traits":
|
case "traits":
|
||||||
context.tab = context.tabs.traits
|
context.tab = context.tabs.traits
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ export function baseItemSchema() {
|
|||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ required: true, initial: "", textSearch: true }),
|
description: new fields.HTMLField({ required: true, initial: "", textSearch: true }),
|
||||||
rarity: raritySchema(),
|
rarity: raritySchema(),
|
||||||
reliability: new fields.NumberField({ ...reqInt, initial: 3, min: 1, max: 5 }),
|
reliability: new fields.NumberField({ ...reqInt, initial: 3, min: 0, max: 10 }),
|
||||||
handicap: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
handicap: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
quantity: new fields.NumberField({ ...reqInt, initial: 1, min: 1 }),
|
quantity: new fields.NumberField({ ...reqInt, initial: 1, min: 1 }),
|
||||||
weight: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
weight: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
skillCategoriesSchema,
|
skillCategoriesSchema,
|
||||||
skillsSchema
|
skillsSchema
|
||||||
} from "./_shared.mjs"
|
} from "./_shared.mjs"
|
||||||
|
import { VermineTraits } from "../system/traits.mjs"
|
||||||
|
|
||||||
export default class VermineCharacterData extends foundry.abstract.TypeDataModel {
|
export default class VermineCharacterData extends foundry.abstract.TypeDataModel {
|
||||||
|
|
||||||
@@ -99,6 +100,11 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
|
|||||||
self_control: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
self_control: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
||||||
effort: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 })
|
effort: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 })
|
||||||
}),
|
}),
|
||||||
|
// Perte de Réserves après perte du Fétiche : -1D de max persistant par Réserve
|
||||||
|
feticheLoss: new fields.SchemaField({
|
||||||
|
self_control: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
||||||
|
effort: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 })
|
||||||
|
}),
|
||||||
// Limites d'apprentissage par phase
|
// Limites d'apprentissage par phase
|
||||||
learned: new fields.SchemaField({
|
learned: new fields.SchemaField({
|
||||||
phase: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
phase: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
||||||
@@ -157,6 +163,7 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
|
|||||||
/**
|
/**
|
||||||
* Calcule le max de sang-froid :
|
* Calcule le max de sang-froid :
|
||||||
* somme des caractéristiques mentales + sociales + modificateur d'âge.
|
* somme des caractéristiques mentales + sociales + modificateur d'âge.
|
||||||
|
* Un Fétiche équipé ajoute +1D ; une perte de Fétiche soustrait 1D.
|
||||||
*/
|
*/
|
||||||
_setSelfControlMax() {
|
_setSelfControlMax() {
|
||||||
const abilities = Object.values(this.abilities)
|
const abilities = Object.values(this.abilities)
|
||||||
@@ -165,12 +172,15 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
|
|||||||
.filter(a => a.category === "mental" || a.category === "social")
|
.filter(a => a.category === "mental" || a.category === "social")
|
||||||
.reduce((acc, a) => acc + a.value, 0)
|
.reduce((acc, a) => acc + a.value, 0)
|
||||||
const bonus = this.experience?.reserveBonus?.self_control || 0
|
const bonus = this.experience?.reserveBonus?.self_control || 0
|
||||||
this.attributes.self_control.max = Math.min(10, sum + modFromAge + bonus)
|
const loss = this.experience?.feticheLoss?.self_control || 0
|
||||||
|
const fetiche = VermineTraits.feticheItem(this.parent) ? 1 : 0
|
||||||
|
this.attributes.self_control.max = Math.min(10, Math.max(0, sum + modFromAge + bonus + fetiche - loss))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcule le max d'effort :
|
* Calcule le max d'effort :
|
||||||
* somme des caractéristiques physiques + manuelles + modificateur d'âge.
|
* somme des caractéristiques physiques + manuelles + modificateur d'âge.
|
||||||
|
* Un Fétiche équipé ajoute +1D ; une perte de Fétiche soustrait 1D.
|
||||||
*/
|
*/
|
||||||
_setEffortMax() {
|
_setEffortMax() {
|
||||||
const abilities = Object.values(this.abilities)
|
const abilities = Object.values(this.abilities)
|
||||||
@@ -179,7 +189,9 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
|
|||||||
.filter(a => a.category === "physical" || a.category === "manual")
|
.filter(a => a.category === "physical" || a.category === "manual")
|
||||||
.reduce((acc, a) => acc + a.value, 0)
|
.reduce((acc, a) => acc + a.value, 0)
|
||||||
const bonus = this.experience?.reserveBonus?.effort || 0
|
const bonus = this.experience?.reserveBonus?.effort || 0
|
||||||
this.attributes.effort.max = Math.min(10, sum + modFromAge + bonus)
|
const loss = this.experience?.feticheLoss?.effort || 0
|
||||||
|
const fetiche = VermineTraits.feticheItem(this.parent) ? 1 : 0
|
||||||
|
this.attributes.effort.max = Math.min(10, Math.max(0, sum + modFromAge + bonus + fetiche - loss))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,6 +65,22 @@ export default class VermineCreatureData extends foundry.abstract.TypeDataModel
|
|||||||
protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1 })
|
protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1 })
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Ajustements manuels des valeurs calculées : permettent les disparités
|
||||||
|
// entre créatures de même Gabarit/Taille (ex. un sanglier avec une
|
||||||
|
// Protection supérieure à celle d'un chien, un loup plus vigoureux).
|
||||||
|
// Base dérivée + ajustement = valeur finale (jamais négative).
|
||||||
|
adjustments: new fields.SchemaField({
|
||||||
|
attack: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
damage: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
vigor: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
reaction: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
reactionBonus: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
pools: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
gear: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
gearHindrance: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
||||||
|
protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 })
|
||||||
|
}),
|
||||||
|
|
||||||
// Équipement
|
// Équipement
|
||||||
equipment: equipmentSchema()
|
equipment: equipmentSchema()
|
||||||
}
|
}
|
||||||
@@ -145,6 +161,18 @@ export default class VermineCreatureData extends foundry.abstract.TypeDataModel
|
|||||||
|
|
||||||
// Protection
|
// Protection
|
||||||
this.computed.protection = roleConfig.protection || 1
|
this.computed.protection = roleConfig.protection || 1
|
||||||
|
|
||||||
|
// Ajustements manuels (base + modificateur), plancher à 0.
|
||||||
|
const adj = this.adjustments ?? {}
|
||||||
|
this.computed.attack = Math.max(0, this.computed.attack + (adj.attack ?? 0))
|
||||||
|
this.computed.damage = Math.max(0, this.computed.damage + (adj.damage ?? 0))
|
||||||
|
this.computed.vigor = Math.max(0, this.computed.vigor + (adj.vigor ?? 0))
|
||||||
|
this.computed.reaction = Math.max(0, this.computed.reaction + (adj.reaction ?? 0))
|
||||||
|
this.computed.reactionBonus = Math.max(0, this.computed.reactionBonus + (adj.reactionBonus ?? 0))
|
||||||
|
this.computed.pools = Math.max(0, this.computed.pools + (adj.pools ?? 0))
|
||||||
|
this.computed.gear = Math.max(0, this.computed.gear + (adj.gear ?? 0))
|
||||||
|
this.computed.gearHindrance = Math.max(0, this.computed.gearHindrance + (adj.gearHindrance ?? 0))
|
||||||
|
this.computed.protection = Math.max(0, this.computed.protection + (adj.protection ?? 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export default class VermineItemData extends foundry.abstract.TypeDataModel {
|
|||||||
needSkill: new fields.SchemaField({
|
needSkill: new fields.SchemaField({
|
||||||
value: new fields.BooleanField({ required: true, initial: false }),
|
value: new fields.BooleanField({ required: true, initial: false }),
|
||||||
skill: new fields.StringField({ required: true, initial: "" })
|
skill: new fields.StringField({ required: true, initial: "" })
|
||||||
})
|
}),
|
||||||
|
equipped: new fields.BooleanField({ required: true, initial: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default class VermineWeaponData extends foundry.abstract.TypeDataModel {
|
|||||||
min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
damage: new fields.SchemaField({
|
damage: new fields.SchemaField({
|
||||||
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
value: new fields.NumberField({ ...reqInt, initial: 0, min: -10 }),
|
||||||
type: new fields.StringField({ required: true, initial: "" }),
|
type: new fields.StringField({ required: true, initial: "" }),
|
||||||
addVigor: new fields.BooleanField({ required: true, initial: false })
|
addVigor: new fields.BooleanField({ required: true, initial: false })
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -295,13 +295,15 @@ VERMINE.totems = {
|
|||||||
* Neutre : aucun modificateur.
|
* Neutre : aucun modificateur.
|
||||||
*/
|
*/
|
||||||
VERMINE.totemInfluence = {
|
VERMINE.totemInfluence = {
|
||||||
|
// Règles p.63 : l'Humain est à l'aise avec L'Homme, La Machine et Les
|
||||||
|
// Armes (+1D), mal à l'aise avec L'Animal, La Survie et La Terre (-1D).
|
||||||
"human": {
|
"human": {
|
||||||
"bonus": ["man", "tool", "world"],
|
"bonus": ["man", "tool", "weapon"],
|
||||||
"malus": ["animal", "survival", "weapon"]
|
"malus": ["animal", "survival", "world"]
|
||||||
},
|
},
|
||||||
"adapted": {
|
"adapted": {
|
||||||
"bonus": ["animal", "survival", "weapon"],
|
"bonus": ["animal", "survival", "world"],
|
||||||
"malus": ["man", "tool", "world"]
|
"malus": ["man", "tool", "weapon"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +364,7 @@ VERMINE.traits = {
|
|||||||
},
|
},
|
||||||
"fetiche": {
|
"fetiche": {
|
||||||
name: "Fétiche",
|
name: "Fétiche",
|
||||||
description: "Le personnage possède un objet fétiche auquel il tient.Tant qu’il le possède, sa Réserve d’Effort ou de Sang- Froid est augmentée de 1D, et il ne subit pas les Malus d’Épuisement liés à la valeur de ses Réserves, même si elles sont réduites à 0. Si le personnage perd ou est privé de cet objet, les règles d’Épuisement s’appliquent de nouveau et il perd immédiatement 2D dans chacune de ses Réserves, dont les valeurs maximales sont réduites de 1D.Un même personnage ne peut posséder qu’un seul objet avec le Trait Fétiche.",
|
description: "Le personnage possède un objet fétiche auquel il tient.Tant qu’il le possède, ses Réserves d’Effort et de Sang-Froid sont augmentées de 1D, et il ne subit pas les Malus d’Épuisement liés à la valeur de ses Réserves, même si elles sont réduites à 0. Si le personnage perd ou est privé de cet objet, les règles d’Épuisement s’appliquent de nouveau et il perd immédiatement 2D dans chacune de ses Réserves, dont les valeurs maximales sont réduites de 1D.Un même personnage ne peut posséder qu’un seul objet avec le Trait Fétiche.",
|
||||||
value: 0
|
value: 0
|
||||||
},
|
},
|
||||||
"incapacitant": {
|
"incapacitant": {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ const { HandlebarsApplicationMixin } = foundry.applications.api;
|
|||||||
/**
|
/**
|
||||||
* Dialogue d'achat d'une Adaptation/Mutation avec les Dés d'Évolution
|
* Dialogue d'achat d'une Adaptation/Mutation avec les Dés d'Évolution
|
||||||
* accumulés. Vérifie le coût (niveau 1-4), la limite du Mode de jeu et les
|
* accumulés. Vérifie le coût (niveau 1-4), la limite du Mode de jeu et les
|
||||||
* restrictions sur les Mutations ; en cas de succès, crée l'item et efface
|
* restrictions sur les Mutations ; en cas de succès, crée l'item et consomme
|
||||||
* les Dés d'Évolution accumulés.
|
* un nombre de Dés d'Évolution égal au niveau choisi.
|
||||||
*/
|
*/
|
||||||
export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
|
export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found
|
|||||||
};
|
};
|
||||||
const levels = [];
|
const levels = [];
|
||||||
for (let value = 1; value <= 4; value++) {
|
for (let value = 1; value <= 4; value++) {
|
||||||
levels.push({ value, cost: value });
|
levels.push({ value });
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
evolutionDice: actor.system?.experience?.evolution || 0,
|
evolutionDice: actor.system?.experience?.evolution || 0,
|
||||||
@@ -73,6 +73,18 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _onRender() {
|
||||||
|
this.element.querySelectorAll("[data-roll]").forEach(inp => inp.addEventListener("change", () => this.#updateUI()));
|
||||||
|
this.#updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Met à jour le total des niveaux « après achat » selon le niveau sélectionné. */
|
||||||
|
#updateUI() {
|
||||||
|
const cost = parseInt(this.element.querySelector("#evolution-level")?.value, 10) || 0;
|
||||||
|
const el = this.element.querySelector("#evolution-total-value");
|
||||||
|
if (el) el.textContent = `${VermineExperience.evolutionTotalLevels(this.#actor) + cost}D`;
|
||||||
|
}
|
||||||
|
|
||||||
static async #onCancel(event, target) {
|
static async #onCancel(event, target) {
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
@@ -102,12 +114,12 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found
|
|||||||
mutation
|
mutation
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
// N'efface les Dés d'Évolution que si l'item a bien été créé.
|
// Ne consomme que le coût du niveau choisi (Règles p. 116-117 : coût = niveau).
|
||||||
if (!created || created.length === 0) {
|
if (!created || created.length === 0) {
|
||||||
ui.notifications.warn(game.i18n.localize("VERMINE.error_evolution_create"));
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_evolution_create"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await actor.update({ "system.experience.evolution": 0 });
|
await actor.update({ "system.experience.evolution": Math.max(0, (actor.system?.experience?.evolution || 0) - cost) });
|
||||||
|
|
||||||
ui.notifications.info(game.i18n.localize("VERMINE.buy_evolution_done"));
|
ui.notifications.info(game.i18n.localize("VERMINE.buy_evolution_done"));
|
||||||
this.close();
|
this.close();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { VermineUtils } from "../roll.mjs";
|
import { VermineUtils } from "../roll.mjs";
|
||||||
import { VermineExchange } from "../exchange.mjs";
|
import { VermineExchange } from "../exchange.mjs";
|
||||||
|
import { VermineTraits } from "../traits.mjs";
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
@@ -50,12 +51,19 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
this.weapon = options.weapon ?? null;
|
this.weapon = options.weapon ?? null;
|
||||||
this.mode = options.mode ?? "contact";
|
this.mode = options.mode ?? "contact";
|
||||||
this.abilityKey = VermineExchange.attackAbility(this.mode);
|
this.abilityKey = VermineExchange.attackAbility(this.mode);
|
||||||
this.skillKey = this.weapon?.system?.skill || null;
|
// Une arme sans compétence définie est traitée comme une attaque
|
||||||
|
// improvisée au contact : Corps-à-corps (Vigueur + pool Corps-à-corps).
|
||||||
|
this.skillKey = this.weapon?.system?.skill || 'close';
|
||||||
this.skillValue = this.skillKey ? (this.actor.system?.skills?.[this.skillKey]?.value || 0) : 0;
|
this.skillValue = this.skillKey ? (this.actor.system?.skills?.[this.skillKey]?.value || 0) : 0;
|
||||||
this.skillPool = this.skillValue ? (CONFIG.VERMINE.SkillLevels?.[this.skillValue]?.dicePool || 0) : 0;
|
this.skillPool = this.skillValue ? (CONFIG.VERMINE.SkillLevels?.[this.skillValue]?.dicePool || 0) : 0;
|
||||||
this.skillReroll = this.skillValue ? (CONFIG.VERMINE.SkillLevels?.[this.skillValue]?.reroll || 0) : 0;
|
this.skillReroll = this.skillValue ? (CONFIG.VERMINE.SkillLevels?.[this.skillValue]?.reroll || 0) : 0;
|
||||||
this.basePool = VermineExchange.baseAttackPool(this.actor, this.weapon, this.mode);
|
this.basePool = VermineExchange.baseAttackPool(this.actor, this.weapon, this.mode);
|
||||||
this.baseDamage = VermineExchange.baseDamage(this.actor, this.weapon);
|
this.baseDamage = VermineExchange.baseDamage(this.actor, this.weapon);
|
||||||
|
this.rafaleValue = this.mode === "ranged" ? VermineTraits.traitValue(this.weapon, "rafale") : 0;
|
||||||
|
this.rapideHandicap = VermineTraits.traitValue(this.weapon, "rapide");
|
||||||
|
this.zoneValue = VermineTraits.traitValue(this.weapon, "zone");
|
||||||
|
const portee = VermineTraits.traitValue(this.weapon, "portee");
|
||||||
|
this.effectiveRange = this.mode === "ranged" && portee > 0 ? portee * VermineTraits.vigor(this.actor) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
@@ -111,9 +119,10 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
const creatureLocked = !isCreature && creatureTargets.length && creatureTargets.length === targets.length;
|
const creatureLocked = !isCreature && creatureTargets.length && creatureTargets.length === targets.length;
|
||||||
if (creatureLocked) {
|
if (creatureLocked) {
|
||||||
const ref = creatureTargets[0];
|
const ref = creatureTargets[0];
|
||||||
const creature = ref.id ? game.actors.get(ref.id) : null;
|
// uuid d'abord : un token non lié ne doit pas résoudre vers l'acteur
|
||||||
const actor = creature ?? (ref.uuid ? await fromUuid(ref.uuid) : null);
|
// monde dont il est issu (même principe que VermineExchange).
|
||||||
const d = parseInt(actor?.system?.combatStatus?.difficulty, 10) || 7;
|
const creature = await VermineExchange.resolveDefender({ id: ref.id, uuid: ref.uuid });
|
||||||
|
const d = parseInt(creature?.system?.combatStatus?.difficulty, 10) || 7;
|
||||||
lockedDifficulty = d;
|
lockedDifficulty = d;
|
||||||
difficultyOptions = [{ difficulty: d, label: "", locked: true }];
|
difficultyOptions = [{ difficulty: d, label: "", locked: true }];
|
||||||
defaultDifficulty = d;
|
defaultDifficulty = d;
|
||||||
@@ -145,9 +154,15 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
lockedDifficulty,
|
lockedDifficulty,
|
||||||
defaultDifficulty,
|
defaultDifficulty,
|
||||||
hasAmmo: this.mode === "ranged" && (this.weapon?.system?.ammo ?? 0) > 0,
|
hasAmmo: this.mode === "ranged" && (this.weapon?.system?.ammo ?? 0) > 0,
|
||||||
availableSpecialties: actor.items.filter(i => i.type === "specialty"),
|
// Specialties are only usable when they match the attack skill.
|
||||||
|
availableSpecialties: actor.items.filter(i => i.type === "specialty" && i.system?.skill === this.skillKey),
|
||||||
availableItems: actor.items.filter(i => i.type === "item"),
|
availableItems: actor.items.filter(i => i.type === "item"),
|
||||||
targets
|
targets,
|
||||||
|
lourdMalus: VermineTraits.lourdMalus(actor),
|
||||||
|
mobilityNote: VermineTraits.mobilityNote(actor),
|
||||||
|
hasRafale: this.rafaleValue > 0,
|
||||||
|
rafaleValue: this.rafaleValue,
|
||||||
|
effectiveRange: this.effectiveRange
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +182,7 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
|
|
||||||
this.element.querySelector("#second-action")?.addEventListener("change", () => this.#updateUI());
|
this.element.querySelector("#second-action")?.addEventListener("change", () => this.#updateUI());
|
||||||
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI());
|
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI());
|
||||||
|
this.element.querySelector("#rafale")?.addEventListener("change", () => this.#updateUI());
|
||||||
this.element.querySelector("#human-totem")?.addEventListener("change", () => this.#updateUI());
|
this.element.querySelector("#human-totem")?.addEventListener("change", () => this.#updateUI());
|
||||||
this.element.querySelector("#adapted-totem")?.addEventListener("change", () => this.#updateUI());
|
this.element.querySelector("#adapted-totem")?.addEventListener("change", () => this.#updateUI());
|
||||||
this.element.querySelector("#pools-spent")?.addEventListener("change", () => this.#updateUI());
|
this.element.querySelector("#pools-spent")?.addEventListener("change", () => this.#updateUI());
|
||||||
@@ -206,10 +222,18 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
return parseInt(this.#el.querySelector("#pools-spent")?.value, 10) || 0;
|
return parseInt(this.#el.querySelector("#pools-spent")?.value, 10) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#getTemporaryMalus() {
|
||||||
|
return parseInt(this.#el.querySelector("#temporary-malus")?.value, 10) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
#getUseAmmo() {
|
#getUseAmmo() {
|
||||||
return this.#el.querySelector("#use-ammo")?.checked ?? false;
|
return this.#el.querySelector("#use-ammo")?.checked ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#getRafale() {
|
||||||
|
return this.#el.querySelector("#rafale")?.checked ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
#getTotems() {
|
#getTotems() {
|
||||||
return {
|
return {
|
||||||
human: this.#el.querySelector("#human-totem")?.checked ?? false,
|
human: this.#el.querySelector("#human-totem")?.checked ?? false,
|
||||||
@@ -222,7 +246,7 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDifficultySelect() {
|
getDifficultySelect() {
|
||||||
return this.#getDifficultyBase() + (this.#getSecondAction() ? 2 : 0);
|
return this.#getDifficultyBase() + (this.#getSecondAction() ? 2 : 0) + (this.#getRafale() ? 2 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSkillCategory() {
|
getSkillCategory() {
|
||||||
@@ -244,8 +268,10 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMaxEffort() {
|
getMaxEffort() {
|
||||||
if (this.actor.type === "character") return this.actor.system?.abilities?.[this.abilityKey]?.value || 0;
|
if (this.actor.type !== "character") return 0;
|
||||||
return 0;
|
const ability = this.actor.system?.abilities?.[this.abilityKey]?.value || 0;
|
||||||
|
const effort = this.actor.system?.attributes?.effort?.value || 0;
|
||||||
|
return Math.min(ability, effort);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPoolBreakdown() {
|
getPoolBreakdown() {
|
||||||
@@ -260,9 +286,12 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
const toolsChecked = this.#el.querySelector("input[name='usingTools']:checked");
|
const toolsChecked = this.#el.querySelector("input[name='usingTools']:checked");
|
||||||
const tooling = toolsChecked && toolsChecked.value !== "0" ? 1 : 0;
|
const tooling = toolsChecked && toolsChecked.value !== "0" ? 1 : 0;
|
||||||
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
||||||
const malus = VermineExchange.woundMalus(this.actor);
|
const wound = VermineExchange.woundMalus(this.actor);
|
||||||
|
const lourd = VermineTraits.lourdMalus(this.actor);
|
||||||
|
const tempMalus = this.#getTemporaryMalus();
|
||||||
|
const malus = wound + lourd + tempMalus;
|
||||||
const total = Math.max(0, this.basePool + selfControl + pools + specialty + help + tooling + group - malus);
|
const total = Math.max(0, this.basePool + selfControl + pools + specialty + help + tooling + group - malus);
|
||||||
return { ability, skill, selfControl, pools, specialty, help, tooling, group, malus, total };
|
return { ability, skill, selfControl, pools, specialty, help, tooling, group, wound, lourd, tempMalus, malus, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
getDicePool() {
|
getDicePool() {
|
||||||
@@ -305,6 +334,14 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
if (handSel && handEl) {
|
if (handSel && handEl) {
|
||||||
handEl.textContent = handSel.options[handSel.selectedIndex]?.text ?? "";
|
handEl.textContent = handSel.options[handSel.selectedIndex]?.text ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const specChecked = this.#el.querySelector("input[name='usingSpecialization']:checked");
|
||||||
|
const specEl = this.#el.querySelector(".current-specialty");
|
||||||
|
if (specEl && specChecked) {
|
||||||
|
specEl.textContent = specChecked.value === "aucune"
|
||||||
|
? game.i18n.localize("VERMINE.none")
|
||||||
|
: specChecked.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#onInputChange() {
|
#onInputChange() {
|
||||||
@@ -365,9 +402,13 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
targets,
|
targets,
|
||||||
weapon: this.weapon
|
weapon: this.weapon
|
||||||
? { name: this.weapon.name, img: this.weapon.img, damage: foundry.utils.duplicate(this.weapon.system.damage) }
|
? { name: this.weapon.name, img: this.weapon.img, damage: foundry.utils.duplicate(this.weapon.system.damage) }
|
||||||
: null
|
: null,
|
||||||
|
rapideHandicap: this.rapideHandicap || null,
|
||||||
|
zoneValue: this.zoneValue || null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rafale = this.#getRafale();
|
||||||
|
|
||||||
const rollParams = {
|
const rollParams = {
|
||||||
actor: this.actor,
|
actor: this.actor,
|
||||||
NoD: this.getDicePool(),
|
NoD: this.getDicePool(),
|
||||||
@@ -389,6 +430,13 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
attack,
|
attack,
|
||||||
messageFlags: { "vermine-exchange": attack }
|
messageFlags: { "vermine-exchange": attack }
|
||||||
};
|
};
|
||||||
|
if (rafale && this.rafaleValue > 0) {
|
||||||
|
rollParams.bonusSuccesses = this.rafaleValue;
|
||||||
|
rollParams.bonusOnSuccess = true;
|
||||||
|
}
|
||||||
|
if (this.effectiveRange !== null) rollParams.effectiveRange = this.effectiveRange;
|
||||||
|
const mobilityHandicap = VermineTraits.mobilityHandicap(this.actor);
|
||||||
|
if (mobilityHandicap > 0) rollParams.mobilityHandicap = mobilityHandicap;
|
||||||
|
|
||||||
// Fermeture immédiate du dialogue : le jet (et l'animation 3D) continue
|
// Fermeture immédiate du dialogue : le jet (et l'animation 3D) continue
|
||||||
// en arrière-plan sans bloquer la fermeture.
|
// en arrière-plan sans bloquer la fermeture.
|
||||||
|
|||||||
@@ -48,13 +48,18 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
if (!memberOf.has(id)) memberOf.set(id, g.id);
|
if (!memberOf.has(id)) memberOf.set(id, g.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const groupById = new Map(groups.map(g => [g.id, g]));
|
||||||
return {
|
return {
|
||||||
characters: characters.map(a => ({
|
characters: characters.map(a => {
|
||||||
id: a.id,
|
const groupId = memberOf.get(a.id) || "";
|
||||||
name: a.name,
|
return {
|
||||||
dice: a.system?.experience?.dice || 0,
|
id: a.id,
|
||||||
groupId: memberOf.get(a.id) || ""
|
name: a.name,
|
||||||
})),
|
dice: a.system?.experience?.dice || 0,
|
||||||
|
groupId,
|
||||||
|
groupName: groupId ? groupById.get(groupId)?.name : ""
|
||||||
|
};
|
||||||
|
}),
|
||||||
groups: groups.map(a => ({
|
groups: groups.map(a => ({
|
||||||
id: a.id,
|
id: a.id,
|
||||||
name: a.name,
|
name: a.name,
|
||||||
@@ -72,6 +77,9 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
async _onRender(context, options) {
|
async _onRender(context, options) {
|
||||||
this.element.querySelectorAll("[data-roll]").forEach(inp => inp.addEventListener("change", () => this.#updateUI()));
|
this.element.querySelectorAll("[data-roll]").forEach(inp => inp.addEventListener("change", () => this.#updateUI()));
|
||||||
this.element.querySelector("#group-select")?.addEventListener("change", () => this.#updateGroupFilter());
|
this.element.querySelector("#group-select")?.addEventListener("change", () => this.#updateGroupFilter());
|
||||||
|
this.element.querySelectorAll("[name^='include-']").forEach(cb =>
|
||||||
|
cb.addEventListener("click", event => event.stopPropagation())
|
||||||
|
);
|
||||||
this.#updateUI();
|
this.#updateUI();
|
||||||
this.#updateGroupFilter();
|
this.#updateGroupFilter();
|
||||||
}
|
}
|
||||||
@@ -84,11 +92,16 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
if (el) el.textContent = `${danger + discoveries + duration}D`;
|
if (el) el.textContent = `${danger + discoveries + duration}D`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filtre les personnages selon le groupe sélectionné (vide = tous). */
|
/** Filtre les personnages selon le groupe sélectionné (vide = tous, "none" = sans groupe). */
|
||||||
#updateGroupFilter() {
|
#updateGroupFilter() {
|
||||||
const groupId = this.element.querySelector("#group-select")?.value || "";
|
const groupId = this.element.querySelector("#group-select")?.value || "";
|
||||||
for (const row of this.element.querySelectorAll("[data-character-id]")) {
|
for (const row of this.element.querySelectorAll("[data-character-id]")) {
|
||||||
const show = !groupId || row.dataset.groupId === groupId;
|
const rowGroupId = row.dataset.groupId || "";
|
||||||
|
const show = groupId === ""
|
||||||
|
? true
|
||||||
|
: groupId === "none"
|
||||||
|
? rowGroupId === ""
|
||||||
|
: rowGroupId === groupId;
|
||||||
row.hidden = !show;
|
row.hidden = !show;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,8 +115,8 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
+ (parseInt(this.element.querySelector("#collective-discoveries")?.value, 10) || 0)
|
+ (parseInt(this.element.querySelector("#collective-discoveries")?.value, 10) || 0)
|
||||||
+ (parseInt(this.element.querySelector("#collective-duration")?.value, 10) || 0);
|
+ (parseInt(this.element.querySelector("#collective-duration")?.value, 10) || 0);
|
||||||
|
|
||||||
const groupId = this.element.querySelector("#group-select")?.value || null;
|
const groupId = this.element.querySelector("#group-select")?.value || "";
|
||||||
if (groupId) {
|
if (groupId && groupId !== "none") {
|
||||||
const group = game.actors.get(groupId);
|
const group = game.actors.get(groupId);
|
||||||
if (group && collective > 0) {
|
if (group && collective > 0) {
|
||||||
const current = group.system?.experience?.dice || 0;
|
const current = group.system?.experience?.dice || 0;
|
||||||
@@ -121,8 +134,11 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
for (const row of this.element.querySelectorAll("[data-character-id]")) {
|
for (const row of this.element.querySelectorAll("[data-character-id]")) {
|
||||||
// Quand un groupe précis est choisi, ne récompenser que ses membres.
|
// Filtre par groupe sélectionné (vide = tous, "none" = sans groupe)
|
||||||
if (groupId && row.dataset.groupId !== groupId) continue;
|
const rowGroupId = row.dataset.groupId || "";
|
||||||
|
if (groupId === "none" ? rowGroupId !== "" : groupId !== "" && rowGroupId !== groupId) continue;
|
||||||
|
// Case « Inclure » décochée → personnage exclu de cette phase
|
||||||
|
if (!row.querySelector(`[name="include-${row.dataset.characterId}"]`)?.checked) continue;
|
||||||
const id = row.dataset.characterId;
|
const id = row.dataset.characterId;
|
||||||
const actor = game.actors.get(id);
|
const actor = game.actors.get(id);
|
||||||
if (!actor) continue;
|
if (!actor) continue;
|
||||||
@@ -164,7 +180,7 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lines = [`${game.i18n.localize("VERMINE.experience_phase")} +1`];
|
const lines = [`${game.i18n.localize("VERMINE.experience_phase")} +1`];
|
||||||
if (groupId) {
|
if (groupId && groupId !== "none") {
|
||||||
const group = game.actors.get(groupId);
|
const group = game.actors.get(groupId);
|
||||||
lines.push(`${group?.name ?? ""} (${game.i18n.localize("VERMINE.collective")}): +${collective}D`);
|
lines.push(`${group?.name ?? ""} (${game.i18n.localize("VERMINE.collective")}): +${collective}D`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,20 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
|
|||||||
return { total: items.length, byLevel };
|
return { total: items.length, byLevel };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bonus de l'atout "Socle de compétences" (règles p. 112).
|
||||||
|
* Rend le niveau (1-3) de l'atout possédé par le personnage, ou 0.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
#socleCompetencesBonus() {
|
||||||
|
const item = (this.#actor.itemTypes?.ability || []).find(i =>
|
||||||
|
i.system?.type === "character"
|
||||||
|
&& (i.name || "").trim().toLowerCase() === "socle de compétences".toLowerCase()
|
||||||
|
);
|
||||||
|
if (!item) return 0;
|
||||||
|
return Math.min(3, Math.max(1, parseInt(item.system?.level?.value, 10) || 1));
|
||||||
|
}
|
||||||
|
|
||||||
async _onRender() {
|
async _onRender() {
|
||||||
this.element.dataset.actorId = this.#actor.id;
|
this.element.dataset.actorId = this.#actor.id;
|
||||||
for (const inp of this.element.querySelectorAll("[data-roll]")) {
|
for (const inp of this.element.querySelectorAll("[data-roll]")) {
|
||||||
@@ -201,13 +215,16 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
|
|||||||
label = game.i18n.localize(VermineExperience.skillLevelLabelKey(targetLevel));
|
label = game.i18n.localize(VermineExperience.skillLevelLabelKey(targetLevel));
|
||||||
const diff = VermineExperience.skillLearningDifficulty(targetLevel);
|
const diff = VermineExperience.skillLearningDifficulty(targetLevel);
|
||||||
const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
|
const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
|
||||||
|
// Atout "Socle de compétences" : 1D gratuit si la Compétence est déjà possédée.
|
||||||
|
const socleBonus = current >= 1 ? this.#socleCompetencesBonus() : 0;
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
key,
|
key,
|
||||||
targetLevel,
|
targetLevel,
|
||||||
difficulty: domain ? Math.max(3, diff - 2) : diff,
|
difficulty: domain ? Math.max(3, diff - 2) : diff,
|
||||||
handicap: VermineExperience.skillLearningHandicap(targetLevel, rarity),
|
handicap: VermineExperience.skillLearningHandicap(targetLevel, rarity),
|
||||||
poolModifier: 0,
|
poolModifier: socleBonus,
|
||||||
|
socleBonus,
|
||||||
label,
|
label,
|
||||||
targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
|
targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
|
||||||
};
|
};
|
||||||
@@ -363,6 +380,15 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
|
|||||||
set("#diff-value", c.difficulty);
|
set("#diff-value", c.difficulty);
|
||||||
set("#handicap-value", c.handicap);
|
set("#handicap-value", c.handicap);
|
||||||
set("#required-value", 1 + c.handicap);
|
set("#required-value", 1 + c.handicap);
|
||||||
|
const socleHint = this.#el.querySelector("#socle-bonus-hint");
|
||||||
|
if (socleHint) {
|
||||||
|
if (type === "skill" && c.socleBonus > 0) {
|
||||||
|
socleHint.style.display = "";
|
||||||
|
socleHint.textContent = game.i18n.format("VERMINE.socle_competences_hint", { level: c.socleBonus });
|
||||||
|
} else {
|
||||||
|
socleHint.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
const pool = Math.max(0, this.#getDiceSpent() + c.poolModifier);
|
const pool = Math.max(0, this.#getDiceSpent() + c.poolModifier);
|
||||||
let poolText = `${pool}D${c.poolModifier ? ` (${c.poolModifier >= 0 ? "+" : ""}${c.poolModifier}D)` : ""}`;
|
let poolText = `${pool}D${c.poolModifier ? ` (${c.poolModifier >= 0 ? "+" : ""}${c.poolModifier}D)` : ""}`;
|
||||||
if (c.adaptedDie) poolText += " — " + game.i18n.localize("VERMINE.adapted_die_x2");
|
if (c.adaptedDie) poolText += " — " + game.i18n.localize("VERMINE.adapted_die_x2");
|
||||||
@@ -496,6 +522,14 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
|
|||||||
if ((roll._total ?? 0) >= required) {
|
if ((roll._total ?? 0) >= required) {
|
||||||
await this.#applySuccess(c, specialtyName, roll._total ?? 0);
|
await this.#applySuccess(c, specialtyName, roll._total ?? 0);
|
||||||
}
|
}
|
||||||
|
// Atout "Socle de compétences" : rappeler le bonus conféré aux camarades
|
||||||
|
// du Groupe qui apprennent une Compétence possédée par le personnage.
|
||||||
|
if (c.socleBonus > 0) {
|
||||||
|
await ChatMessage.create({
|
||||||
|
content: game.i18n.format("VERMINE.socle_competences_message", { level: c.socleBonus }),
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor })
|
||||||
|
});
|
||||||
|
}
|
||||||
// Les Dés d'Expérience sont perdus que le jet soit réussi ou raté.
|
// Les Dés d'Expérience sont perdus que le jet soit réussi ou raté.
|
||||||
await actor.update({ "system.experience.dice": Math.max(0, (actor.system.experience.dice || 0) - spent) });
|
await actor.update({ "system.experience.dice": Math.max(0, (actor.system.experience.dice || 0) - spent) });
|
||||||
this.close();
|
this.close();
|
||||||
|
|||||||
@@ -140,6 +140,21 @@ export default class NpcRollDialog extends HandlebarsApplicationMixin(foundry.ap
|
|||||||
inp.addEventListener("change", this.#onInputChange.bind(this))
|
inp.addEventListener("change", this.#onInputChange.bind(this))
|
||||||
}
|
}
|
||||||
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI())
|
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI())
|
||||||
|
// Trait Rapide (n) : le défenseur subit (n) Handicaps sur sa défense.
|
||||||
|
if (this.locks?.handicap !== undefined && this.locks?.handicap !== null) {
|
||||||
|
const sel = this.#el.querySelector("#handicap")
|
||||||
|
if (sel) {
|
||||||
|
const locked = parseInt(this.locks.handicap, 10)
|
||||||
|
let opt = [...sel.options].find(o => parseInt(o.value, 10) === locked + 1)
|
||||||
|
if (!opt) {
|
||||||
|
opt = document.createElement("option")
|
||||||
|
opt.value = String(locked + 1)
|
||||||
|
opt.textContent = game.i18n.localize("VERMINE.handicap") + ` (${locked})`
|
||||||
|
sel.appendChild(opt)
|
||||||
|
}
|
||||||
|
opt.selected = true
|
||||||
|
}
|
||||||
|
}
|
||||||
this.#updateUI()
|
this.#updateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,11 +174,15 @@ export default class NpcRollDialog extends HandlebarsApplicationMixin(foundry.ap
|
|||||||
|
|
||||||
#getGroup() { return parseInt(this.#el.querySelector("#group")?.value, 10) || 0 }
|
#getGroup() { return parseInt(this.#el.querySelector("#group")?.value, 10) || 0 }
|
||||||
|
|
||||||
|
#getTemporaryMalus() { return parseInt(this.#el.querySelector("#temporary-malus")?.value, 10) || 0 }
|
||||||
|
|
||||||
getPoolBreakdown() {
|
getPoolBreakdown() {
|
||||||
const help = this.#getHelped()
|
const help = this.#getHelped()
|
||||||
const group = this.#getGroup()
|
const group = this.#getGroup()
|
||||||
const malus = VermineExchange.woundMalus(this.#actor)
|
const wound = VermineExchange.woundMalus(this.#actor)
|
||||||
return { pool: this.pool, help, group, malus, total: Math.max(0, this.pool + help + group - malus) }
|
const tempMalus = this.#getTemporaryMalus()
|
||||||
|
const malus = wound + tempMalus
|
||||||
|
return { pool: this.pool, help, group, wound, tempMalus, malus, total: Math.max(0, this.pool + help + group - malus) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateUI() {
|
#updateUI() {
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { VermineReliability } from '../reliability.mjs';
|
||||||
|
|
||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogue de Test de Fiabilité / Réparation d'un item.
|
||||||
|
* Mode "test" : choix de la gravité des Dommages matériels (Mineurs/Graves/Critiques)
|
||||||
|
* + case Immersion (Trait Étanche). Mode "repair" : type de réparation
|
||||||
|
* (classique/de fortune) + compétence utilisée.
|
||||||
|
*/
|
||||||
|
export default class ReliabilityDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
|
||||||
|
#item;
|
||||||
|
#mode;
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ['vermine-roll', 'reliability'],
|
||||||
|
tag: 'form',
|
||||||
|
window: {
|
||||||
|
icon: 'fas fa-tools',
|
||||||
|
resizable: false,
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
width: 460,
|
||||||
|
height: 'auto',
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
run: ReliabilityDialog.#onRun,
|
||||||
|
cancel: ReliabilityDialog.#onCancel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
main: { template: 'systems/vermine2047/templates/dialogs/reliability-dialog.hbs', scrollable: [''] },
|
||||||
|
};
|
||||||
|
|
||||||
|
static async create({ item, mode = 'test' }) {
|
||||||
|
return new ReliabilityDialog({ item, mode });
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options);
|
||||||
|
this.#item = options.item;
|
||||||
|
this.#mode = options.mode ?? 'test';
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return game.i18n.localize(this.#mode === 'repair' ? 'VERMINE.reliability_repair' : 'VERMINE.reliability_test');
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext() {
|
||||||
|
const hasEtanche = !!this.#item.system?.traits?.etanche;
|
||||||
|
const hasActor = !!this.#item.actor;
|
||||||
|
const coque = this.#item.system?.traits?.coque ?? {};
|
||||||
|
const hasBrokenCoque = this.#mode === 'repair' && (parseInt(coque?.value ?? 0, 10) || 0) === 0 && (parseInt(coque?.max ?? 0, 10) || 0) > 0;
|
||||||
|
const storedSkill = (this.#item.system?.needSkill?.value && this.#item.system?.needSkill?.skill)
|
||||||
|
|| this.#item.system?.skill || '';
|
||||||
|
const severities = ['mineurs', 'graves', 'critiques'].map((key) => ({
|
||||||
|
key,
|
||||||
|
label: game.i18n.localize(`VERMINE.reliability_severity_${key}`),
|
||||||
|
selected: key === 'graves',
|
||||||
|
}));
|
||||||
|
const skills = VermineReliability.REPAIR_SKILLS.map((key) => ({
|
||||||
|
key,
|
||||||
|
label: game.i18n.localize(`SKILLS.${key}.name`),
|
||||||
|
selected: key === storedSkill && VermineReliability.REPAIR_SKILLS.includes(storedSkill),
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
mode: this.#mode,
|
||||||
|
isTest: this.#mode === 'test',
|
||||||
|
isRepair: this.#mode === 'repair',
|
||||||
|
hasEtanche,
|
||||||
|
hasActor,
|
||||||
|
hasBrokenCoque,
|
||||||
|
coqueMax: parseInt(coque?.max ?? 0, 10) || 0,
|
||||||
|
severities,
|
||||||
|
skills,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getOptions() {
|
||||||
|
const el = this.element;
|
||||||
|
if (this.#mode === 'test') {
|
||||||
|
return {
|
||||||
|
severity: el.querySelector('input[name="severity"]:checked')?.value ?? 'graves',
|
||||||
|
immersion: !!el.querySelector('#reliability-immersion')?.checked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
mode: el.querySelector('input[name="repairMode"]:checked')?.value ?? 'classique',
|
||||||
|
skill: el.querySelector('#reliability-skill')?.value ?? 'technology',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onRun() {
|
||||||
|
const options = this.#getOptions();
|
||||||
|
if (this.#mode === 'test') {
|
||||||
|
await VermineReliability.test(this.#item, options);
|
||||||
|
} else {
|
||||||
|
await VermineReliability.repair(this.#item, options);
|
||||||
|
}
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static #onCancel() {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { VermineUtils } from "../roll.mjs";
|
import { VermineUtils } from "../roll.mjs";
|
||||||
import { VermineExchange } from "../exchange.mjs";
|
import { VermineExchange } from "../exchange.mjs";
|
||||||
|
import { VermineTraits } from "../traits.mjs";
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
const isCharacter = actor.type === "character";
|
const isCharacter = actor.type === "character";
|
||||||
const group = isCharacter ? RollDialog.#findGroup(actor) : null;
|
const group = isCharacter ? RollDialog.#findGroup(actor) : null;
|
||||||
const groupPool = group?.system?.experience?.dice || 0;
|
const groupPool = group?.system?.experience?.dice || 0;
|
||||||
|
const intimidant = isCharacter ? VermineTraits.equippedItems(actor, 'intimidant')[0] ?? null : null;
|
||||||
return {
|
return {
|
||||||
actor,
|
actor,
|
||||||
system: actor.system,
|
system: actor.system,
|
||||||
@@ -78,6 +80,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
specialty: false,
|
specialty: false,
|
||||||
availableSpecialties: actor.items.filter(i => i.type === "specialty"),
|
availableSpecialties: actor.items.filter(i => i.type === "specialty"),
|
||||||
availableItems: actor.items.filter(i => i.type === "item"),
|
availableItems: actor.items.filter(i => i.type === "item"),
|
||||||
|
hasIntimidant: Boolean(intimidant),
|
||||||
|
intimidantName: intimidant?.name ?? "",
|
||||||
locks: this.locks,
|
locks: this.locks,
|
||||||
experience: {
|
experience: {
|
||||||
hasExperience: isCharacter,
|
hasExperience: isCharacter,
|
||||||
@@ -140,6 +144,50 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
#getSelfCtrl() { return this.#el.querySelector("#self_control"); }
|
#getSelfCtrl() { return this.#el.querySelector("#self_control"); }
|
||||||
#getExperiencePull() { return parseInt(this.#el.querySelector("#experience-pull")?.value, 10) || 0; }
|
#getExperiencePull() { return parseInt(this.#el.querySelector("#experience-pull")?.value, 10) || 0; }
|
||||||
|
|
||||||
|
/** Clé de la compétence sélectionnée (data-label) ou null. */
|
||||||
|
getSelectedSkillKey() {
|
||||||
|
const sel = this.#getSkill();
|
||||||
|
return sel?.options[sel.selectedIndex]?.dataset?.label ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Outil générique sélectionné (radio usingTools) ou null. */
|
||||||
|
getSelectedTool() {
|
||||||
|
const checked = this.#el.querySelector("input[name='usingTools']:checked");
|
||||||
|
if (!checked || checked.value === "0") return null;
|
||||||
|
return this.#actor.items.find(i => i.name === checked.value) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bonus d'outillage : +1D, ou +2D si l'outil possède le Trait Pratique. */
|
||||||
|
#getTooling() {
|
||||||
|
const tool = this.getSelectedTool();
|
||||||
|
if (!tool) return 0;
|
||||||
|
return VermineTraits.hasTrait(tool, 'pratique') ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Malus temporaires saisis manuellement (Trait Malus). */
|
||||||
|
#getTemporaryMalus() {
|
||||||
|
return parseInt(this.#el.querySelector("#temporary-malus")?.value, 10) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Case « Brandit » (Trait Intimidant). */
|
||||||
|
getIntimidant() {
|
||||||
|
return this.#el.querySelector("#intimidant")?.checked ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Valeur de Handicap brute du sélecteur (0-2). */
|
||||||
|
#getBaseHandicap() {
|
||||||
|
const sel = this.#getHandicap();
|
||||||
|
return Math.max(0, (parseInt(sel?.value, 10) || 1) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Nombre de Handicaps annulés par l'outil Ponctuel sélectionné. */
|
||||||
|
getPonctuelUsed() {
|
||||||
|
const tool = this.getSelectedTool();
|
||||||
|
const ponctuel = tool ? VermineTraits.traitValue(tool, 'ponctuel') : 0;
|
||||||
|
if (ponctuel <= 0) return 0;
|
||||||
|
return Math.min(ponctuel, this.#getBaseHandicap());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trouve le premier Groupe dont le personnage est membre.
|
* Trouve le premier Groupe dont le personnage est membre.
|
||||||
* @param {Actor} actor
|
* @param {Actor} actor
|
||||||
@@ -158,16 +206,17 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
const selfControl = parseInt(this.#getSelfCtrl()?.value, 10) || 0;
|
const selfControl = parseInt(this.#getSelfCtrl()?.value, 10) || 0;
|
||||||
const specChecked = this.hasSpecialtySelected();
|
const specChecked = this.hasSpecialtySelected();
|
||||||
const helped = this.#el.querySelector("#helped")?.checked;
|
const helped = this.#el.querySelector("#helped")?.checked;
|
||||||
const toolsChecked = this.#el.querySelector("input[name='usingTools']:checked");
|
|
||||||
const tools = toolsChecked && toolsChecked.value !== "0";
|
|
||||||
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
||||||
const experience = this.#getExperiencePull();
|
const experience = this.#getExperiencePull();
|
||||||
const specialty = specChecked ? 1 : 0;
|
const specialty = specChecked ? 1 : 0;
|
||||||
const help = helped ? 1 : 0;
|
const help = helped ? 1 : 0;
|
||||||
const tooling = tools ? 1 : 0;
|
const tooling = this.#getTooling();
|
||||||
const malus = VermineExchange.woundMalus(this.#actor);
|
const wound = VermineExchange.woundMalus(this.#actor);
|
||||||
|
const tempMalus = this.#getTemporaryMalus();
|
||||||
|
const psychology = this.getIntimidant() && this.getSelectedSkillKey() === 'psychology' ? 1 : 0;
|
||||||
|
const malus = wound + tempMalus + psychology;
|
||||||
const total = Math.max(0, ability + skillPool + selfControl + specialty + help + tooling + group + experience - malus);
|
const total = Math.max(0, ability + skillPool + selfControl + specialty + help + tooling + group + experience - malus);
|
||||||
return { ability, skill: skillPool, selfControl, specialty, help, tooling, group, experience, malus, total };
|
return { ability, skill: skillPool, selfControl, specialty, help, tooling, group, experience, wound, tempMalus, psychology, malus, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
getDicePool() {
|
getDicePool() {
|
||||||
@@ -188,12 +237,15 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
getReroll() {
|
getReroll() {
|
||||||
const sel = this.#getSkill();
|
const sel = this.#getSkill();
|
||||||
const idx = sel?.selectedIndex ?? 0;
|
const idx = sel?.selectedIndex ?? 0;
|
||||||
return parseInt(sel?.options[idx]?.dataset?.reroll, 10) || 0;
|
const base = parseInt(sel?.options[idx]?.dataset?.reroll, 10) || 0;
|
||||||
|
// Trait Intimidant (hors Psychologie) : Relance 1D tant que l'objet est brandi.
|
||||||
|
if (this.getIntimidant() && this.getSelectedSkillKey() !== 'psychology') return base + 1;
|
||||||
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHandicapSelect() {
|
getHandicapSelect() {
|
||||||
const sel = this.#getHandicap();
|
// Trait Ponctuel : l'outil sélectionné annule jusqu'à (n) Handicaps.
|
||||||
return Math.max(0, (parseInt(sel?.value, 10) || 1) - 1);
|
return this.#getBaseHandicap() - this.getPonctuelUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSkillCategory() {
|
getSkillCategory() {
|
||||||
@@ -221,14 +273,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
|
|
||||||
getLabel() {
|
getLabel() {
|
||||||
const type = this.getRollType();
|
const type = this.getRollType();
|
||||||
if (type === "skill") {
|
const sel = type === "skill" ? this.#getSkill() : this.#getAbility();
|
||||||
const sel = this.#getSkill();
|
|
||||||
const idx = sel?.selectedIndex ?? 0;
|
|
||||||
return sel?.options[idx]?.dataset?.label ?? "";
|
|
||||||
}
|
|
||||||
const sel = this.#getAbility();
|
|
||||||
const idx = sel?.selectedIndex ?? 0;
|
const idx = sel?.selectedIndex ?? 0;
|
||||||
return sel?.options[idx]?.dataset?.label ?? "";
|
const raw = sel?.options[idx]?.dataset?.label ?? "";
|
||||||
|
if (!raw) return "";
|
||||||
|
const base = type === "skill" ? "SKILLS" : "ABILITIES";
|
||||||
|
const localized = game.i18n.localize(`${base}.${raw}.name`);
|
||||||
|
return localized === `${base}.${raw}.name` ? raw : localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelfControl() {
|
getSelfControl() {
|
||||||
@@ -237,7 +288,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
|
|
||||||
getMaxEffort() {
|
getMaxEffort() {
|
||||||
const sel = this.#getAbility();
|
const sel = this.#getAbility();
|
||||||
return parseInt(sel?.value, 10) || 0;
|
const ability = parseInt(sel?.value, 10) || 0;
|
||||||
|
const effort = this.#actor?.system?.attributes?.effort?.value || 0;
|
||||||
|
return Math.min(ability, effort);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotems() {
|
getTotems() {
|
||||||
@@ -285,6 +338,19 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
opt.selected = true;
|
opt.selected = true;
|
||||||
difficulty.disabled = true;
|
difficulty.disabled = true;
|
||||||
}
|
}
|
||||||
|
const handicap = this.#getHandicap();
|
||||||
|
if (handicap && locks.handicap !== undefined && locks.handicap !== null) {
|
||||||
|
const locked = parseInt(locks.handicap, 10);
|
||||||
|
let opt = [...handicap.options].find(o => parseInt(o.value, 10) === locked + 1);
|
||||||
|
if (!opt) {
|
||||||
|
// Handicap supérieur aux options (ex: Rapide 3) : option dédiée.
|
||||||
|
opt = document.createElement("option");
|
||||||
|
opt.value = String(locked + 1);
|
||||||
|
opt.textContent = game.i18n.localize("VERMINE.handicap") + ` (${locked})`;
|
||||||
|
handicap.appendChild(opt);
|
||||||
|
}
|
||||||
|
opt.selected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#displaySpecialties() {
|
#displaySpecialties() {
|
||||||
@@ -342,7 +408,10 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
const handSel = this.#getHandicap();
|
const handSel = this.#getHandicap();
|
||||||
const handEl = this.#el.querySelector("#current-handicap");
|
const handEl = this.#el.querySelector("#current-handicap");
|
||||||
if (handEl && handSel) {
|
if (handEl && handSel) {
|
||||||
handEl.textContent = handSel.options[handSel.selectedIndex].text;
|
let text = handSel.options[handSel.selectedIndex].text;
|
||||||
|
const used = this.getPonctuelUsed();
|
||||||
|
if (used > 0) text += ` (${game.i18n.localize("VERMINE.ponctuel")} -${used})`;
|
||||||
|
handEl.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const abilSel = this.#getAbility();
|
const abilSel = this.#getAbility();
|
||||||
@@ -450,6 +519,22 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
await group.update({ "system.experience.dice": groupPool - pulled });
|
await group.update({ "system.experience.dice": groupPool - pulled });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trait Ponctuel : consomme (n) charges de l'outil sélectionné pour annuler
|
||||||
|
// les Handicaps ; l'objet perd définitivement le Trait si la valeur tombe à 0.
|
||||||
|
const ponctuelUsed = this.getPonctuelUsed();
|
||||||
|
const tool = this.getSelectedTool();
|
||||||
|
if (tool && ponctuelUsed > 0) {
|
||||||
|
const current = VermineTraits.traitValue(tool, 'ponctuel');
|
||||||
|
const remaining = current - ponctuelUsed;
|
||||||
|
if (remaining <= 0) {
|
||||||
|
await tool.update({ "system.traits.ponctuel": null });
|
||||||
|
} else {
|
||||||
|
await tool.update({ "system.traits.ponctuel.value": remaining });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const intimidant = this.getIntimidant();
|
||||||
|
|
||||||
const rollParams = {
|
const rollParams = {
|
||||||
actor: this.#actor,
|
actor: this.#actor,
|
||||||
NoD: this.getDicePool(),
|
NoD: this.getDicePool(),
|
||||||
@@ -470,6 +555,10 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
targets: [...game.user.targets].map(t => t.name),
|
targets: [...game.user.targets].map(t => t.name),
|
||||||
messageFlags: this.defense ? { "vermine-defense": this.defense } : null
|
messageFlags: this.defense ? { "vermine-defense": this.defense } : null
|
||||||
};
|
};
|
||||||
|
if (intimidant) {
|
||||||
|
rollParams.intimidant = { used: true, psychology: this.getSelectedSkillKey() === 'psychology' };
|
||||||
|
}
|
||||||
|
if (ponctuelUsed > 0) rollParams.ponctuelUsed = ponctuelUsed;
|
||||||
|
|
||||||
// Fermeture immédiate du dialogue : le jet (et l'animation 3D) continue
|
// Fermeture immédiate du dialogue : le jet (et l'animation 3D) continue
|
||||||
// en arrière-plan sans bloquer la fermeture.
|
// en arrière-plan sans bloquer la fermeture.
|
||||||
|
|||||||
+135
-29
@@ -258,17 +258,19 @@ export class VermineExchange {
|
|||||||
* Résout l'échange pour une cible donnée (défense la plus récente liée
|
* Résout l'échange pour une cible donnée (défense la plus récente liée
|
||||||
* à cette cible, ou abstention si aucune défense).
|
* à cette cible, ou abstention si aucune défense).
|
||||||
* @param {ChatMessage} attackMessage
|
* @param {ChatMessage} attackMessage
|
||||||
* @param {string|null} targetId id d'acteur de la cible (défaut : première cible)
|
* @param {Object|string|null} targetRef cible de l'attaque (ou son uuid),
|
||||||
|
* défaut : première cible — le matching se fait par uuid d'abord car des
|
||||||
|
* PNJ mineurs (tokens non liés) issus de la même fiche partagent le même id
|
||||||
* @returns {Promise<Object|null>} résultat de la résolution
|
* @returns {Promise<Object|null>} résultat de la résolution
|
||||||
*/
|
*/
|
||||||
static async resolve(attackMessage, targetId = null) {
|
static async resolve(attackMessage, targetRef = null) {
|
||||||
const exchange = attackMessage.getFlag('world', FLAG_EXCHANGE)
|
const exchange = attackMessage.getFlag('world', FLAG_EXCHANGE)
|
||||||
if (!exchange) return null
|
if (!exchange) return null
|
||||||
|
|
||||||
const target = (targetId && exchange.targets?.find(t => t.id === targetId)) || exchange.targets?.[0] || null
|
const target = this.#matchTarget(exchange.targets || [], targetRef)
|
||||||
if (!target) return null
|
if (!target) return null
|
||||||
|
|
||||||
const defs = this.#defenseMessagesFor(exchange.id, target.id)
|
const defs = this.#defenseMessagesFor(exchange.id, target)
|
||||||
const defenderMessage = defs[defs.length - 1]
|
const defenderMessage = defs[defs.length - 1]
|
||||||
const defense = defenderMessage ? defenderMessage.getFlag('world', FLAG_DEFENSE) : null
|
const defense = defenderMessage ? defenderMessage.getFlag('world', FLAG_DEFENSE) : null
|
||||||
|
|
||||||
@@ -281,7 +283,7 @@ export class VermineExchange {
|
|||||||
const defenderRef = defense
|
const defenderRef = defense
|
||||||
? { id: defense.actorId ?? null, uuid: defense.actorUuid ?? null, name: defense.actorName ?? '' }
|
? { id: defense.actorId ?? null, uuid: defense.actorUuid ?? null, name: defense.actorName ?? '' }
|
||||||
: target
|
: target
|
||||||
const defender = await this.#resolveDefender(defenderRef)
|
const defender = await this.resolveDefender(defenderRef)
|
||||||
|
|
||||||
const damage = (exchange.baseDamage || 0) + attackerSuccesses
|
const damage = (exchange.baseDamage || 0) + attackerSuccesses
|
||||||
const parryLevel = defense?.type === 'parade' ? (defense.parryLevel ?? 0) : null
|
const parryLevel = defense?.type === 'parade' ? (defense.parryLevel ?? 0) : null
|
||||||
@@ -369,6 +371,12 @@ export class VermineExchange {
|
|||||||
html.querySelectorAll('[data-action="exchange-apply"]').forEach(btn => {
|
html.querySelectorAll('[data-action="exchange-apply"]').forEach(btn => {
|
||||||
btn.addEventListener('click', ev => this.#onApply(ev, message))
|
btn.addEventListener('click', ev => this.#onApply(ev, message))
|
||||||
})
|
})
|
||||||
|
html.querySelectorAll('.exchange-incapacitant').forEach(btn => {
|
||||||
|
btn.addEventListener('click', ev => this.#onIncapacitant(ev, message))
|
||||||
|
})
|
||||||
|
html.querySelectorAll('.exchange-zone').forEach(btn => {
|
||||||
|
btn.addEventListener('click', ev => this.#onApplyZone(ev, message))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #onDefense(ev, attackMessage) {
|
static async #onDefense(ev, attackMessage) {
|
||||||
@@ -378,7 +386,7 @@ export class VermineExchange {
|
|||||||
const exchange = attackMessage.getFlag('world', FLAG_EXCHANGE)
|
const exchange = attackMessage.getFlag('world', FLAG_EXCHANGE)
|
||||||
if (!exchange) return
|
if (!exchange) return
|
||||||
const type = btn.dataset.type
|
const type = btn.dataset.type
|
||||||
const actor = await this.#resolveDefender({ id: btn.dataset.targetId, uuid: btn.dataset.targetUuid })
|
const actor = await this.resolveDefender({ id: btn.dataset.targetId, uuid: btn.dataset.targetUuid })
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected'))
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected'))
|
||||||
return
|
return
|
||||||
@@ -407,7 +415,7 @@ export class VermineExchange {
|
|||||||
const dialog = await NpcRollDialog.create({
|
const dialog = await NpcRollDialog.create({
|
||||||
actor,
|
actor,
|
||||||
rolltype: 'action',
|
rolltype: 'action',
|
||||||
locks: { difficulty: exchange.difficulty },
|
locks: { difficulty: exchange.difficulty, handicap: exchange.rapideHandicap ?? null },
|
||||||
defense
|
defense
|
||||||
})
|
})
|
||||||
if (dialog) dialog.render(true)
|
if (dialog) dialog.render(true)
|
||||||
@@ -426,14 +434,16 @@ export class VermineExchange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static #defenseLocks(actor, exchange, type) {
|
static #defenseLocks(actor, exchange, type) {
|
||||||
if (type === 'parade') {
|
const locks = type === 'parade'
|
||||||
return { ability: 'vigor', skill: this.#parrySkill(actor), difficulty: exchange.difficulty }
|
? { ability: 'vigor', skill: this.#parrySkill(actor), difficulty: exchange.difficulty }
|
||||||
}
|
: {
|
||||||
return {
|
ability: 'reflexes',
|
||||||
ability: 'reflexes',
|
skill: exchange.mode === 'ranged' ? 'alertness' : 'close',
|
||||||
skill: exchange.mode === 'ranged' ? 'alertness' : 'close',
|
difficulty: exchange.difficulty
|
||||||
difficulty: exchange.difficulty
|
}
|
||||||
}
|
// Trait Rapide (n) : le défenseur subit (n) Handicaps sur sa défense.
|
||||||
|
if (exchange.rapideHandicap) locks.handicap = exchange.rapideHandicap
|
||||||
|
return locks
|
||||||
}
|
}
|
||||||
|
|
||||||
static #parrySkill(actor) {
|
static #parrySkill(actor) {
|
||||||
@@ -466,7 +476,7 @@ export class VermineExchange {
|
|||||||
|
|
||||||
const targets = exchange.targets?.length ? exchange.targets : [null]
|
const targets = exchange.targets?.length ? exchange.targets : [null]
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
const result = await this.resolve(attackMessage, target?.id ?? null)
|
const result = await this.resolve(attackMessage, target)
|
||||||
if (!result) continue
|
if (!result) continue
|
||||||
const block = document.createElement('div')
|
const block = document.createElement('div')
|
||||||
block.innerHTML = this.renderResolution(result)
|
block.innerHTML = this.renderResolution(result)
|
||||||
@@ -479,10 +489,79 @@ export class VermineExchange {
|
|||||||
if (msgEl) await attackMessage.update({ content: msgEl.outerHTML })
|
if (msgEl) await attackMessage.update({ content: msgEl.outerHTML })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onIncapacitant(ev) {
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
|
const btn = ev.currentTarget
|
||||||
|
const actor = await this.resolveDefender({ id: btn.dataset.targetId, uuid: btn.dataset.targetUuid })
|
||||||
|
if (!actor) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!game.user.isGM && !actor.isOwner) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_permission'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const value = parseInt(btn.dataset.value, 10) || 7
|
||||||
|
if (actor.type === 'character') {
|
||||||
|
const { default: RollDialog } = await import('./dialogs/rollDialog.mjs')
|
||||||
|
const dialog = await RollDialog.create({
|
||||||
|
actor,
|
||||||
|
rolltype: 'ability',
|
||||||
|
label: 'health',
|
||||||
|
locks: { ability: 'health', difficulty: value }
|
||||||
|
})
|
||||||
|
if (dialog) dialog.render(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { default: NpcRollDialog } = await import('./dialogs/npcRollDialog.mjs')
|
||||||
|
const dialog = await NpcRollDialog.create({
|
||||||
|
actor,
|
||||||
|
rolltype: 'action',
|
||||||
|
locks: { difficulty: value }
|
||||||
|
})
|
||||||
|
if (dialog) dialog.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait Zone (n) : applique la blessure résolue à toutes les cibles de
|
||||||
|
* l'attaque présentes dans la zone (confort pour le MJ).
|
||||||
|
*/
|
||||||
|
static async #onApplyZone(ev, attackMessage) {
|
||||||
|
ev.preventDefault()
|
||||||
|
ev.stopPropagation()
|
||||||
|
const btn = ev.currentTarget
|
||||||
|
const exchange = attackMessage.getFlag('world', FLAG_EXCHANGE)
|
||||||
|
if (!exchange) return
|
||||||
|
if (!game.user.isGM && !attackMessage.isOwner) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_permission'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!exchange.targets?.length) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_target'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let applied = 0
|
||||||
|
for (const target of exchange.targets) {
|
||||||
|
const r = await this.resolve(attackMessage, target)
|
||||||
|
if (!r?.hit || !r.woundCategory) continue
|
||||||
|
const defender = await this.resolveDefender({ id: r.defenderId, uuid: r.defenderUuid })
|
||||||
|
if (!defender || !this.canApplyWound(defender)) continue
|
||||||
|
const result = await this.applyWound(defender, r.woundCategory)
|
||||||
|
if (result && result !== false) applied += 1
|
||||||
|
}
|
||||||
|
btn.disabled = true
|
||||||
|
btn.innerHTML = `<i class="fas fa-check"></i> ${game.i18n.format('VERMINE.zone_applied', { count: applied })}`
|
||||||
|
if (game.user.isGM || attackMessage.isOwner) {
|
||||||
|
const msgEl = btn.closest('.vermine-roll-message')
|
||||||
|
if (msgEl) await attackMessage.update({ content: msgEl.outerHTML })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async #onApply(ev, attackMessage) {
|
static async #onApply(ev, attackMessage) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
const btn = ev.currentTarget
|
const btn = ev.currentTarget
|
||||||
const defender = await this.#resolveDefender({ id: btn.dataset.defenderId, uuid: btn.dataset.defenderUuid })
|
const defender = await this.resolveDefender({ id: btn.dataset.defenderId, uuid: btn.dataset.defenderUuid })
|
||||||
const wound = btn.dataset.wound
|
const wound = btn.dataset.wound
|
||||||
if (!defender || !wound) return
|
if (!defender || !wound) return
|
||||||
if (!this.canApplyWound(defender)) {
|
if (!this.canApplyWound(defender)) {
|
||||||
@@ -511,31 +590,58 @@ export class VermineExchange {
|
|||||||
// ── Helpers internes ────────────────────────────────────────────────
|
// ── Helpers internes ────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Résout un acteur depuis son id d'acteur monde (acteurs liés) ou son
|
* Résout l'acteur d'une cible depuis sa référence (id d'acteur monde ou
|
||||||
* uuid (tokens non liés via fromUuid).
|
* uuid). Le uuid est privilégié : pour un token non lié, `token.actor.id`
|
||||||
|
* expose encore l'id de l'acteur monde dont le token est issu, et résoudre
|
||||||
|
* par id blesserait alors tous les tokens liés à cette fiche (PNJ mineurs).
|
||||||
|
* Le uuid d'un acteur synthétique ("Scene.x.Token.y.Actor.z") n'est pas
|
||||||
|
* résolvable de façon fiable par fromUuid (document non indexé quand la
|
||||||
|
* résolution est asynchrone) : on extrait donc directement le token.
|
||||||
* @param {{id: string|null, uuid: string|null}} ref
|
* @param {{id: string|null, uuid: string|null}} ref
|
||||||
* @returns {Promise<Actor|null>}
|
* @returns {Promise<Actor|null>}
|
||||||
*/
|
*/
|
||||||
static async #resolveDefender({ id = null, uuid = null }) {
|
static async resolveDefender({ id = null, uuid = null }) {
|
||||||
|
if (uuid) {
|
||||||
|
const parts = uuid.split('.')
|
||||||
|
if (parts[0] === 'Scene' && parts[2] === 'Token') {
|
||||||
|
const token = game.scenes.get(parts[1])?.tokens.get(parts[3])
|
||||||
|
return token?.actor ?? null
|
||||||
|
}
|
||||||
|
if (parts[0] === 'Actor') {
|
||||||
|
return game.actors.get(parts[1]) ?? null
|
||||||
|
}
|
||||||
|
}
|
||||||
if (id) {
|
if (id) {
|
||||||
const actor = game.actors.get(id)
|
const actor = game.actors.get(id)
|
||||||
if (actor) return actor
|
if (actor) return actor
|
||||||
}
|
}
|
||||||
if (uuid) {
|
|
||||||
const doc = await fromUuid(uuid)
|
|
||||||
if (!doc) return null
|
|
||||||
if (doc.documentType === 'Actor') return doc
|
|
||||||
if (doc.documentType === 'Token') return doc.actor ?? null
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
static #defenseMessagesFor(exchangeId, targetId = null) {
|
/**
|
||||||
|
* Retrouve la cible d'un échange par uuid d'abord, puis par id. Le uuid est
|
||||||
|
* fiable pour distinguer des PNJ mineurs (tokens non liés) issus de la même
|
||||||
|
* fiche, qui partagent le même id d'acteur monde.
|
||||||
|
* @param {Object[]} targets cibles du flag d'échange
|
||||||
|
* @param {Object|string|null} ref référence de la cible (ou son uuid)
|
||||||
|
* @returns {Object|null}
|
||||||
|
*/
|
||||||
|
static #matchTarget(targets, ref) {
|
||||||
|
if (!ref) return targets[0] ?? null
|
||||||
|
const uuid = typeof ref === 'string' ? ref : (ref.uuid ?? null)
|
||||||
|
const id = typeof ref === 'string' ? null : (ref.id ?? null)
|
||||||
|
if (uuid) return targets.find(t => t.uuid === uuid) ?? null
|
||||||
|
if (id) return targets.find(t => t.id === id) ?? null
|
||||||
|
return targets[0] ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
static #defenseMessagesFor(exchangeId, target = null) {
|
||||||
return game.messages.contents.filter(m => {
|
return game.messages.contents.filter(m => {
|
||||||
const f = m.getFlag('world', FLAG_DEFENSE)
|
const f = m.getFlag('world', FLAG_DEFENSE)
|
||||||
if (!f || f.exchangeId !== exchangeId) return false
|
if (!f || f.exchangeId !== exchangeId) return false
|
||||||
if (targetId) return f.actorId === targetId
|
if (!target) return true
|
||||||
return true
|
if (target.uuid && f.actorUuid === target.uuid) return true
|
||||||
|
return f.actorId === target.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+69
-9
@@ -210,19 +210,29 @@ export class VermineCombat extends Combat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************************/
|
/************************************************************************************/
|
||||||
_onDelete() {
|
/**
|
||||||
/*let combatants = this.combatants.contents
|
* Nettoyage lors de la suppression du combat (bouton « Terminer » / suppression).
|
||||||
for (let c of combatants) {
|
* Doit appeler le handler de base : c'est lui qui re-rend le CombatTracker
|
||||||
let actor = game.actors.get(c.actorId)
|
* (`ui.combat.render({combat: null})`) — sans cela le tracker reste figé sur
|
||||||
actor.clearInitiative()
|
* l'ancien ordre de tour jusqu'au rechargement de la page (F5).
|
||||||
actor.displayRecuperation()
|
* @param {object} options
|
||||||
}
|
* @param {string} userId
|
||||||
super._onDelete()*/
|
*/
|
||||||
|
_onDelete(options, userId) {
|
||||||
|
super._onDelete(options, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
actions: {
|
||||||
|
deleteCombatant: VermineCombatTracker.#onDeleteCombatant,
|
||||||
|
deleteCombat: VermineCombatTracker.#onDeleteCombat
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
main: {
|
main: {
|
||||||
@@ -248,13 +258,17 @@ export class VermineCombatTracker extends foundry.applications.sidebar.tabs.Comb
|
|||||||
Object.assign(context, {
|
Object.assign(context, {
|
||||||
combat,
|
combat,
|
||||||
hasCombat,
|
hasCombat,
|
||||||
|
combats: combats.map((c, i) => ({ id: c.id, name: c.name, label: i + 1, active: i === currentIdx })),
|
||||||
combatCount: combats.length,
|
combatCount: combats.length,
|
||||||
currentIndex: currentIdx + 1,
|
currentIndex: currentIdx + 1,
|
||||||
previousId: combats[currentIdx - 1]?.id,
|
previousId: combats[currentIdx - 1]?.id,
|
||||||
nextId: combats[currentIdx + 1]?.id,
|
nextId: combats[currentIdx + 1]?.id,
|
||||||
round: combat?.round,
|
round: combat?.round,
|
||||||
|
displayCycle: combats.length > 7,
|
||||||
control: isPlayerTurn && canControl,
|
control: isPlayerTurn && canControl,
|
||||||
linked: combat?.scene !== null,
|
linked: combat?.scene !== null,
|
||||||
|
initiativeIcon: CONFIG.Combat.initiativeIcon,
|
||||||
|
css: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
|
||||||
cssClass: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
|
cssClass: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
|
||||||
cssId: "combat",
|
cssId: "combat",
|
||||||
tabName: "combat",
|
tabName: "combat",
|
||||||
@@ -264,12 +278,21 @@ export class VermineCombatTracker extends foundry.applications.sidebar.tabs.Comb
|
|||||||
turns: []
|
turns: []
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tracker part : construit les tours + gestion des décimales d'initiative.
|
||||||
|
let hasDecimals = false;
|
||||||
if ( combat ) {
|
if ( combat ) {
|
||||||
for ( const [i, combatant] of combat.turns.entries() ) {
|
for ( const [i, combatant] of combat.turns.entries() ) {
|
||||||
if ( !combatant.visible ) continue;
|
if ( !combatant.visible ) continue;
|
||||||
context.turns.push(await this._prepareTurnContext(combat, combatant, i));
|
const turn = await this._prepareTurnContext(combat, combatant, i);
|
||||||
|
if ( turn.hasDecimals ) hasDecimals = true;
|
||||||
|
context.turns.push(turn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const precision = CONFIG.Combat.initiative.decimals;
|
||||||
|
context.turns.forEach(t => {
|
||||||
|
if ( Number.isFinite(t.initiative) ) t.initiative = t.initiative.toFixed(hasDecimals ? precision : 0);
|
||||||
|
});
|
||||||
|
context.hasDecimals = hasDecimals;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +341,31 @@ export class VermineCombatTracker extends foundry.applications.sidebar.tabs.Comb
|
|||||||
let actor = await game.actors.get(combatant.actorId)
|
let actor = await game.actors.get(combatant.actorId)
|
||||||
// combatant attitude set
|
// combatant attitude set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un combatant du combat en cours.
|
||||||
|
* @param {PointerEvent} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
*/
|
||||||
|
static async #onDeleteCombatant(event, target) {
|
||||||
|
const combatantId = target.closest("[data-combatant-id]")?.dataset?.combatantId
|
||||||
|
const combatant = this.viewed?.combatants.get(combatantId)
|
||||||
|
if (!combatant) return
|
||||||
|
return combatant.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime le combat affiché (bouton poubelle de l'en-tête).
|
||||||
|
* @param {PointerEvent} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
*/
|
||||||
|
static async #onDeleteCombat() {
|
||||||
|
const combat = this.viewed
|
||||||
|
if (!combat) return
|
||||||
|
const deleted = await combat.delete()
|
||||||
|
this.viewed = null
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VermineCombatant extends Combatant {
|
export class VermineCombatant extends Combatant {
|
||||||
@@ -330,6 +378,18 @@ export class VermineCombatant extends Combatant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_getInitiativeFormula() {
|
_getInitiativeFormula() {
|
||||||
|
// Personnage : jet de Réaction (Réflexes + Vigilance) contre la
|
||||||
|
// Difficulté du statut de combat — les Réussites = valeur de Réaction.
|
||||||
|
// PNJ et Créature : n'ont pas de caractéristiques/compétences — ils
|
||||||
|
// roulent leur pool de Réaction (computed.reaction) contre la même
|
||||||
|
// Difficulté, majoré de leur bonus de Réaction.
|
||||||
|
const actor = this.actor
|
||||||
|
if ( actor?.type === 'npc' || actor?.type === 'creature' ) {
|
||||||
|
const reaction = parseInt(actor.system?.computed?.reaction, 10) || 0
|
||||||
|
const difficulty = parseInt(actor.system?.combatStatus?.difficulty, 10) || 7
|
||||||
|
const bonus = parseInt(actor.system?.computed?.reactionBonus, 10) || 0
|
||||||
|
return `${reaction}d10cs>=${difficulty}+${bonus}`
|
||||||
|
}
|
||||||
return String(CONFIG.Combat.initiative.formula || game.system.initiative)
|
return String(CONFIG.Combat.initiative.formula || game.system.initiative)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import RollDialog from "./dialogs/rollDialog.mjs";
|
import RollDialog from "./dialogs/rollDialog.mjs";
|
||||||
import { initUserDice } from "./dice3d.mjs";
|
import { initUserDice } from "./dice3d.mjs";
|
||||||
import { VermineUtils } from "./roll.mjs";
|
import { VermineUtils } from "./roll.mjs";
|
||||||
import { registerTours } from "./tour.mjs";
|
import { postWelcomeMessage } from "./tour.mjs";
|
||||||
|
|
||||||
export const registerHooks = function () {
|
export const registerHooks = function () {
|
||||||
/**
|
/**
|
||||||
@@ -67,9 +67,11 @@ export const registerHooks = function () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
Hooks.once("ready", async () => {
|
Hooks.once("ready", async () => {
|
||||||
// System initialized
|
// Relays player rerolls to the GM client (Foundry v14 ChatMessage
|
||||||
//await registerTours();
|
// updates are GM-only).
|
||||||
|
VermineUtils.registerRerollSocket();
|
||||||
|
// Message de bienvenue (crédits), affiché à chaque chargement.
|
||||||
|
await postWelcomeMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
// changement de la pause
|
// changement de la pause
|
||||||
|
|||||||
@@ -0,0 +1,326 @@
|
|||||||
|
import { VermineUtils } from './roll.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gestion de la Fiabilité du matériel (regles/regles_materiel.txt).
|
||||||
|
*
|
||||||
|
* Test de Fiabilité (Dommages matériels) : selon la gravité (Mineurs 1D,
|
||||||
|
* Graves 3D, Critiques 5D), on lance les dés sans Relance ni Réserve ;
|
||||||
|
* chaque dé >= Fiabilité inflige 1 Dommage (réduit la Fiabilité de 1).
|
||||||
|
* À 0, l'équipement est détruit ou inutilisable.
|
||||||
|
*
|
||||||
|
* Réparations : jet de Précision + Compétence contre la Fiabilité d'origine
|
||||||
|
* (classique) ou de Précision + Bricolage contre la Fiabilité actuelle
|
||||||
|
* (de fortune). Chaque Réussite rend 1 point de Fiabilité (classique) ;
|
||||||
|
* la réparation de fortune rend l'objet fonctionnel sans rendre de point.
|
||||||
|
*/
|
||||||
|
export class VermineReliability {
|
||||||
|
/** Gravités et nombre de dés de Dommages (p. 37). */
|
||||||
|
static SEVERITIES = { mineurs: 1, graves: 3, critiques: 5 };
|
||||||
|
|
||||||
|
/** Handicap de réparation selon les Dommages subis (Mineure I, Grave II, Critique III). */
|
||||||
|
static #PANNE_HANDICAP = { 1: 1, 2: 1, 3: 2, 4: 2, 5: 3 };
|
||||||
|
|
||||||
|
/** Compétences de réparation classique (regles_materiel p. 39). */
|
||||||
|
static REPAIR_SKILLS = ['crafting', 'mecanical', 'technology', 'armory'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handicap de la panne courante (0 si intact).
|
||||||
|
* @param {Item} item
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static panneHandicap(item) {
|
||||||
|
const value = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
|
||||||
|
return this.#PANNE_HANDICAP[value] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* État courant dérivé des Dommages subis (tableau `damages.state`).
|
||||||
|
* @param {Item} item
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static panneState(item) {
|
||||||
|
const damages = item.system?.damages;
|
||||||
|
const value = parseInt(damages?.value ?? 0, 10) || 0;
|
||||||
|
if (value <= 0 || !damages?.state?.[value - 1]) return '';
|
||||||
|
return game.i18n.localize(`VERMINE.damages_state_${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test de Fiabilité : jet de Dommages matériels.
|
||||||
|
* @param {Item} item
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string} opts.severity "mineurs" | "graves" | "critiques"
|
||||||
|
* @param {boolean} opts.immersion si vrai, le Trait Étanche peut annuler les Dommages
|
||||||
|
* @returns {Promise<Object|null>} Résultat (null si impossible)
|
||||||
|
*/
|
||||||
|
static async test(item, { severity = 'graves', immersion = false } = {}) {
|
||||||
|
if (!item) return null;
|
||||||
|
const diceCount = this.SEVERITIES[severity] ?? 3;
|
||||||
|
const current = parseInt(item.system?.reliability ?? 0, 10) || 0;
|
||||||
|
const traits = item.system?.traits ?? {};
|
||||||
|
const damagesValue = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
|
||||||
|
|
||||||
|
if (current <= 0) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.reliability_already_destroyed'));
|
||||||
|
return { destroyed: true, dommages: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jet des dés de Dommages (sans Relance ni Réserve)
|
||||||
|
const roll = new Roll(`${diceCount}d10`);
|
||||||
|
await roll.evaluate();
|
||||||
|
await VermineUtils.showDiceSoNice(roll);
|
||||||
|
|
||||||
|
const results = (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => r.result ?? 0));
|
||||||
|
const traitsNotes = [];
|
||||||
|
|
||||||
|
// Trait Étanche : ignore les Dommages d'immersion tant que la Fiabilité
|
||||||
|
// est à son maximum (objet sans Dommage cumulé).
|
||||||
|
let dommages = results.filter((r) => r >= current).length;
|
||||||
|
if (immersion && traits.etanche && damagesValue === 0) {
|
||||||
|
dommages = 0;
|
||||||
|
traitsNotes.push(game.i18n.localize('VERMINE.reliability_etanche'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait Coque (n) : ignore les (n) premiers Dommages puis s'épuise.
|
||||||
|
// La coque épuisée est stockée { value: 0, max: n } : réparable (Phase 1).
|
||||||
|
let coqueValue = parseInt(traits.coque?.value ?? 0, 10) || 0;
|
||||||
|
const coqueInit = parseInt(traits.coque?.max ?? traits.coque?.value ?? 0, 10) || 0;
|
||||||
|
const updates = {};
|
||||||
|
if (coqueValue > 0 && dommages > 0) {
|
||||||
|
const absorbed = Math.min(coqueValue, dommages);
|
||||||
|
dommages -= absorbed;
|
||||||
|
coqueValue -= absorbed;
|
||||||
|
if (coqueValue > 0) {
|
||||||
|
updates['system.traits.coque.value'] = coqueValue;
|
||||||
|
traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_absorbed', { absorbed, remaining: coqueValue }));
|
||||||
|
} else {
|
||||||
|
updates['system.traits.coque'] = { value: 0, max: coqueInit };
|
||||||
|
traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_lost', { max: coqueInit }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait Cassant : toutes les pertes de Fiabilité sont majorées d'1 point.
|
||||||
|
const cassant = !!traits.cassant;
|
||||||
|
if (cassant && dommages > 0) {
|
||||||
|
dommages += 1;
|
||||||
|
traitsNotes.push(game.i18n.localize('VERMINE.reliability_cassant'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const newReliability = Math.max(0, current - dommages);
|
||||||
|
const newDamagesValue = Math.min(5, damagesValue + dommages);
|
||||||
|
const destroyed = newReliability === 0;
|
||||||
|
|
||||||
|
updates['system.reliability'] = newReliability;
|
||||||
|
updates['system.damages.value'] = newDamagesValue;
|
||||||
|
await item.update(updates);
|
||||||
|
|
||||||
|
// État final affiché sur la carte.
|
||||||
|
let state = '';
|
||||||
|
if (destroyed) {
|
||||||
|
state = game.i18n.localize('VERMINE.reliability_destroyed');
|
||||||
|
} else if (newDamagesValue > 0) {
|
||||||
|
state = game.i18n.localize(`VERMINE.damages_state_${newDamagesValue}`);
|
||||||
|
} else {
|
||||||
|
state = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ne marque comme Dommage que les dés réellement comptés (après
|
||||||
|
// absorption Étanche/Coque) : les dés absorbés restent affichés mais
|
||||||
|
// sans le style "damage".
|
||||||
|
const candidates = results.map((v, i) => ({ v, i })).filter((o) => o.v >= current);
|
||||||
|
const damageIdx = new Set(candidates.slice(0, Math.max(0, dommages)).map((o) => o.i));
|
||||||
|
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/vermine2047/templates/reliability-test.hbs',
|
||||||
|
{
|
||||||
|
item: { name: item.name, img: item.img },
|
||||||
|
severityLabel: game.i18n.localize(`VERMINE.reliability_severity_${severity}`),
|
||||||
|
diceCount,
|
||||||
|
oldReliability: current,
|
||||||
|
newReliability,
|
||||||
|
dommages,
|
||||||
|
results: results.map((v, i) => ({ value: v, isDamage: damageIdx.has(i) })),
|
||||||
|
state,
|
||||||
|
destroyed,
|
||||||
|
traitsNotes,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await ChatMessage.create({
|
||||||
|
user: game.user?._id,
|
||||||
|
speaker: this.#getSpeaker(item),
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { dommages, newReliability, destroyed };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Réparation classique, de fortune ou de coque.
|
||||||
|
* @param {Item} item
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string} opts.mode "classique" | "fortune" | "coque"
|
||||||
|
* @param {string} opts.skill Compétence de réparation classique (clé de skills)
|
||||||
|
* @returns {Promise<Object|null>}
|
||||||
|
*/
|
||||||
|
static async repair(item, { mode = 'classique', skill = 'technology' } = {}) {
|
||||||
|
const actor = item?.actor;
|
||||||
|
if (!item || !actor) {
|
||||||
|
ui.notifications.warn(game.i18n.localize('VERMINE.reliability_need_actor'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const traits = item.system?.traits ?? {};
|
||||||
|
const current = parseInt(item.system?.reliability ?? 0, 10) || 0;
|
||||||
|
const damagesValue = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
|
||||||
|
const handicap = this.panneHandicap(item);
|
||||||
|
|
||||||
|
// Trait Pratique : +1 Handicap sur toutes les réparations.
|
||||||
|
const pratique = !!traits.pratique;
|
||||||
|
const pratiqueHandicap = pratique ? 1 : 0;
|
||||||
|
const pratiqueNote = pratique ? game.i18n.localize('VERMINE.reliability_pratique') : '';
|
||||||
|
|
||||||
|
const isCoque = mode === 'coque';
|
||||||
|
const coqueMax = isCoque ? parseInt(traits.coque?.max ?? 0, 10) || 0 : 0;
|
||||||
|
|
||||||
|
// Difficulté : Fiabilité d'origine (≈ actuelle + Dommages subis) pour la
|
||||||
|
// réparation classique ; Fiabilité actuelle pour la réparation de fortune ;
|
||||||
|
// valeur maximale du Trait pour la réparation de coque.
|
||||||
|
let difficulty = current;
|
||||||
|
if (isCoque) difficulty = coqueMax;
|
||||||
|
else if (mode === 'classique') difficulty = Math.min(10, current + damagesValue);
|
||||||
|
|
||||||
|
const precision = parseInt(actor.system?.abilities?.precision?.value ?? 0, 10) || 0;
|
||||||
|
const skillValue = mode === 'fortune'
|
||||||
|
? parseInt(actor.system?.skills?.diy?.value ?? 0, 10) || 0
|
||||||
|
: parseInt(actor.system?.skills?.[skill]?.value ?? 0, 10) || 0;
|
||||||
|
const NoD = Math.max(0, precision + skillValue);
|
||||||
|
|
||||||
|
// Réparation de fortune (p. 39) : si le personnage possède la Compétence
|
||||||
|
// requise, +1D sur le jet ; sinon, Handicap de Rareté de cette Compétence
|
||||||
|
// (le Bonus de Bricolage s'applique dans les deux cas).
|
||||||
|
const skillRarity = parseInt(actor.system?.skills?.[skill]?.rarity ?? 0, 10) || 0;
|
||||||
|
const hasSkill = (parseInt(actor.system?.skills?.[skill]?.value ?? 0, 10) || 0) > 0;
|
||||||
|
const fortuneBonus = mode === 'fortune' && hasSkill ? 1 : 0;
|
||||||
|
const fortuneHandicap = mode === 'fortune' && !hasSkill ? skillRarity : 0;
|
||||||
|
let fortuneNote = '';
|
||||||
|
if (mode === 'fortune') {
|
||||||
|
if (hasSkill) {
|
||||||
|
fortuneNote = game.i18n.format('VERMINE.reliability_fortune_skill_bonus', { skill: game.i18n.localize(`SKILLS.${skill}.name`) });
|
||||||
|
} else if (skillRarity > 0) {
|
||||||
|
fortuneNote = game.i18n.format('VERMINE.reliability_fortune_skill_missing', { skill: game.i18n.localize(`SKILLS.${skill}.name`), rarity: skillRarity });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait Bien conçu : Réussite automatique sur les Réparations classiques.
|
||||||
|
const bienConcu = !!traits.bien_conçu;
|
||||||
|
const autoSuccess = bienConcu && mode === 'classique';
|
||||||
|
let bienConcuNote = '';
|
||||||
|
|
||||||
|
// Trait Bien conçu (fortune) : la 1ʳᵉ réparation de fortune réussie rend
|
||||||
|
// 1 point de Fiabilité (compteur traits.bien_conçu.value).
|
||||||
|
const bienConcuUsed = bienConcu && mode === 'fortune' && (parseInt(traits.bien_conçu?.value ?? 0, 10) || 0) > 0;
|
||||||
|
const bienConcuBonus = bienConcu && mode === 'fortune' && !bienConcuUsed;
|
||||||
|
|
||||||
|
// Réparation de coque : pas de panne à réparer, Difficulté = max du Trait.
|
||||||
|
const required = 1 + (isCoque ? 0 : handicap) + (isCoque ? 0 : fortuneHandicap) + pratiqueHandicap;
|
||||||
|
|
||||||
|
const roll = autoSuccess ? null : new Roll(`${NoD + fortuneBonus}d10cs>=${difficulty}`);
|
||||||
|
if (roll) {
|
||||||
|
await roll.evaluate();
|
||||||
|
await VermineUtils.showDiceSoNice(roll);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = roll
|
||||||
|
? (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => ({
|
||||||
|
value: r.result ?? 0,
|
||||||
|
isSuccess: r.success,
|
||||||
|
})))
|
||||||
|
: [];
|
||||||
|
const successes = roll?._total ?? required;
|
||||||
|
const success = autoSuccess || successes >= required;
|
||||||
|
|
||||||
|
let gained = 0;
|
||||||
|
let fortuneFixed = false;
|
||||||
|
let coqueRestored = false;
|
||||||
|
let newReliability = null;
|
||||||
|
let resultLabel = '';
|
||||||
|
if (success) {
|
||||||
|
if (isCoque) {
|
||||||
|
coqueRestored = true;
|
||||||
|
resultLabel = game.i18n.format('VERMINE.reliability_coque_restored', { max: coqueMax });
|
||||||
|
} else if (mode === 'classique') {
|
||||||
|
gained = autoSuccess ? damagesValue : successes;
|
||||||
|
newReliability = Math.min(10, current + gained);
|
||||||
|
resultLabel = game.i18n.format('VERMINE.reliability_gained', { gained });
|
||||||
|
if (autoSuccess) {
|
||||||
|
bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fortuneFixed = true;
|
||||||
|
if (bienConcuBonus) {
|
||||||
|
gained = 1;
|
||||||
|
newReliability = Math.min(10, current + 1);
|
||||||
|
bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu_fortune');
|
||||||
|
}
|
||||||
|
resultLabel = game.i18n.localize('VERMINE.reliability_fixed');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resultLabel = game.i18n.localize('VERMINE.reliability_failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = {};
|
||||||
|
if (isCoque && coqueRestored) {
|
||||||
|
updates['system.traits.coque'] = { value: coqueMax, max: coqueMax };
|
||||||
|
} else if (mode === 'classique' && gained > 0) {
|
||||||
|
updates['system.reliability'] = newReliability;
|
||||||
|
updates['system.damages.value'] = Math.max(0, damagesValue - gained);
|
||||||
|
} else if (mode === 'fortune' && fortuneFixed) {
|
||||||
|
updates['system.damages.value'] = 0;
|
||||||
|
if (bienConcuBonus) {
|
||||||
|
updates['system.reliability'] = newReliability;
|
||||||
|
updates['system.traits.bien_conçu'] = { value: 1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updates).length) await item.update(updates);
|
||||||
|
|
||||||
|
const skillLabel = mode === 'fortune'
|
||||||
|
? game.i18n.localize('SKILLS.diy.name')
|
||||||
|
: game.i18n.localize(`SKILLS.${skill}.name`);
|
||||||
|
const modeLabel = isCoque
|
||||||
|
? game.i18n.localize('VERMINE.reliability_repair_coque_label')
|
||||||
|
: game.i18n.localize(mode === 'classique' ? 'VERMINE.reliability_repair_classic' : 'VERMINE.reliability_repair_fortune');
|
||||||
|
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/vermine2047/templates/reliability-repair.hbs',
|
||||||
|
{
|
||||||
|
item: { name: item.name, img: item.img },
|
||||||
|
modeLabel,
|
||||||
|
skillLabel,
|
||||||
|
difficulty,
|
||||||
|
handicap: isCoque ? 0 : handicap,
|
||||||
|
pratiqueHandicap,
|
||||||
|
results,
|
||||||
|
successes,
|
||||||
|
required,
|
||||||
|
resultLabel,
|
||||||
|
autoSuccess,
|
||||||
|
fortuneNote,
|
||||||
|
bienConcuNote,
|
||||||
|
pratiqueNote,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await ChatMessage.create({
|
||||||
|
user: game.user?._id,
|
||||||
|
speaker: this.#getSpeaker(item),
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success, gained, successes, required };
|
||||||
|
}
|
||||||
|
|
||||||
|
static #getSpeaker(item) {
|
||||||
|
return ChatMessage.getSpeaker({ actor: item?.actor ?? null });
|
||||||
|
}
|
||||||
|
}
|
||||||
+122
-21
@@ -16,6 +16,10 @@ export class VermineUtils {
|
|||||||
* @param {string} [options.keepTotem=null] - Totem to keep ('human' or 'adapted')
|
* @param {string} [options.keepTotem=null] - Totem to keep ('human' or 'adapted')
|
||||||
* @param {number} [options.skillLevel=null] - Skill level for auto-successes
|
* @param {number} [options.skillLevel=null] - Skill level for auto-successes
|
||||||
* @param {boolean} [options.hasSpecialty=false] - Whether a specialty is used
|
* @param {boolean} [options.hasSpecialty=false] - Whether a specialty is used
|
||||||
|
* @param {number} [options.effectiveRange=null] - Portée effective (Trait Portée)
|
||||||
|
* @param {number} [options.mobilityHandicap=0] - Handicap de Mobilité (note)
|
||||||
|
* @param {number} [options.ponctuelUsed=0] - Handicaps annulés (Trait Ponctuel)
|
||||||
|
* @param {Object|null} [options.intimidant=null] - Trait Intimidant utilisé
|
||||||
* @returns {Promise<Roll>} The roll result
|
* @returns {Promise<Roll>} The roll result
|
||||||
*/
|
*/
|
||||||
static async roll({
|
static async roll({
|
||||||
@@ -33,12 +37,17 @@ export class VermineUtils {
|
|||||||
hasSpecialty = false,
|
hasSpecialty = false,
|
||||||
handicap = 0,
|
handicap = 0,
|
||||||
bonusSuccesses = 0,
|
bonusSuccesses = 0,
|
||||||
|
bonusOnSuccess = false,
|
||||||
poolBreakdown = null,
|
poolBreakdown = null,
|
||||||
specialtyName = null,
|
specialtyName = null,
|
||||||
weapon = null,
|
weapon = null,
|
||||||
targets = [],
|
targets = [],
|
||||||
attack = null,
|
attack = null,
|
||||||
messageFlags = null
|
messageFlags = null,
|
||||||
|
effectiveRange = null,
|
||||||
|
mobilityHandicap = 0,
|
||||||
|
ponctuelUsed = 0,
|
||||||
|
intimidant = null
|
||||||
}) {
|
}) {
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
@@ -160,12 +169,17 @@ export class VermineUtils {
|
|||||||
hasSpecialty,
|
hasSpecialty,
|
||||||
handicap,
|
handicap,
|
||||||
bonusSuccesses,
|
bonusSuccesses,
|
||||||
|
bonusOnSuccess,
|
||||||
poolBreakdown,
|
poolBreakdown,
|
||||||
specialtyName,
|
specialtyName,
|
||||||
weapon,
|
weapon,
|
||||||
targets,
|
targets,
|
||||||
attack,
|
attack,
|
||||||
messageFlags
|
messageFlags,
|
||||||
|
effectiveRange,
|
||||||
|
mobilityHandicap,
|
||||||
|
ponctuelUsed,
|
||||||
|
intimidant
|
||||||
});
|
});
|
||||||
|
|
||||||
return roll;
|
return roll;
|
||||||
@@ -190,9 +204,10 @@ export class VermineUtils {
|
|||||||
* @returns {Promise<boolean>} Whether the reroll was successful
|
* @returns {Promise<boolean>} Whether the reroll was successful
|
||||||
*/
|
*/
|
||||||
static async onReroll(message, target) {
|
static async onReroll(message, target) {
|
||||||
// Verify user permissions
|
// Verify user permissions. Foundry v14 renamed the ChatMessage field
|
||||||
const msgUserId = message.user?.id ?? message.user;
|
// `user` to `author`, so `message.user` is undefined on this core.
|
||||||
if (msgUserId !== game.user?.id && !game.user?.isGM) {
|
const msgAuthorId = message.author?.id ?? message.user?.id ?? message.user;
|
||||||
|
if (msgAuthorId !== game.user?.id && !game.user?.isGM) {
|
||||||
ui.notifications.warn(game.i18n.localize('VERMINE.error_cannot_reroll'));
|
ui.notifications.warn(game.i18n.localize('VERMINE.error_cannot_reroll'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -217,9 +232,6 @@ export class VermineUtils {
|
|||||||
|
|
||||||
target.classList.add('rerolled');
|
target.classList.add('rerolled');
|
||||||
|
|
||||||
// Set reroll flag
|
|
||||||
await message.setFlag("world", "reroll", true);
|
|
||||||
|
|
||||||
// Get difficulty and dice type
|
// Get difficulty and dice type
|
||||||
const ulElement = target.closest('ul');
|
const ulElement = target.closest('ul');
|
||||||
const difficulty = ulElement?.dataset.difficulty ?? 7;
|
const difficulty = ulElement?.dataset.difficulty ?? 7;
|
||||||
@@ -317,14 +329,55 @@ export class VermineUtils {
|
|||||||
+ mk('adapted', stats.adapted.count, stats.adapted.success);
|
+ mk('adapted', stats.adapted.count, stats.adapted.success);
|
||||||
}
|
}
|
||||||
|
|
||||||
await message.update({
|
// Persist the reroll. The GM writes the message directly ; players
|
||||||
content: messageContent.outerHTML
|
// cannot update ChatMessages on Foundry v14 (permission "OWNER"), so
|
||||||
});
|
// their reroll is relayed through the system socket and applied by the
|
||||||
|
// GM client (see registerRerollSocket).
|
||||||
|
if (game.user?.isGM) {
|
||||||
|
await message.setFlag("world", "reroll", true);
|
||||||
|
await message.update({
|
||||||
|
content: messageContent.outerHTML
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
game.socket?.emit(`system.${game.system.id}`, {
|
||||||
|
type: "reroll",
|
||||||
|
messageId: message.id,
|
||||||
|
requestId: foundry.utils.randomID(8),
|
||||||
|
content: messageContent.outerHTML
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the system socket listener which applies rerolls requested by
|
||||||
|
* players. Foundry v14 only allows GMs to update ChatMessages, so player
|
||||||
|
* rerolls are relayed to the GM client through `system.<id>` (see onReroll).
|
||||||
|
* Call once after the world is ready; re-registration replaces the handler.
|
||||||
|
*/
|
||||||
|
static registerRerollSocket() {
|
||||||
|
const eventName = `system.${game.system.id}`;
|
||||||
|
const handler = async (payload, userId) => {
|
||||||
|
if (!game.user?.isGM) return;
|
||||||
|
if (payload?.type !== "reroll") return;
|
||||||
|
const message = game.messages.get(payload.messageId);
|
||||||
|
if (!message) return;
|
||||||
|
// Security: only the message author may request the reroll update.
|
||||||
|
if (message.author?.id !== userId) return;
|
||||||
|
await message.setFlag("world", "reroll", true);
|
||||||
|
await message.update({ content: payload.content });
|
||||||
|
};
|
||||||
|
if (VermineUtils.#rerollSocketHandler) {
|
||||||
|
game.socket?.off(eventName, VermineUtils.#rerollSocketHandler);
|
||||||
|
}
|
||||||
|
VermineUtils.#rerollSocketHandler = handler;
|
||||||
|
game.socket?.on(eventName, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static #rerollSocketHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up event listeners for chat messages.
|
* Sets up event listeners for chat messages.
|
||||||
* @param {HTMLElement} html - The HTML element containing chat events.
|
* @param {HTMLElement} html - The HTML element containing chat events.
|
||||||
@@ -362,34 +415,78 @@ export class VermineUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add click event for granting rerolls
|
// Add click event for spending Effort points as rerolls (règles p.35) :
|
||||||
|
// 1 point d'Effort = 1 relance, ajouté aux relances de Compétence restantes.
|
||||||
html.querySelectorAll("button.grant-reroll").forEach(el => {
|
html.querySelectorAll("button.grant-reroll").forEach(el => {
|
||||||
el.addEventListener('click', async (ev) => {
|
el.addEventListener('click', async (ev) => {
|
||||||
const btn = ev.currentTarget;
|
const btn = ev.currentTarget;
|
||||||
|
const rollMessage = btn.closest(".vermine-roll-message");
|
||||||
const grantedRerollElement = html.querySelector('#granted-reroll');
|
const grantedRerollElement = html.querySelector('#granted-reroll');
|
||||||
const allowedRerollElement = html.querySelector("#allowed_reroll");
|
const allowedRerollElement = html.querySelector("#allowed_reroll");
|
||||||
|
const requested = parseInt(grantedRerollElement?.innerText, 10) || 0;
|
||||||
|
|
||||||
if (grantedRerollElement && allowedRerollElement) {
|
// Déduit les points d'Effort de l'acteur (max : ce qu'il lui reste).
|
||||||
allowedRerollElement.innerText = grantedRerollElement.innerText;
|
const actor = await VermineUtils.#resolveMessageActor(rollMessage);
|
||||||
|
const effort = actor?.system?.attributes?.effort;
|
||||||
|
let spent = 0;
|
||||||
|
if (effort && requested > 0) {
|
||||||
|
spent = Math.min(requested, parseInt(effort.value, 10) || 0);
|
||||||
|
if (spent > 0) {
|
||||||
|
const remaining = (parseInt(effort.value, 10) || 0) - spent;
|
||||||
|
await actor.update({ 'system.attributes.effort.value': remaining });
|
||||||
|
const poolEl = rollMessage?.querySelector('.effort-pool');
|
||||||
|
if (poolEl) poolEl.innerText = `${remaining}/${effort.max || 0}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Les relances d'Effort s'ajoutent aux relances de Compétence restantes.
|
||||||
|
if (allowedRerollElement) {
|
||||||
|
const current = parseInt(allowedRerollElement.innerText, 10) || 0;
|
||||||
|
allowedRerollElement.innerText = current + spent;
|
||||||
|
}
|
||||||
|
const effortSection = btn.closest('.reroll-from-effort');
|
||||||
|
if (effortSection) effortSection.style.display = "none";
|
||||||
|
|
||||||
const mesEl = btn.closest('[data-message-id]');
|
const mesEl = btn.closest('[data-message-id]');
|
||||||
const messageId = mesEl?.dataset?.messageId;
|
const messageId = mesEl?.dataset?.messageId;
|
||||||
|
|
||||||
if (messageId) {
|
if (messageId) {
|
||||||
btn.closest('.reroll-from-effort').style.display = "none";
|
const content = rollMessage.outerHTML;
|
||||||
|
// GM applies the update directly ; players relay through the
|
||||||
const rollMessage = btn.closest(".vermine-roll-message");
|
// system socket (Foundry v14 ChatMessage updates are GM-only).
|
||||||
if (rollMessage) {
|
if (game.user?.isGM) {
|
||||||
const content = rollMessage.outerHTML;
|
|
||||||
const message = await game.messages.get(messageId);
|
const message = await game.messages.get(messageId);
|
||||||
await message.update({ content: content });
|
await message.update({ content: content });
|
||||||
|
} else {
|
||||||
|
game.socket?.emit(`system.${game.system.id}`, {
|
||||||
|
type: "reroll",
|
||||||
|
messageId: messageId,
|
||||||
|
requestId: foundry.utils.randomID(8),
|
||||||
|
content: content
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout l'acteur à l'origine d'un message de jet depuis les attributs
|
||||||
|
* data-actor-id / data-actor-uuid posés sur le message. Le uuid est
|
||||||
|
* privilégié : il distingue les tokens non liés (PNJ mineurs) issus de la
|
||||||
|
* même fiche, qui partagent le même id d'acteur monde.
|
||||||
|
* @param {HTMLElement} rollMessage
|
||||||
|
* @returns {Promise<Actor|null>}
|
||||||
|
*/
|
||||||
|
static async #resolveMessageActor(rollMessage) {
|
||||||
|
if (!rollMessage) return null;
|
||||||
|
const id = rollMessage.dataset.actorId ?? null;
|
||||||
|
const uuid = rollMessage.dataset.actorUuid ?? null;
|
||||||
|
if (!id && !uuid) return null;
|
||||||
|
const { VermineExchange } = await import('./exchange.mjs');
|
||||||
|
return VermineExchange.resolveDefender({ id, uuid });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays dice rolls in 3D if available.
|
* Displays dice rolls in 3D if available.
|
||||||
* @param {Roll} roll - The roll to display
|
* @param {Roll} roll - The roll to display
|
||||||
@@ -445,8 +542,12 @@ export class VermineUtils {
|
|||||||
}
|
}
|
||||||
// Verdict
|
// Verdict
|
||||||
const required = 1 + (param.handicap ?? 0);
|
const required = 1 + (param.handicap ?? 0);
|
||||||
const bonusSuccesses = param.bonusSuccesses ?? 0;
|
const rawTotal = roll._total ?? 0;
|
||||||
const total = (roll._total ?? 0) + bonusSuccesses;
|
// Réussites automatiques : appliquées inconditionnellement, ou uniquement
|
||||||
|
// si le jet est réussi (Trait Rafale — bonusOnSuccess).
|
||||||
|
const requested = param.bonusSuccesses ?? 0;
|
||||||
|
const bonusSuccesses = requested > 0 && (!param.bonusOnSuccess || rawTotal >= required) ? requested : 0;
|
||||||
|
const total = rawTotal + bonusSuccesses;
|
||||||
const verdict = {
|
const verdict = {
|
||||||
success: total >= required,
|
success: total >= required,
|
||||||
total,
|
total,
|
||||||
|
|||||||
@@ -28,13 +28,4 @@ export const registerSettings = function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
game.settings.register("vermine2047", "first-run-tips-shown", {
|
|
||||||
name: game.i18n.localize("SETTINGS.world.first_run.name"),
|
|
||||||
hint: game.i18n.localize("SETTINGS.world.first_run.hint"),
|
|
||||||
scope: "system",
|
|
||||||
config: true,
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
+9
-154
@@ -1,159 +1,14 @@
|
|||||||
function compact(array) {
|
/**
|
||||||
var index2 = -1, length = array == null ? 0 : array.length, resIndex = 0, result = [];
|
* Message de bienvenue (crédits du système et du jeu) posté dans le chat
|
||||||
while (++index2 < length) {
|
* à chaque chargement du monde. Un seul exemplaire par refresh : il est
|
||||||
var value2 = array[index2];
|
* posté par le MJ actif uniquement.
|
||||||
if (value2) {
|
*/
|
||||||
result[resIndex++] = value2;
|
export async function postWelcomeMessage() {
|
||||||
}
|
if (!game.user?.isActiveGM) return;
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
class CreateActorDialog extends FormApplication {
|
|
||||||
constructor() {
|
|
||||||
super({});
|
|
||||||
}
|
|
||||||
async _updateObject() {
|
|
||||||
}
|
|
||||||
static get defaultOptions() {
|
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
||||||
title: game.i18n.format("DOCUMENT.Create", {
|
|
||||||
type: game.i18n.localize("DOCUMENT.Actor")
|
|
||||||
}),
|
|
||||||
template: "systems/vermine2047/templates/actor/create.hbs",
|
|
||||||
id: "new-actor-dialog",
|
|
||||||
resizable: false,
|
|
||||||
classes: ["vermine2047", "sheet", "new-actor"],
|
|
||||||
width: 650,
|
|
||||||
height: 200
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getData(_options) {
|
|
||||||
return {
|
|
||||||
// sfenabled: IronswornSettings.starforgedToolsEnabled
|
|
||||||
};
|
|
||||||
}
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
html.find(".vermine__character__create").on("click", async (ev) => {
|
|
||||||
await this._characterCreate.call(this, ev);
|
|
||||||
});
|
|
||||||
html.find(".vermine__shared__create").on("click", async (ev) => {
|
|
||||||
await this._sharedCreate.call(this, ev);
|
|
||||||
});
|
|
||||||
html.find(".vermine__site__create").on("click", async (ev) => {
|
|
||||||
await this._siteCreate.call(this, ev);
|
|
||||||
});
|
|
||||||
html.find(".vermine__foe__create").on("click", async (ev) => {
|
|
||||||
await this._foeCreate.call(this, ev);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class VermineTour extends foundry.nue.Tour {
|
|
||||||
/** @override */
|
|
||||||
async _preStep() {
|
|
||||||
var _a2, _b, _c, _d, _e;
|
|
||||||
await super._preStep();
|
|
||||||
if ((_a2 = this.currentStep) == null ? void 0 : _a2.sidebarTab) {
|
|
||||||
await ((_b = ui.sidebar) == null ? void 0 : _b.activateTab(this.currentStep.sidebarTab));
|
|
||||||
}
|
|
||||||
if ((_c = this.currentStep) == null ? void 0 : _c.layer) {
|
|
||||||
const layer = canvas == null ? void 0 : canvas[this.currentStep.layer];
|
|
||||||
if (layer.active && this.currentStep.tool)
|
|
||||||
(_d = ui.controls) == null ? void 0 : _d.initialize({ tool: this.currentStep.tool });
|
|
||||||
else
|
|
||||||
layer.activate({ tool: this.currentStep.tool });
|
|
||||||
}
|
|
||||||
if (((_e = this.currentStep) == null ? void 0 : _e.hook) != null) {
|
|
||||||
await this.currentStep.hook();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WelcomeTour extends VermineTour {
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
title: "TOURS.Welcome.Title",
|
|
||||||
description: "TOURS.Welcome.Description",
|
|
||||||
canBeResumed: false,
|
|
||||||
display: true,
|
|
||||||
steps: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
get steps() {
|
|
||||||
var _a2;
|
|
||||||
return compact([
|
|
||||||
{
|
|
||||||
id: "welcome",
|
|
||||||
title: "TOURS.Welcome.WelcomeTitle",
|
|
||||||
content: "TOURS.Welcome.WelcomeContent"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "character-tab",
|
|
||||||
title: "TOURS.Welcome.ActorTabTitle",
|
|
||||||
content: "TOURS.Welcome.ActorTabContent",
|
|
||||||
sidebarTab: "actors",
|
|
||||||
selector: "#actors .create-document"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "character-create",
|
|
||||||
title: "TOURS.Welcome.CharacterCreateTitle",
|
|
||||||
content: "TOURS.Welcome.CharacterCreateContent",
|
|
||||||
hook: async () => {
|
|
||||||
this.createActorDialog = new CreateActorDialog();
|
|
||||||
await this.createActorDialog.render(true);
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
|
||||||
},
|
|
||||||
selector: "#new-actor-dialog #new-group"
|
|
||||||
},
|
|
||||||
/*{
|
|
||||||
id: "compendia",
|
|
||||||
title: "TOURS.Welcome.CompendiumTitle",
|
|
||||||
content: "TOURS.Welcome.CompendiumContent",
|
|
||||||
hook: async () => {
|
|
||||||
var _a3;
|
|
||||||
return await ((_a3 = this.createActorDialog) == null ? void 0 : _a3.close());
|
|
||||||
},
|
|
||||||
sidebarTab: "compendium",
|
|
||||||
selector: 'li[data-pack="vermine2047.verminescenes"]'
|
|
||||||
},*/
|
|
||||||
/*((_a2 = game.user) == null ? void 0 : _a2.viewedScene) && {
|
|
||||||
id: "oracletool",
|
|
||||||
title: "TOURS.Welcome.OracleToolTitle",
|
|
||||||
content: "TOURS.Welcome.OracleToolContent",
|
|
||||||
layer: "tokens",
|
|
||||||
selector: '[data-tool="Oracles"]'
|
|
||||||
},*/
|
|
||||||
{
|
|
||||||
id: "tours",
|
|
||||||
title: "TOURS.Welcome.ToursTitle",
|
|
||||||
content: "TOURS.Welcome.ToursContent",
|
|
||||||
hook: async () => {
|
|
||||||
var _a3;
|
|
||||||
return await ((_a3 = this.createActorDialog) == null ? void 0 : _a3.close());
|
|
||||||
},
|
|
||||||
sidebarTab: "settings",
|
|
||||||
selector: 'button[data-action="tours"]'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export async function registerTours() {
|
|
||||||
game.tours.register("vermine2047", "welcome", new WelcomeTour());
|
|
||||||
document.addEventListener("click", (el) => {
|
|
||||||
if (el.target.closest("#chat-log #vermine-tour-chat-button")) {
|
|
||||||
const tour = game.tours.get("vermine2047.welcome");
|
|
||||||
tour?.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (game.settings.get("vermine2047", "first-run-tips-shown"))
|
|
||||||
return;
|
|
||||||
// Posting first-start messages
|
|
||||||
const gms = ChatMessage.getWhisperRecipients("GM");
|
const gms = ChatMessage.getWhisperRecipients("GM");
|
||||||
ChatMessage.implementation;
|
await ChatMessage.create({
|
||||||
ChatMessage.create({
|
|
||||||
whisper: gms,
|
whisper: gms,
|
||||||
speaker: { alias: game.i18n.localize("VERMINE.name") },
|
speaker: { alias: game.i18n.localize("VERMINE.name") },
|
||||||
content: game.i18n.localize("TOURS.ChatMessage")
|
content: game.i18n.localize("TOURS.ChatMessage")
|
||||||
});
|
});
|
||||||
game.settings.set("vermine2047", "first-run-tips-shown", true);
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* Helpers partagés pour les Traits (regles/regles_traits.txt).
|
||||||
|
*
|
||||||
|
* Centralise la lecture des Traits sur les items (présence, valeur) et les
|
||||||
|
* calculs liés aux objets équipés d'un acteur (Lourd, Mobilité, Fétiche…).
|
||||||
|
* Consommé par les dialogues de jet (combat, action) et les cartes de chat.
|
||||||
|
*/
|
||||||
|
export class VermineTraits {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Présence d'un Trait sur un item.
|
||||||
|
* @param {Item|null} item
|
||||||
|
* @param {string} key clé du Trait (ex. "lourd")
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
static hasTrait(item, key) {
|
||||||
|
return Boolean(item?.system?.traits?.[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valeur (n) d'un Trait sur un item (0 si absent ou sans valeur).
|
||||||
|
* @param {Item|null} item
|
||||||
|
* @param {string} key clé du Trait
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static traitValue(item, key) {
|
||||||
|
if (!item?.system?.traits?.[key]) return 0;
|
||||||
|
return parseInt(item.system.traits[key]?.value ?? 0, 10) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Items équipés d'un acteur (armes + protections + objets génériques)
|
||||||
|
* possédant un Trait donné.
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @param {string} traitKey
|
||||||
|
* @returns {Item[]}
|
||||||
|
*/
|
||||||
|
static equippedItems(actor, traitKey) {
|
||||||
|
if (!actor?.itemTypes) return [];
|
||||||
|
return [...actor.itemTypes.weapon || [], ...actor.itemTypes.defense || [], ...actor.itemTypes.item || []]
|
||||||
|
.filter(i => i.system?.equipped && this.hasTrait(i, traitKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vigueur de l'acteur : caractéristique pour le personnage, tables pour
|
||||||
|
* PNJ (Menace) et créature (computed).
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static vigor(actor) {
|
||||||
|
if (!actor?.system) return 0;
|
||||||
|
if (actor.type === 'npc') {
|
||||||
|
const threat = actor.system?.threat?.value || 1;
|
||||||
|
return CONFIG.VERMINE.npcThreatLevels?.[threat]?.vigor || 0;
|
||||||
|
}
|
||||||
|
if (actor.type === 'creature') return actor.system?.computed?.vigor || 0;
|
||||||
|
return actor.system?.abilities?.vigor?.value || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Malus cumulés des objets Lourd (n) équipés dont la Vigueur du porteur
|
||||||
|
* est inférieure à (n). Les Malus se cumulent entre objets (règles p. …).
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static lourdMalus(actor) {
|
||||||
|
const vigor = this.vigor(actor);
|
||||||
|
return this.equippedItems(actor, 'lourd').reduce((sum, item) => {
|
||||||
|
const n = this.traitValue(item, 'lourd');
|
||||||
|
return sum + (n > 0 && vigor < n ? 1 : 0);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handicap de Mobilité total : somme des handicaps des protections équipées
|
||||||
|
* qui ne sont pas Maniables (le Trait Maniable annule le Handicap).
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static mobilityHandicap(actor) {
|
||||||
|
const defenses = (actor?.itemTypes?.defense || []).filter(d => d.system?.equipped);
|
||||||
|
return defenses.reduce((sum, d) => sum + (this.hasTrait(d, 'maniable') ? 0 : (parseInt(d.system?.mobility ?? 0, 10) || 0)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note de rappel sur la Mobilité (protections équipées non Maniables),
|
||||||
|
* ou null si aucun Handicap de Mobilité.
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
static mobilityNote(actor) {
|
||||||
|
const handicap = this.mobilityHandicap(actor);
|
||||||
|
if (handicap <= 0) return null;
|
||||||
|
return game.i18n.format('VERMINE.traits_mobility_note', { handicap });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Premier item équipé possédant le Trait Fétiche, ou null.
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {Item|null}
|
||||||
|
*/
|
||||||
|
static feticheItem(actor) {
|
||||||
|
return this.equippedItems(actor, 'fetiche')[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nombre d'items équipés avec le Trait Fétiche.
|
||||||
|
* @param {Actor} actor
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
static feticheCount(actor) {
|
||||||
|
return this.equippedItems(actor, 'fetiche').length;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -29,8 +29,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198965
|
createdTime: 1786119843343
|
||||||
modifiedTime: 1785775198965
|
modifiedTime: 1786119843357
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!vPA7gekUx8kUNOWB'
|
_key: '!items!vPA7gekUx8kUNOWB'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198966
|
createdTime: 1786119843344
|
||||||
modifiedTime: 1785775198966
|
modifiedTime: 1786119843357
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!DItkLLGSyjAGzruO'
|
_key: '!items!DItkLLGSyjAGzruO'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198966
|
createdTime: 1786119843345
|
||||||
modifiedTime: 1785775198966
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!Cs6ibCrcPxim7QHR'
|
_key: '!items!Cs6ibCrcPxim7QHR'
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -27,8 +27,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198967
|
createdTime: 1786119843345
|
||||||
modifiedTime: 1785775198967
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!pP9hfvk5JKsoFcnt'
|
_key: '!items!pP9hfvk5JKsoFcnt'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198967
|
createdTime: 1786119843345
|
||||||
modifiedTime: 1785775198967
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!h3Q6LwnnVuj6MH4Y'
|
_key: '!items!h3Q6LwnnVuj6MH4Y'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198967
|
createdTime: 1786119843346
|
||||||
modifiedTime: 1785775198967
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!JrtHAUqFgA75e6pq'
|
_key: '!items!JrtHAUqFgA75e6pq'
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -29,8 +29,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198967
|
createdTime: 1786119843346
|
||||||
modifiedTime: 1785775198967
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!t4p4n73kwwVoeSMe'
|
_key: '!items!t4p4n73kwwVoeSMe'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198968
|
createdTime: 1786119843346
|
||||||
modifiedTime: 1785775198968
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!2h0xUozy1xqLdKcE'
|
_key: '!items!2h0xUozy1xqLdKcE'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198968
|
createdTime: 1786119843347
|
||||||
modifiedTime: 1785775198968
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!oNfjRGGRVFGtfbK0'
|
_key: '!items!oNfjRGGRVFGtfbK0'
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -22,8 +22,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198968
|
createdTime: 1786119843347
|
||||||
modifiedTime: 1785775198968
|
modifiedTime: 1786119843358
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!vgYLoiEqEOxrGdy6'
|
_key: '!items!vgYLoiEqEOxrGdy6'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198968
|
createdTime: 1786119843347
|
||||||
modifiedTime: 1785775198968
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!uGG0FrBn1QtMcxbz'
|
_key: '!items!uGG0FrBn1QtMcxbz'
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -29,8 +29,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198968
|
createdTime: 1786119843347
|
||||||
modifiedTime: 1785775198968
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!bV6V4i1v4ORWDJSQ'
|
_key: '!items!bV6V4i1v4ORWDJSQ'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198969
|
createdTime: 1786119843348
|
||||||
modifiedTime: 1785775198969
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!S8AwTKDxIdez7vHB'
|
_key: '!items!S8AwTKDxIdez7vHB'
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -24,8 +24,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198969
|
createdTime: 1786119843348
|
||||||
modifiedTime: 1785775198969
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!RjdZB4Qd2V8mp44x'
|
_key: '!items!RjdZB4Qd2V8mp44x'
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -30,8 +30,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198969
|
createdTime: 1786119843348
|
||||||
modifiedTime: 1785775198969
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!kPoioCvlK7H0uJB1'
|
_key: '!items!kPoioCvlK7H0uJB1'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198969
|
createdTime: 1786119843349
|
||||||
modifiedTime: 1785775198969
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!EqeONvUCK7OhJaNN'
|
_key: '!items!EqeONvUCK7OhJaNN'
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -32,8 +32,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198969
|
createdTime: 1786119843349
|
||||||
modifiedTime: 1785775198969
|
modifiedTime: 1786119843359
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!8ShwJQCWKHsfXLko'
|
_key: '!items!8ShwJQCWKHsfXLko'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198970
|
createdTime: 1786119843349
|
||||||
modifiedTime: 1785775198970
|
modifiedTime: 1786119843360
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!ivx8gS0XBEcoqtdl'
|
_key: '!items!ivx8gS0XBEcoqtdl'
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -28,8 +28,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198970
|
createdTime: 1786119843350
|
||||||
modifiedTime: 1785775198970
|
modifiedTime: 1786119843362
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!HfC2oOARrpKnpoQf'
|
_key: '!items!HfC2oOARrpKnpoQf'
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -27,8 +27,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198970
|
createdTime: 1786119843350
|
||||||
modifiedTime: 1785775198970
|
modifiedTime: 1786119843362
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!Re0aeZzupnz3EL6E'
|
_key: '!items!Re0aeZzupnz3EL6E'
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -33,8 +33,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198970
|
createdTime: 1786119843350
|
||||||
modifiedTime: 1785775198970
|
modifiedTime: 1786119843362
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!UxNguZd8K62nt1GH'
|
_key: '!items!UxNguZd8K62nt1GH'
|
||||||
|
|||||||
+3
-3
@@ -17,11 +17,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -30,8 +30,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198971
|
createdTime: 1786119843351
|
||||||
modifiedTime: 1785775198971
|
modifiedTime: 1786119843362
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!AAw7U5tUxbUczorz'
|
_key: '!items!AAw7U5tUxbUczorz'
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -24,8 +24,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198971
|
createdTime: 1786119843351
|
||||||
modifiedTime: 1785775198971
|
modifiedTime: 1786119843362
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!guC1pOfmcmitRGDo'
|
_key: '!items!guC1pOfmcmitRGDo'
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -29,8 +29,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198971
|
createdTime: 1786119843351
|
||||||
modifiedTime: 1785775198971
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!kFak0PMoNAuPnSfX'
|
_key: '!items!kFak0PMoNAuPnSfX'
|
||||||
|
|||||||
+3
-3
@@ -23,11 +23,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -36,8 +36,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198971
|
createdTime: 1786119843352
|
||||||
modifiedTime: 1785775198971
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!4jRJw5vuhZeqoKy9'
|
_key: '!items!4jRJw5vuhZeqoKy9'
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -37,8 +37,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198972
|
createdTime: 1786119843352
|
||||||
modifiedTime: 1785775198972
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!Y8UeTOYpGkM3nt9R'
|
_key: '!items!Y8UeTOYpGkM3nt9R'
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -24,8 +24,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198972
|
createdTime: 1786119843352
|
||||||
modifiedTime: 1785775198972
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!BNZeEd0CpTgYkEou'
|
_key: '!items!BNZeEd0CpTgYkEou'
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -28,8 +28,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198972
|
createdTime: 1786119843353
|
||||||
modifiedTime: 1785775198972
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!wH5OPnv4cenzpgws'
|
_key: '!items!wH5OPnv4cenzpgws'
|
||||||
|
|||||||
+3
-3
@@ -14,11 +14,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -27,8 +27,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198973
|
createdTime: 1786119843354
|
||||||
modifiedTime: 1785775198973
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!rCQOrAzaCgCF0Dri'
|
_key: '!items!rCQOrAzaCgCF0Dri'
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -27,8 +27,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198972
|
createdTime: 1786119843353
|
||||||
modifiedTime: 1785775198972
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!DHv0PXsIgQ8rPPUY'
|
_key: '!items!DHv0PXsIgQ8rPPUY'
|
||||||
|
|||||||
+3
-3
@@ -15,11 +15,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -28,8 +28,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198973
|
createdTime: 1786119843354
|
||||||
modifiedTime: 1785775198973
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!IMXQRepTNtiHZcaf'
|
_key: '!items!IMXQRepTNtiHZcaf'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198972
|
createdTime: 1786119843353
|
||||||
modifiedTime: 1785775198972
|
modifiedTime: 1786119843363
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!nxy53EF4ZRSSwCvr'
|
_key: '!items!nxy53EF4ZRSSwCvr'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198973
|
createdTime: 1786119843354
|
||||||
modifiedTime: 1785775198973
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!uneyg1G0Gpbb7l5P'
|
_key: '!items!uneyg1G0Gpbb7l5P'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198973
|
createdTime: 1786119843354
|
||||||
modifiedTime: 1785775198973
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!mxzf2Od6SMlvhKon'
|
_key: '!items!mxzf2Od6SMlvhKon'
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -25,8 +25,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198973
|
createdTime: 1786119843355
|
||||||
modifiedTime: 1785775198973
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!71goz10chR7bPQZY'
|
_key: '!items!71goz10chR7bPQZY'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198974
|
createdTime: 1786119843355
|
||||||
modifiedTime: 1785775198974
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!haijrxkf8tAxmWx5'
|
_key: '!items!haijrxkf8tAxmWx5'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 2
|
value: 2
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198974
|
createdTime: 1786119843355
|
||||||
modifiedTime: 1785775198974
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!qdtjHLeC7pKh57tP'
|
_key: '!items!qdtjHLeC7pKh57tP'
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ system:
|
|||||||
value: 4
|
value: 4
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -32,8 +32,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198974
|
createdTime: 1786119843356
|
||||||
modifiedTime: 1785775198974
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!VIPHgGAqSklGFhFh'
|
_key: '!items!VIPHgGAqSklGFhFh'
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -26,8 +26,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198974
|
createdTime: 1786119843356
|
||||||
modifiedTime: 1785775198974
|
modifiedTime: 1786119843364
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!ZcvIaqqfjvy67qWx'
|
_key: '!items!ZcvIaqqfjvy67qWx'
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ system:
|
|||||||
value: 3
|
value: 3
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -31,8 +31,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198974
|
createdTime: 1786119843356
|
||||||
modifiedTime: 1785775198974
|
modifiedTime: 1786119843365
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!noIASNejaUkv0Qo3'
|
_key: '!items!noIASNejaUkv0Qo3'
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ system:
|
|||||||
value: 1
|
value: 1
|
||||||
min: 1
|
min: 1
|
||||||
max: 4
|
max: 4
|
||||||
|
mutation: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -24,8 +24,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198975
|
createdTime: 1786119843357
|
||||||
modifiedTime: 1785775198975
|
modifiedTime: 1786119843365
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!flRHah4HO4JQ4DaM'
|
_key: '!items!flRHah4HO4JQ4DaM'
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775198946
|
createdTime: 1786119843318
|
||||||
modifiedTime: 1785775198993
|
modifiedTime: 1786119843391
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!folders!WTyHT2h0fsSnUHFT'
|
_key: '!folders!WTyHT2h0fsSnUHFT'
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
folder: 1usUE4cvarmMlQdA
|
||||||
|
name: Adage
|
||||||
|
type: ability
|
||||||
|
_id: IQftageK6SlRVKhO
|
||||||
|
img: systems/vermine2047/assets/icons/items/ability.webp
|
||||||
|
system:
|
||||||
|
description: >-
|
||||||
|
<p>Le Prêcheur bénéficie d’un Bonus de 1D aux jets de Volonté pour capter
|
||||||
|
l’attention de plus d’un individu (hors ceux de son Groupe actif). Qui plus
|
||||||
|
est, il peut décider d’utiliser sa Volonté à la place de son Savoir lorsque
|
||||||
|
sa conviction est plus importante que ses connaissances (cependant le Bonus
|
||||||
|
de 1D n’est accordé que lors de ses prises de parole).</p>
|
||||||
|
type: character
|
||||||
|
totem: human
|
||||||
|
learn:
|
||||||
|
threshold: 5
|
||||||
|
hindrance: 0
|
||||||
|
level:
|
||||||
|
value: 1
|
||||||
|
min: 1
|
||||||
|
max: 3
|
||||||
|
effects: []
|
||||||
|
learned: false
|
||||||
|
effects: []
|
||||||
|
sort: 0
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
kb05teJOwuTQVHwZ: 3
|
||||||
|
flags: {}
|
||||||
|
_stats:
|
||||||
|
coreVersion: '14.365'
|
||||||
|
systemId: vermine2047
|
||||||
|
systemVersion: 0.1.13
|
||||||
|
createdTime: 1786119900165
|
||||||
|
modifiedTime: 1786119900165
|
||||||
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
exportSource: null
|
||||||
|
_key: '!items!IQftageK6SlRVKhO'
|
||||||
|
|
||||||
@@ -18,11 +18,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -31,8 +31,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498760
|
createdTime: 1786119917828
|
||||||
modifiedTime: 1785775498760
|
modifiedTime: 1786119917844
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!zfeNmyLcOyfD4AxI'
|
_key: '!items!zfeNmyLcOyfD4AxI'
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -39,8 +39,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498762
|
createdTime: 1786119917831
|
||||||
modifiedTime: 1785775498762
|
modifiedTime: 1786119917846
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!CQeFYqMRYMFa6pAa'
|
_key: '!items!CQeFYqMRYMFa6pAa'
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ system:
|
|||||||
les autres protagonistes doivent assister à ce duel singulier sans
|
les autres protagonistes doivent assister à ce duel singulier sans
|
||||||
intervenir. Les créatures sans aucun intellect ne sont pas affectées. Cette
|
intervenir. Les créatures sans aucun intellect ne sont pas affectées. Cette
|
||||||
Capacité ne fonctionne qu’en présence d’au moins la moitié du Groupe.</p>
|
Capacité ne fonctionne qu’en présence d’au moins la moitié du Groupe.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: predator
|
totem: predator
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -21,11 +21,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -34,8 +34,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496862
|
createdTime: 1786119905163
|
||||||
modifiedTime: 1785775496862
|
modifiedTime: 1786119905174
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!NOBtH3xQJPptBU6I'
|
_key: '!items!NOBtH3xQJPptBU6I'
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -34,8 +34,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498746
|
createdTime: 1786119917816
|
||||||
modifiedTime: 1785775498746
|
modifiedTime: 1786119917837
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!Vggeykg6wQGTaR2o'
|
_key: '!items!Vggeykg6wQGTaR2o'
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -45,8 +45,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498763
|
createdTime: 1786119917831
|
||||||
modifiedTime: 1785775498763
|
modifiedTime: 1786119917846
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!X1RckrwGCbG7IQVR'
|
_key: '!items!X1RckrwGCbG7IQVR'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ system:
|
|||||||
leurs réserves, y compris celle de Groupe. S’ils trouvent un objet de Rareté
|
leurs réserves, y compris celle de Groupe. S’ils trouvent un objet de Rareté
|
||||||
supérieure ou égale à 9, le gain est de 2D par Réserve, et le Groupe gagne
|
supérieure ou égale à 9, le gain est de 2D par Réserve, et le Groupe gagne
|
||||||
1D d’expérience de Découverte.</p>
|
1D d’expérience de Découverte.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: scavenger
|
totem: scavenger
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -22,11 +22,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -35,8 +35,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496847
|
createdTime: 1786119905158
|
||||||
modifiedTime: 1785775496847
|
modifiedTime: 1786119905173
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!1UA2GqAD1OXwuAZx'
|
_key: '!items!1UA2GqAD1OXwuAZx'
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
folder: XH0LM14mhjTAIOyv
|
||||||
|
name: Apprentissage
|
||||||
|
type: ability
|
||||||
|
_id: AfWXXhXHfbmqaeGT
|
||||||
|
img: systems/vermine2047/assets/icons/items/ability.webp
|
||||||
|
system:
|
||||||
|
description: >-
|
||||||
|
<p>Lorsqu’un Objectif est rempli, les membres d’un Clan bénéficient de 2D
|
||||||
|
d’Expérience supplémentaires.</p>
|
||||||
|
type: group
|
||||||
|
totem: human
|
||||||
|
learn:
|
||||||
|
threshold: 5
|
||||||
|
hindrance: 0
|
||||||
|
level:
|
||||||
|
value: 1
|
||||||
|
min: 1
|
||||||
|
max: 3
|
||||||
|
effects: []
|
||||||
|
learned: false
|
||||||
|
effects: []
|
||||||
|
sort: 0
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
kb05teJOwuTQVHwZ: 3
|
||||||
|
flags: {}
|
||||||
|
_stats:
|
||||||
|
coreVersion: '14.365'
|
||||||
|
systemId: vermine2047
|
||||||
|
systemVersion: 0.1.13
|
||||||
|
createdTime: 1786119897586
|
||||||
|
modifiedTime: 1786119897586
|
||||||
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
exportSource: null
|
||||||
|
_key: '!items!AfWXXhXHfbmqaeGT'
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ system:
|
|||||||
d’Expérience de Groupe peuvent bénéficier à tous les personnages pour leurs
|
d’Expérience de Groupe peuvent bénéficier à tous les personnages pour leurs
|
||||||
jets individuels sur un apprentissage commun, moyennant la dépense de 1D de
|
jets individuels sur un apprentissage commun, moyennant la dépense de 1D de
|
||||||
Réserve de Groupe par personnage impliqué dans l’apprentissage.</p>
|
Réserve de Groupe par personnage impliqué dans l’apprentissage.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: symbiote
|
totem: symbiote
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -19,11 +19,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -32,8 +32,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496872
|
createdTime: 1786119905167
|
||||||
modifiedTime: 1785775496872
|
modifiedTime: 1786119905177
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!dDIHKXaimA3aaOdO'
|
_key: '!items!dDIHKXaimA3aaOdO'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ system:
|
|||||||
doivent dormir, ils le feront à même le sol – ou dans les sacs de couchage
|
doivent dormir, ils le feront à même le sol – ou dans les sacs de couchage
|
||||||
dont ils disposent dans leur équipement ou sur des couches qu’ils auront
|
dont ils disposent dans leur équipement ou sur des couches qu’ils auront
|
||||||
fabriquées par ailleurs.</p>
|
fabriquées par ailleurs.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: builder
|
totem: builder
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -24,11 +24,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -37,8 +37,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496840
|
createdTime: 1786119905155
|
||||||
modifiedTime: 1785775496840
|
modifiedTime: 1786119905172
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!vXAgLueVVeORjEzO'
|
_key: '!items!vXAgLueVVeORjEzO'
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
folder: CN0O7riHof00DkiC
|
||||||
|
name: As
|
||||||
|
type: ability
|
||||||
|
_id: AR5snodS870lKJDV
|
||||||
|
img: systems/vermine2047/assets/icons/items/ability.webp
|
||||||
|
system:
|
||||||
|
description: >-
|
||||||
|
<p>Si l’Artificier tente une Action pour laquelle une Compétence du Domaine
|
||||||
|
de la Machine qu’il ne possède pas serait nécessaire, il ignore les
|
||||||
|
Handicaps de Rareté de cette Compétences. De plus, s’il tente une Action
|
||||||
|
pour laquelle il possède la Compétence du Domaine de la Machine, mais pas la
|
||||||
|
Spécialisation, il ignore les Handicaps de Spécialisation.</p>
|
||||||
|
type: character
|
||||||
|
totem: human
|
||||||
|
learn:
|
||||||
|
threshold: 5
|
||||||
|
hindrance: 0
|
||||||
|
level:
|
||||||
|
value: 1
|
||||||
|
min: 1
|
||||||
|
max: 3
|
||||||
|
effects: []
|
||||||
|
learned: false
|
||||||
|
effects: []
|
||||||
|
sort: 0
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
kb05teJOwuTQVHwZ: 3
|
||||||
|
flags: {}
|
||||||
|
_stats:
|
||||||
|
coreVersion: '14.365'
|
||||||
|
systemId: vermine2047
|
||||||
|
systemVersion: 0.1.13
|
||||||
|
createdTime: 1786119900163
|
||||||
|
modifiedTime: 1786119900163
|
||||||
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
exportSource: null
|
||||||
|
_key: '!items!AR5snodS870lKJDV'
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ system:
|
|||||||
|
|
||||||
<p>En Mode <strong>Apocalypse</strong>, les événements négatifs font baisser
|
<p>En Mode <strong>Apocalypse</strong>, les événements négatifs font baisser
|
||||||
la Réserve de Groupe de 2D de moins que la normale.</p>
|
la Réserve de Groupe de 2D de moins que la normale.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: hive
|
totem: hive
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -22,11 +22,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -35,8 +35,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496825
|
createdTime: 1786119905150
|
||||||
modifiedTime: 1785775496825
|
modifiedTime: 1786119905171
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!37V8cS5XqgodzRYB'
|
_key: '!items!37V8cS5XqgodzRYB'
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -33,8 +33,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498741
|
createdTime: 1786119917813
|
||||||
modifiedTime: 1785775498741
|
modifiedTime: 1786119917836
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!aHRsfu7k2kEQusni'
|
_key: '!items!aHRsfu7k2kEQusni'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ system:
|
|||||||
<p>En Mode <strong>Apocalypse</strong>, il est possible de dépasser le
|
<p>En Mode <strong>Apocalypse</strong>, il est possible de dépasser le
|
||||||
Niveau Maître, avec un total de 5 correspondant à un Bonus de 3D et 2
|
Niveau Maître, avec un total de 5 correspondant à un Bonus de 3D et 2
|
||||||
Relances, et un total de 6 à un Bonus de 3D et 3 Relances.</p>
|
Relances, et un total de 6 à un Bonus de 3D et 3 Relances.</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: scavenger
|
totem: scavenger
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -24,11 +24,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -37,8 +37,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496848
|
createdTime: 1786119905158
|
||||||
modifiedTime: 1785775496848
|
modifiedTime: 1786119905173
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!svA8aIY2ACliNr4f'
|
_key: '!items!svA8aIY2ACliNr4f'
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -33,8 +33,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498763
|
createdTime: 1786119917832
|
||||||
modifiedTime: 1785775498763
|
modifiedTime: 1786119917846
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!uzUrffNWChsgFiEm'
|
_key: '!items!uzUrffNWChsgFiEm'
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -39,8 +39,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775498746
|
createdTime: 1786119917816
|
||||||
modifiedTime: 1785775498746
|
modifiedTime: 1786119917837
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!NBsKcx5rMfarenBE'
|
_key: '!items!NBsKcx5rMfarenBE'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ system:
|
|||||||
qui visaient son camarade : elle peut se mettre devant lui et encaisser
|
qui visaient son camarade : elle peut se mettre devant lui et encaisser
|
||||||
l’effet, le tir qui le visait peut être dévié ou le tireur peut décider au
|
l’effet, le tir qui le visait peut être dévié ou le tireur peut décider au
|
||||||
dernier moment qu’elle est plus dangereuse que sa cible initiale…</p>
|
dernier moment qu’elle est plus dangereuse que sa cible initiale…</p>
|
||||||
type: group
|
type: totem
|
||||||
totem: symbiote
|
totem: symbiote
|
||||||
learn:
|
learn:
|
||||||
threshold: 5
|
threshold: 5
|
||||||
@@ -24,11 +24,11 @@ system:
|
|||||||
min: 1
|
min: 1
|
||||||
max: 3
|
max: 3
|
||||||
effects: []
|
effects: []
|
||||||
|
learned: false
|
||||||
effects: []
|
effects: []
|
||||||
sort: 0
|
sort: 0
|
||||||
ownership:
|
ownership:
|
||||||
default: 0
|
default: 0
|
||||||
GXSW26kNdNoaabl8: 3
|
|
||||||
kb05teJOwuTQVHwZ: 3
|
kb05teJOwuTQVHwZ: 3
|
||||||
flags: {}
|
flags: {}
|
||||||
_stats:
|
_stats:
|
||||||
@@ -37,8 +37,8 @@ _stats:
|
|||||||
coreVersion: '14.365'
|
coreVersion: '14.365'
|
||||||
systemId: vermine2047
|
systemId: vermine2047
|
||||||
systemVersion: 0.1.13
|
systemVersion: 0.1.13
|
||||||
createdTime: 1785775496873
|
createdTime: 1786119905168
|
||||||
modifiedTime: 1785775496873
|
modifiedTime: 1786119905177
|
||||||
lastModifiedBy: kb05teJOwuTQVHwZ
|
lastModifiedBy: kb05teJOwuTQVHwZ
|
||||||
exportSource: null
|
exportSource: null
|
||||||
_key: '!items!O9hdMXTIln6hx0LU'
|
_key: '!items!O9hdMXTIln6hx0LU'
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user