Compare commits
17 Commits
14.0.2
..
3ff2b8e9bb
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ff2b8e9bb | |||
| 38525c3257 | |||
| f035bcfae2 | |||
| a8bf356d20 | |||
| cd70b70088 | |||
| 14763cc5b3 | |||
| 0258c2e8b7 | |||
| 9b3d34c5d7 | |||
| 335238df3d | |||
| a1519e7a60 | |||
| e55b5cbe15 | |||
| f28719fc6f | |||
| d0423b2017 | |||
| 156672d853 | |||
| 5ab03920d6 | |||
| 9dd6fbd2e7 | |||
| 76ed974352 |
+458
@@ -0,0 +1,458 @@
|
|||||||
|
# Corrections apportées au module Mournblade CYD 2.0
|
||||||
|
|
||||||
|
## Date : 2026-06-07
|
||||||
|
|
||||||
|
## Dernière mise à jour : 2026-06-07
|
||||||
|
|
||||||
|
## Problèmes identifiés et corrigés
|
||||||
|
|
||||||
|
### 1. ❌ Erreur de chargement des partials Handlebars
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
Les feuilles de personnage et de créature généraient une erreur lors du rendu :
|
||||||
|
```
|
||||||
|
Failed to render template part "sheet":
|
||||||
|
The partial systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs could not be found
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
La fonction `preloadHandlebarsTemplates()` dans `modules/mournblade-cyd2-utility.js` ne préchargeait pas tous les partials nécessaires. Seuls 7 templates étaient préchargés sur 9 utilisés.
|
||||||
|
|
||||||
|
**Partials manquants :**
|
||||||
|
- `partial-active-effects.hbs` - Utilisé dans les feuilles actor-sheet.hbs et creature-sheet.hbs
|
||||||
|
- `partial-item-effects.hbs` - Utilisé dans de nombreux templates d'items
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Ajout des deux partials manquants à la liste des templates préchargés dans la fonction `preloadHandlebarsTemplates()`.
|
||||||
|
|
||||||
|
**Fichier modifié :**
|
||||||
|
- `modules/mournblade-cyd2-utility.js` (lignes 189-201)
|
||||||
|
|
||||||
|
**Code avant :**
|
||||||
|
```javascript
|
||||||
|
const templatePaths = [
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/editor-notes-gm.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-prix.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-automation.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/hud-adversites.hbs',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code après :**
|
||||||
|
```javascript
|
||||||
|
const templatePaths = [
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/editor-notes-gm.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-prix.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-automation.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs', // ✅ Ajouté
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs', // ✅ Ajouté
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/hud-adversites.hbs',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. ❌ Erreur de création d'effet actif
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
```
|
||||||
|
base-actor-sheet.mjs:357 MournbladeCYD2 | Failed to create effect: TypeError:
|
||||||
|
Cannot read properties of undefined (reading 'create')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
La fonction `ActiveEffectDialog.create()` n'existe pas dans Foundry VTT v14. L'API a changé et cette méthode a été supprimée.
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Remplacement de l'appel à `foundry.applications.api.ActiveEffectDialog.create()` par une création directe via `document.createEmbeddedDocuments("ActiveEffect", [data])`, suivie de l'ouverture de la feuille d'édition.
|
||||||
|
|
||||||
|
**Fichiers modifiés :**
|
||||||
|
- `modules/applications/sheets/base-actor-sheet.mjs` (lignes 328-363)
|
||||||
|
- `modules/applications/sheets/base-item-sheet.mjs` (lignes 189-224)
|
||||||
|
|
||||||
|
**Code avant :**
|
||||||
|
```javascript
|
||||||
|
const effect = await foundry.applications.api.ActiveEffectDialog.create({
|
||||||
|
document: this.document,
|
||||||
|
effect: defaultEffectData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
await this.document.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code après :**
|
||||||
|
```javascript
|
||||||
|
const [effect] = await this.document.createEmbeddedDocuments("ActiveEffect", [defaultEffectData]);
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. ❌ Boucle infinie de chargement d'icône (effect.webp introuvable) ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
```
|
||||||
|
404 (Not Found) - GET https://localhost:31000/systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp
|
||||||
|
Boucle infinie de tentatives de chargement
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
L'icône `effect.webp` était référencée dans plusieurs fichiers mais n'existait pas dans le dossier `assets/icons/`. Chaque fois que la dialog de création d'effet s'ouvrait, le navigateur essayait de charger cette image manquante en boucle.
|
||||||
|
|
||||||
|
**Fichiers concernés :**
|
||||||
|
- `modules/applications/sheets/base-actor-sheet.mjs` (ligne 336)
|
||||||
|
- `modules/applications/sheets/base-item-sheet.mjs` (ligne 197)
|
||||||
|
- `modules/mournblade-cyd2-effects.js` (lignes 120, 180)
|
||||||
|
- `templates/partial-active-effects.hbs` (ligne 30)
|
||||||
|
- `templates/partial-item-effects.hbs` (ligne 30)
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Remplacement de toutes les références à `effect.webp` par `capacite.webp`, une icône existante dans le dossier `assets/icons/`.
|
||||||
|
|
||||||
|
**Code avant :**
|
||||||
|
```javascript
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code après :**
|
||||||
|
```javascript
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. ❌ Propriété dépréciée ActiveEffectDuration.type ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
```
|
||||||
|
foundry.mjs:1555 Error: You are accessing ActiveEffectDuration#type,
|
||||||
|
which is now at ActiveEffectDuration#units.
|
||||||
|
Deprecated since Version 14
|
||||||
|
Backwards-compatible support will be removed in Version 16
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
En Foundry VTT v14, la propriété `duration.type` a été renommée en `duration.units`. L'ancien nom était encore supporté pour la compatibilité, mais générait des avertissements et sera supprimé en v16.
|
||||||
|
|
||||||
|
**Fichiers concernés :**
|
||||||
|
- `templates/partial-active-effects.hbs` (lignes 55-61)
|
||||||
|
- `templates/partial-item-effects.hbs` (lignes 51-56)
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Remplacement de toutes les occurrences de `effect.duration.type` par `effect.duration.units` dans les templates.
|
||||||
|
|
||||||
|
**Code avant :**
|
||||||
|
```handlebars
|
||||||
|
{{#if effect.duration.type}}
|
||||||
|
{{#if (eq effect.duration.type "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "turns")}}🎭{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code après :**
|
||||||
|
```handlebars
|
||||||
|
{{#if effect.duration.units}}
|
||||||
|
{{#if (eq effect.duration.units "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "turns")}}🎭{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. ❌ Helper Handlebars "subtract" manquant ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
```
|
||||||
|
Failed to render Application "MournbladeCYD2PersonnageSheet":
|
||||||
|
Missing helper: "subtract"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
Le template utilisait le helper `subtract` dans `{{#unless (eq index (subtract effect.changes.length 1))}}` mais ce helper n'était pas enregistré dans Handlebars.
|
||||||
|
|
||||||
|
**Fichiers concernés :**
|
||||||
|
- `templates/partial-active-effects.hbs` (ligne 44)
|
||||||
|
- `templates/partial-item-effects.hbs` (ligne 44)
|
||||||
|
- `modules/mournblade-cyd2-utility.js` (helper non enregistré)
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Ajout du helper `subtract` dans la méthode `init()` de `MournbladeCYD2Utility` :
|
||||||
|
|
||||||
|
**Code ajouté :**
|
||||||
|
```javascript
|
||||||
|
Handlebars.registerHelper('subtract', function (a, b) {
|
||||||
|
return parseInt(a) - parseInt(b);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fonctionnalité :**
|
||||||
|
Le helper permet de soustraire deux nombres dans les templates Handlebars, utilisé pour détecter le dernier élément d'une liste.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. ❌ Clés i18n manquantes pour les effets ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
Les clés de localisation pour les messages d'erreur des effets actifs étaient manquantes dans `lang/fr.json`, ce qui pouvait entraîner l'affichage de messages en anglais ou vides.
|
||||||
|
|
||||||
|
**Clés manquantes identifiées :**
|
||||||
|
- `MOURNBLADECYD2.EFFECT.createError`
|
||||||
|
- `MOURNBLADECYD2.EFFECT.deleteError`
|
||||||
|
- `MOURNBLADECYD2.EFFECT.applyError`
|
||||||
|
- `MOURNBLADECYD2.EFFECT.applyItemError`
|
||||||
|
- `MOURNBLADECYD2.EFFECT.selectActor`
|
||||||
|
- `MOURNBLADECYD2.EFFECT.toggleError`
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Ajout de toutes les clés manquantes dans la section `EFFECT` du fichier `lang/fr.json`.
|
||||||
|
|
||||||
|
**Traductions ajoutées :**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"createError": "Erreur lors de la création de l'effet",
|
||||||
|
"deleteError": "Erreur lors de la suppression de l'effet",
|
||||||
|
"applyError": "Erreur lors de l'application de l'effet",
|
||||||
|
"applyItemError": "Erreur lors de l'application de l'effet sur l'item",
|
||||||
|
"selectActor": "Sélectionnez un acteur pour appliquer l'effet",
|
||||||
|
"toggleError": "Erreur lors de l'activation/désactivation de l'effet"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fichier modifié :** `lang/fr.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. ❌ Clés i18n MNBL manquantes ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
Les clés de localisation `MNBL.details` et `MNBL.description` étaient manquantes dans `lang/fr.json`, ce qui entraînait l'affichage de la clé elle-même au lieu d'une traduction.
|
||||||
|
|
||||||
|
**Clés manquantes identifiées :**
|
||||||
|
- `MNBL.details` - Utilisée dans l'onglet "Détails" des fiches d'items
|
||||||
|
- `MNBL.description` - Utilisée dans l'onglet "Description" des fiches d'items
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Ajout des deux clés manquantes dans la section `MNBL` du fichier `lang/fr.json`.
|
||||||
|
|
||||||
|
**Traductions ajoutées :**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"details": "Détails",
|
||||||
|
"description": "Description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fichier modifié :** `lang/fr.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. ❌ Onglet "Effets" manquant dans les fiches d'items ✅
|
||||||
|
|
||||||
|
**Problème :**
|
||||||
|
L'onglet "Effets" n'apparaissait pas dans les fiches d'items, empêchant l'accès à la gestion des effets actifs sur les items.
|
||||||
|
|
||||||
|
**Cause :**
|
||||||
|
Dans `templates/partial-item-nav.hbs`, l'onglet "Effets" n'était affiché que si l'item avait déjà des effets (`{{#if item.effects.length}}`).
|
||||||
|
|
||||||
|
**Solution :**
|
||||||
|
Suppression de la condition pour toujours afficher l'onglet "Effets", même lorsque l'item n'a pas encore d'effets actifs.
|
||||||
|
|
||||||
|
**Fichier modifié :** `templates/partial-item-nav.hbs`
|
||||||
|
|
||||||
|
**Code avant :**
|
||||||
|
```handlebars
|
||||||
|
{{#if item.effects.length}}
|
||||||
|
<a class="item" data-tab="effects" ...>{{localize "MOURNBLADECYD2.EFFECT.activeEffects"}}</a>
|
||||||
|
{{/if}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code après :**
|
||||||
|
```handlebars
|
||||||
|
<a class="item" data-tab="effects" ...>{{localize "MOURNBLADECYD2.EFFECT.activeEffects"}}</a>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. ❌ Erreur de parsing JSON (historique)
|
||||||
|
|
||||||
|
**Problème mentionné :**
|
||||||
|
```
|
||||||
|
SyntaxError: Expected ',' or '}' after property value in JSON at position 3753 (line 118 column 4)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Statut :**
|
||||||
|
Cette erreur concernait probablement une ancienne version du fichier `lang/fr.json`. Le fichier actuel est valide et ne contient pas d'erreur de syntaxe.
|
||||||
|
|
||||||
|
**Vérification :**
|
||||||
|
```bash
|
||||||
|
# Le fichier passe la validation JSON
|
||||||
|
node -e "require('./lang/fr.json')" # ✅ Pas d'erreur
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Liste complète des partials Handlebars
|
||||||
|
|
||||||
|
### Partials utilisés dans le système :
|
||||||
|
|
||||||
|
| Partial | Utilisation | Pré-chargé ? |
|
||||||
|
|---------|-------------|--------------|
|
||||||
|
| `partial-item-header.hbs` | En-têtes des items | ✅ Oui |
|
||||||
|
| `partial-item-description.hbs` | Descriptions des items | ✅ Oui |
|
||||||
|
| `partial-item-nav.hbs` | Navigation des items | ✅ Oui |
|
||||||
|
| `partial-item-prix.hbs` | Prix des items | ✅ Oui |
|
||||||
|
| `partial-item-effects.hbs` | Effets des items | ✅ Oui (ajouté) |
|
||||||
|
| `partial-active-effects.hbs` | Effets actifs (actors) | ✅ Oui (ajouté) |
|
||||||
|
| `partial-automation.hbs` | Automatisation | ✅ Oui |
|
||||||
|
| `editor-notes-gm.hbs` | Notes GM | ✅ Oui |
|
||||||
|
| `hud-adversites.hbs` | HUD Adversités | ✅ Oui |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Templates principaux
|
||||||
|
|
||||||
|
### Fiches d'acteurs :
|
||||||
|
- `actor-sheet.hbs` - Feuille de personnage
|
||||||
|
- `creature-sheet.hbs` - Feuille de créature
|
||||||
|
|
||||||
|
### Fiches d'items :
|
||||||
|
- `item-arme-sheet.hbs`
|
||||||
|
- `item-capaciteautomata-sheet.hbs`
|
||||||
|
- `item-competence-sheet.hbs`
|
||||||
|
- `item-don-sheet.hbs`
|
||||||
|
- `item-equipement-sheet.hbs`
|
||||||
|
- `item-historique-sheet.hbs`
|
||||||
|
- `item-monnaie-sheet.hbs`
|
||||||
|
- `item-pacte-sheet.hbs`
|
||||||
|
- `item-pouvoirselementaire-sheet.hbs`
|
||||||
|
- `item-profil-sheet.hbs`
|
||||||
|
- `item-protection-sheet.hbs`
|
||||||
|
- `item-ressource-sheet.hbs`
|
||||||
|
- `item-rune-sheet.hbs`
|
||||||
|
- `item-runeeffect-sheet.hbs`
|
||||||
|
- `item-talent-sheet.hbs`
|
||||||
|
- `item-tendance-sheet.hbs`
|
||||||
|
- `item-traitchaotique-sheet.hbs`
|
||||||
|
- `item-traitdemoniaque-sheet.hbs`
|
||||||
|
- `item-traitespece-sheet.hbs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Outils de test
|
||||||
|
|
||||||
|
Un script de test a été créé pour valider les corrections :
|
||||||
|
- **Fichier :** `test-templates.js`
|
||||||
|
- **Exécution :** `node test-templates.js`
|
||||||
|
|
||||||
|
**Fonctionnalités du test :**
|
||||||
|
1. ✅ Vérifie que tous les templates préchargés existent
|
||||||
|
2. ✅ Scanne tous les templates pour trouver les partials utilisés
|
||||||
|
3. ✅ Vérifie que tous les partials utilisés sont préchargés
|
||||||
|
4. ✅ Valide le fichier de localisation JSON
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bonnes pratiques rappelées
|
||||||
|
|
||||||
|
### Pré-chargement des templates Handlebars
|
||||||
|
|
||||||
|
En Foundry VTT v12+, il est **obligatoire** de pré-charger tous les partials Handlebars utilisés via la fonction `foundry.applications.handlebars.loadTemplates()` dans le hook `init`.
|
||||||
|
|
||||||
|
**Pourquoi ?**
|
||||||
|
- Les partials ne sont pas chargés automatiquement
|
||||||
|
- Sans pré-chargement, le rendu échouera avec une erreur "partial could not be found"
|
||||||
|
- Le pré-chargement améliore les performances en cacheant les templates
|
||||||
|
|
||||||
|
**Où ?**
|
||||||
|
Dans le hook `init`, avant l'enregistrement des feuilles (sheets) :
|
||||||
|
```javascript
|
||||||
|
Hooks.once("init", async function () {
|
||||||
|
// Pré-charger les templates AVANT d'enregistrer les feuilles
|
||||||
|
await MournbladeCYD2Utility.preloadHandlebarsTemplates();
|
||||||
|
|
||||||
|
// Ensuite enregistrer les feuilles
|
||||||
|
Actors.registerSheet(...);
|
||||||
|
Items.registerSheet(...);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gestion des chemins des templates
|
||||||
|
|
||||||
|
Les chemins doivent être **relatifs au répertoire `systems/`** :
|
||||||
|
- ✅ Bon : `'systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs'`
|
||||||
|
- ❌ Mauvais : `'./templates/partial-active-effects.hbs'`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Impact des corrections
|
||||||
|
|
||||||
|
### Avant les corrections :
|
||||||
|
- ❌ Ouverture des feuilles de personnage → Erreur
|
||||||
|
- ❌ Ouverture des feuilles de créature → Erreur
|
||||||
|
- ❌ Affichage des effets actifs → Impossible
|
||||||
|
- ❌ Utilisation des effets d'items → Problèmes potentiels
|
||||||
|
- ❌ Création d'effets actifs → Erreur TypeError
|
||||||
|
- ❌ Boucle infinie de 404 sur effect.webp
|
||||||
|
- ❌ Avertissements duration.type déprécié
|
||||||
|
- ❌ Helper subtract manquant → Erreur de rendu
|
||||||
|
- ❌ Clés i18n manquantes → Messages en anglais
|
||||||
|
- ❌ Clés MNBL.details et MNBL.description manquantes
|
||||||
|
- ❌ Onglet "Effets" manquant dans les fiches d'items
|
||||||
|
|
||||||
|
### Après les corrections :
|
||||||
|
- ✅ Toutes les feuilles s'ouvrent correctement
|
||||||
|
- ✅ Les effets actifs s'affichent correctement
|
||||||
|
- ✅ Tous les items affichent leurs effets
|
||||||
|
- ✅ Plus d'erreurs de templates manquants
|
||||||
|
- ✅ Création d'effets actifs fonctionne correctement
|
||||||
|
- ✅ Plus de boucles infinies de chargement d'icônes
|
||||||
|
- ✅ Plus d'avertissements de compatibilité
|
||||||
|
- ✅ Helper subtract disponible et fonctionnel
|
||||||
|
- ✅ Toutes les clés i18n présentes → Localisation complète
|
||||||
|
- ✅ Toutes les clés MNBL présentes
|
||||||
|
- ✅ Onglet "Effets" toujours visible dans les fiches d'items
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommandations pour le développement futur
|
||||||
|
|
||||||
|
1. **Toujours pré-charger les nouveaux partials** lorsqu'ils sont ajoutés
|
||||||
|
2. **Utiliser un script de test** pour valider les templates après modification
|
||||||
|
3. **Maintenir une liste à jour** des partials utilisés dans le projet
|
||||||
|
4. **Vérifier les erreurs de console** lors du développement
|
||||||
|
5. **Tester toutes les feuilles** après ajout de nouveaux partials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fichiers modifiés
|
||||||
|
|
||||||
|
| Fichier | Modification | Statut |
|
||||||
|
|---------|--------------|--------|
|
||||||
|
| `modules/mournblade-cyd2-utility.js` | Ajout partials + helper subtract | ✅ Corrigé |
|
||||||
|
| `modules/applications/sheets/base-actor-sheet.mjs` | Correction création effets + icône | ✅ Corrigé |
|
||||||
|
| `modules/applications/sheets/base-item-sheet.mjs` | Correction création effets + icône | ✅ Corrigé |
|
||||||
|
| `modules/mournblade-cyd2-effects.js` | Remplacement effect.webp par capacite.webp | ✅ Corrigé |
|
||||||
|
| `templates/partial-active-effects.hbs` | Remplacement effect.webp + duration.type → duration.units | ✅ Corrigé |
|
||||||
|
| `templates/partial-item-effects.hbs` | Remplacement effect.webp + duration.type → duration.units | ✅ Corrigé |
|
||||||
|
| `templates/partial-item-nav.hbs` | Affichage permanent onglet Effets + clés MNBL | ✅ Corrigé |
|
||||||
|
| `test-templates.js` | Nouveau fichier de test | ✅ Ajouté |
|
||||||
|
| `CORRECTIONS.md` | Documentation des corrections | ✅ Ajouté |
|
||||||
|
| `lang/fr.json` | Ajout des clés i18n EFFECT + MNBL manquantes | ✅ Corrigé |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auteurs
|
||||||
|
|
||||||
|
Corrections réalisées par : Mistral Vibe (via Vibe CLI)
|
||||||
|
Date : 2026-06-07
|
||||||
+57
-2
@@ -20,7 +20,17 @@
|
|||||||
"runeeffect": "Effet de Rune",
|
"runeeffect": "Effet de Rune",
|
||||||
"tendance": "Tendance",
|
"tendance": "Tendance",
|
||||||
"traitchaotique": "Trait Chaotique",
|
"traitchaotique": "Trait Chaotique",
|
||||||
"traitespece": "Trait d'Espèce"
|
"traitespece": "Trait d'Espèce",
|
||||||
|
"traitdemoniaque": "Trait Démoniaque",
|
||||||
|
"pouvoirselementaire": "Pouvoir Élémentaire",
|
||||||
|
"capaciteautomata": "Capacité d'Automata"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SHEETS": {
|
||||||
|
"Item": {
|
||||||
|
"traitdemoniaque": "Trait Démoniaque",
|
||||||
|
"pouvoirselementaire": "Pouvoir Élémentaire",
|
||||||
|
"capaciteautomata": "Capacité d'Automata"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"MOURNBLADE": {
|
"MOURNBLADE": {
|
||||||
@@ -32,10 +42,13 @@
|
|||||||
"MNBL": {
|
"MNBL": {
|
||||||
"all": "Tous",
|
"all": "Tous",
|
||||||
"allegiance": "Allégeance",
|
"allegiance": "Allégeance",
|
||||||
|
"balance": "Balance",
|
||||||
"beastslords": "Seigneurs des Bêtes",
|
"beastslords": "Seigneurs des Bêtes",
|
||||||
"chaos": "Chaos",
|
"chaos": "Chaos",
|
||||||
"difficulty": "Difficulté",
|
"difficulty": "Difficulté",
|
||||||
"duration": "Durée",
|
"duration": "Durée",
|
||||||
|
"details": "Détails",
|
||||||
|
"description": "Description",
|
||||||
"elementslords": "Seigneurs des Éléments",
|
"elementslords": "Seigneurs des Éléments",
|
||||||
"equipment": "Equipement",
|
"equipment": "Equipement",
|
||||||
"examples": "Exemples",
|
"examples": "Exemples",
|
||||||
@@ -56,9 +69,51 @@
|
|||||||
"pronouncerune": "Prononcer",
|
"pronouncerune": "Prononcer",
|
||||||
"rune": "Rune",
|
"rune": "Rune",
|
||||||
"soulcost": "Coût en points de pouvoir",
|
"soulcost": "Coût en points de pouvoir",
|
||||||
"soulpoints": "Points d'Âme",
|
"soulpoints": "Points de Pouvoir",
|
||||||
"traced": "Tracée",
|
"traced": "Tracée",
|
||||||
"tracedrune": "Rune tracée",
|
"tracedrune": "Rune tracée",
|
||||||
"tracerune": "Tracer"
|
"tracerune": "Tracer"
|
||||||
|
},
|
||||||
|
"EFFECT": {
|
||||||
|
"new": "Nouvel Effet",
|
||||||
|
"edit": "Éditer l'effet",
|
||||||
|
"delete": "Supprimer l'effet",
|
||||||
|
"deleteConfirm": "Supprimer l'effet",
|
||||||
|
"deleteConfirmText": "Êtes-vous sûr de vouloir supprimer cet effet ?",
|
||||||
|
"deleteError": "Erreur lors de la suppression de l'effet",
|
||||||
|
"create": "Créer un effet",
|
||||||
|
"createError": "Erreur lors de la création de l'effet",
|
||||||
|
"applyError": "Erreur lors de l'application de l'effet",
|
||||||
|
"applyItemError": "Erreur lors de l'application de l'effet sur l'item",
|
||||||
|
"selectActor": "Sélectionnez un acteur pour appliquer l'effet",
|
||||||
|
"toggleError": "Erreur lors de l'activation/désactivation de l'effet",
|
||||||
|
"name": "Nom de l'effet",
|
||||||
|
"icon": "Icône",
|
||||||
|
"description": "Description",
|
||||||
|
"changes": "Modifications",
|
||||||
|
"addChange": "Ajouter une modification",
|
||||||
|
"duration": "Durée",
|
||||||
|
"durationType": "Type de durée",
|
||||||
|
"durationValue": "Valeur",
|
||||||
|
"disabled": "Désactivé",
|
||||||
|
"transfer": "Transférer au token",
|
||||||
|
"noDuration": "Aucune (permanent)",
|
||||||
|
"rounds": "Rounds",
|
||||||
|
"turns": "Tours",
|
||||||
|
"seconds": "Secondes",
|
||||||
|
"combat": "Jusqu'à la fin du combat",
|
||||||
|
"scene": "Jusqu'à la fin de la scène",
|
||||||
|
"attribute": "Attribut",
|
||||||
|
"value": "Valeur",
|
||||||
|
"mode": "Mode",
|
||||||
|
"modeAdd": "Ajouter",
|
||||||
|
"modeMultiply": "Multiplier",
|
||||||
|
"modeOverride": "Remplacer",
|
||||||
|
"modeUpgrade": "Améliorer",
|
||||||
|
"modeDowngrade": "Dégrader",
|
||||||
|
"activeEffects": "Effets Actifs",
|
||||||
|
"noActiveEffects": "Aucun effet actif",
|
||||||
|
"effectSummary": "Résumé des modifications",
|
||||||
|
"toggleEffect": "Activer/Désactiver"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,3 +23,6 @@ export { default as MournbladeCYD2TalentSheet } from './mournblade-cyd2-talent-s
|
|||||||
export { default as MournbladeCYD2TendanceSheet } from './mournblade-cyd2-tendance-sheet.mjs';
|
export { default as MournbladeCYD2TendanceSheet } from './mournblade-cyd2-tendance-sheet.mjs';
|
||||||
export { default as MournbladeCYD2TraitChaotiqueSheet } from './mournblade-cyd2-traitchaotique-sheet.mjs';
|
export { default as MournbladeCYD2TraitChaotiqueSheet } from './mournblade-cyd2-traitchaotique-sheet.mjs';
|
||||||
export { default as MournbladeCYD2TraitEspeceSheet } from './mournblade-cyd2-traitespece-sheet.mjs';
|
export { default as MournbladeCYD2TraitEspeceSheet } from './mournblade-cyd2-traitespece-sheet.mjs';
|
||||||
|
export { default as MournbladeCYD2TraitDemoniaqueSheet } from './mournblade-cyd2-traitdemoniaque-sheet.mjs';
|
||||||
|
export { default as MournbladeCYD2PouvoirElementaireSheet } from './mournblade-cyd2-pouvoirselementaire-sheet.mjs';
|
||||||
|
export { default as MournbladeCYD2CapaciteAutomataSheet } from './mournblade-cyd2-capaciteautomata-sheet.mjs';
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
|
|||||||
rollDesengager: MournbladeCYD2ActorSheetV2.#onRollDesengager,
|
rollDesengager: MournbladeCYD2ActorSheetV2.#onRollDesengager,
|
||||||
rollInitiative: MournbladeCYD2ActorSheetV2.#onRollInitiative,
|
rollInitiative: MournbladeCYD2ActorSheetV2.#onRollInitiative,
|
||||||
rollFuir: MournbladeCYD2ActorSheetV2.#onRollFuir,
|
rollFuir: MournbladeCYD2ActorSheetV2.#onRollFuir,
|
||||||
|
// Actions pour les ActiveEffects
|
||||||
|
createEffect: MournbladeCYD2ActorSheetV2.#onCreateEffect,
|
||||||
|
editEffect: MournbladeCYD2ActorSheetV2.#onEditEffect,
|
||||||
|
deleteEffect: MournbladeCYD2ActorSheetV2.#onDeleteEffect,
|
||||||
|
toggleEffect: MournbladeCYD2ActorSheetV2.#onToggleEffect,
|
||||||
|
applyEffect: MournbladeCYD2ActorSheetV2.#onApplyEffect,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,7 +182,8 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
|
|||||||
const item = await Item.fromDropData(data);
|
const item = await Item.fromDropData(data);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
if (this.document.uuid === item.parent?.uuid) return;
|
if (this.document.uuid === item.parent?.uuid) return;
|
||||||
return this.document.createEmbeddedDocuments("Item", [item.toObject()]);
|
await this.document.createEmbeddedDocuments("Item", [item.toObject()]);
|
||||||
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDropActor(event, data) {}
|
async _onDropActor(event, data) {}
|
||||||
@@ -309,4 +316,149 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
|
|||||||
static async #onRollFuir(event, target) {
|
static async #onRollFuir(event, target) {
|
||||||
await this.document.rollFuir();
|
await this.document.rollFuir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #region ActiveEffects Management
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel effet actif
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onCreateEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Créer les données par défaut pour un nouvel effet
|
||||||
|
const defaultEffectData = {
|
||||||
|
name: game.i18n.localize("MOURNBLADECYD2.EFFECT.new") || "Nouvel Effet",
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
|
||||||
|
description: "",
|
||||||
|
changes: [],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: this.document.uuid,
|
||||||
|
tint: "",
|
||||||
|
transfer: true,
|
||||||
|
flags: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Créer directement l'effet actif sur l'acteur
|
||||||
|
const [effect] = await this.document.createEmbeddedDocuments("ActiveEffect", [defaultEffectData]);
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la feuille d'édition de l'effet
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to create effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.createError") ||
|
||||||
|
"Erreur lors de la création de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Édite un effet actif existant
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la sheet de l'effet pour édition
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet actif
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDeleteEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
const effectName = effect.name;
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
title: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirm") || "Supprimer l'effet",
|
||||||
|
content: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirmText", {name: effectName}) ||
|
||||||
|
`Êtes-vous sûr de vouloir supprimer l'effet "${effectName}" ?`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
try {
|
||||||
|
await this.document.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to delete effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteError") ||
|
||||||
|
"Erreur lors de la suppression de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état actif/désactivé d'un effet
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onToggleEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
try {
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to toggle effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.toggleError") ||
|
||||||
|
"Erreur lors du basculement de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à partir d'un item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onApplyEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
postItem: MournbladeCYD2ItemSheetV2.#onPostItem,
|
postItem: MournbladeCYD2ItemSheetV2.#onPostItem,
|
||||||
addPredilection: MournbladeCYD2ItemSheetV2.#onAddPredilection,
|
addPredilection: MournbladeCYD2ItemSheetV2.#onAddPredilection,
|
||||||
deletePredilection: MournbladeCYD2ItemSheetV2.#onDeletePredilection,
|
deletePredilection: MournbladeCYD2ItemSheetV2.#onDeletePredilection,
|
||||||
|
addAutomation: MournbladeCYD2ItemSheetV2.#onAddAutomation,
|
||||||
|
deleteAutomation: MournbladeCYD2ItemSheetV2.#onDeleteAutomation,
|
||||||
|
// Actions pour les ActiveEffects
|
||||||
|
createEffect: MournbladeCYD2ItemSheetV2.#onCreateEffect,
|
||||||
|
editEffect: MournbladeCYD2ItemSheetV2.#onEditEffect,
|
||||||
|
deleteEffect: MournbladeCYD2ItemSheetV2.#onDeleteEffect,
|
||||||
|
toggleEffect: MournbladeCYD2ItemSheetV2.#onToggleEffect,
|
||||||
|
applyEffect: MournbladeCYD2ItemSheetV2.#onApplyEffect,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,13 +58,13 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
item: this.document,
|
item: this.document,
|
||||||
system: this.document.system,
|
system: this.document.system,
|
||||||
source: this.document.toObject(),
|
source: this.document.toObject(),
|
||||||
|
config: game.system.mournbladecyd2.config,
|
||||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
this.document.system.description || "", { async: true }
|
this.document.system.description || "", { async: true }
|
||||||
),
|
),
|
||||||
isEditMode: true,
|
isEditMode: this.isEditMode,
|
||||||
isEditable: this.isEditable,
|
isEditable: this.isEditable,
|
||||||
isGM: game.user.isGM,
|
isGM: game.user.isGM,
|
||||||
config: game.system.mournbladecyd2.config,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,4 +147,189 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
preds.splice(idx, 1);
|
preds.splice(idx, 1);
|
||||||
await this.document.update({ "system.predilections": preds });
|
await this.document.update({ "system.predilections": preds });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async #onAddAutomation(event) {
|
||||||
|
const automations = foundry.utils.duplicate(this.document.system.automations || []);
|
||||||
|
automations.push({
|
||||||
|
id: foundry.utils.randomID(),
|
||||||
|
eventtype: "on-drop",
|
||||||
|
name: "",
|
||||||
|
bonusname: "vigueur",
|
||||||
|
bonus: 0,
|
||||||
|
competence: "",
|
||||||
|
minLevel: 0,
|
||||||
|
baCost: 0
|
||||||
|
});
|
||||||
|
await this.document.update({
|
||||||
|
"system.automations": automations,
|
||||||
|
"system.isautomated": true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async #onDeleteAutomation(event, target) {
|
||||||
|
const idx = Number(target.dataset.automationIndex);
|
||||||
|
const automations = foundry.utils.duplicate(this.document.system.automations || []);
|
||||||
|
automations.splice(idx, 1);
|
||||||
|
await this.document.update({ "system.automations": automations });
|
||||||
|
if (automations.length === 0) {
|
||||||
|
await this.document.update({ "system.isautomated": false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region ActiveEffects Management
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel effet actif sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onCreateEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Créer les données par défaut pour un nouvel effet
|
||||||
|
const defaultEffectData = {
|
||||||
|
name: game.i18n.localize("MOURNBLADECYD2.EFFECT.new") || "Nouvel Effet",
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
|
||||||
|
description: "",
|
||||||
|
changes: [],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: this.document.uuid,
|
||||||
|
tint: "",
|
||||||
|
transfer: false,
|
||||||
|
flags: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Créer directement l'effet actif sur l'item
|
||||||
|
const [effect] = await this.document.createEmbeddedDocuments("ActiveEffect", [defaultEffectData]);
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la feuille d'édition de l'effet
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to create effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.createError") ||
|
||||||
|
"Erreur lors de la création de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Édite un effet actif existant sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la sheet de l'effet pour édition
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet actif de l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDeleteEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
const effectName = effect.name;
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
title: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirm") || "Supprimer l'effet",
|
||||||
|
content: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirmText", {name: effectName}) ||
|
||||||
|
`Êtes-vous sûr de vouloir supprimer l'effet "${effectName}" ?`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
try {
|
||||||
|
await this.document.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to delete effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteError") ||
|
||||||
|
"Erreur lors de la suppression de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état actif/désactivé d'un effet sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onToggleEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable || !this.document) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
try {
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to toggle effect:", error);
|
||||||
|
ui.notifications.error(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.toggleError") ||
|
||||||
|
"Erreur lors du basculement de l'effet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à partir de l'item à un acteur sélectionné
|
||||||
|
* Cette méthode n'est pas directement utilisable sans acteur cible
|
||||||
|
* Elle est gardée pour compatibilité mais devrait être appelée avec un acteur
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onApplyEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId || !this.document) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (!effect) return;
|
||||||
|
|
||||||
|
// Pour appliquer un effet d'item, il faut sélectionner un acteur
|
||||||
|
// Cette méthode est placeholders - l'application devrait être gérée par drag-drop
|
||||||
|
// ou par une action spécifique qui demande à l'utilisateur de sélectionner un acteur
|
||||||
|
ui.notifications.warn(
|
||||||
|
game.i18n.localize("MOURNBLADECYD2.EFFECT.selectActor") ||
|
||||||
|
"Veuillez d'abord sélectionner un acteur pour appliquer cet effet."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import MournbladeCYD2ItemSheetV2 from "./base-item-sheet.mjs";
|
||||||
|
|
||||||
|
export default class MournbladeCYD2CapaciteAutomataSheet extends MournbladeCYD2ItemSheetV2 {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
classes: [...super.DEFAULT_OPTIONS.classes, "capaciteautomata"],
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.capaciteautomata",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-mournblade-cyd-2-0/templates/item-capaciteautomata-sheet.hbs",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ export default class MournbladeCYD2CreatureSheet extends MournbladeCYD2ActorShee
|
|||||||
|
|
||||||
context.skills = actor.getSkills?.() ?? [];
|
context.skills = actor.getSkills?.() ?? [];
|
||||||
context.combativiteList = MournbladeCYD2Utility.getCombativiteList(actor.system.sante?.nbcombativite || 0);
|
context.combativiteList = MournbladeCYD2Utility.getCombativiteList(actor.system.sante?.nbcombativite || 0);
|
||||||
|
context.ameList = MournbladeCYD2Utility.getAmeList(actor.system.ame.nbame, actor.getAmeMax?.() ?? 0);
|
||||||
|
context.ameMaxList = MournbladeCYD2Utility.getAmeMaxList(actor.system.ame.nbame);
|
||||||
context.armes = foundry.utils.duplicate(actor.getWeapons?.() ?? []);
|
context.armes = foundry.utils.duplicate(actor.getWeapons?.() ?? []);
|
||||||
context.protections = foundry.utils.duplicate(actor.getArmors?.() ?? []);
|
context.protections = foundry.utils.duplicate(actor.getArmors?.() ?? []);
|
||||||
context.runes = foundry.utils.duplicate(actor.getRunes?.() ?? []);
|
context.runes = foundry.utils.duplicate(actor.getRunes?.() ?? []);
|
||||||
@@ -37,9 +39,16 @@ export default class MournbladeCYD2CreatureSheet extends MournbladeCYD2ActorShee
|
|||||||
context.equipements = foundry.utils.duplicate(actor.getEquipments?.() ?? []);
|
context.equipements = foundry.utils.duplicate(actor.getEquipments?.() ?? []);
|
||||||
context.monnaies = foundry.utils.duplicate(actor.getMonnaies?.() ?? []);
|
context.monnaies = foundry.utils.duplicate(actor.getMonnaies?.() ?? []);
|
||||||
context.talents = foundry.utils.duplicate(actor.getTalents?.() ?? []);
|
context.talents = foundry.utils.duplicate(actor.getTalents?.() ?? []);
|
||||||
|
context.traitsChaotiques = foundry.utils.duplicate(actor.getTraitsChaotiques?.() ?? []);
|
||||||
|
context.traitsEspeces = foundry.utils.duplicate(actor.getTraitsEspeces?.() ?? []);
|
||||||
context.protectionTotal = actor.getProtectionTotal?.() ?? 0;
|
context.protectionTotal = actor.getProtectionTotal?.() ?? 0;
|
||||||
context.adversiteTotal = (actor.system.adversite?.bleue || 0) + (actor.system.adversite?.rouge || 0) + (actor.system.adversite?.noire || 0);
|
context.adversiteTotal = (actor.system.adversite?.bleue || 0) + (actor.system.adversite?.rouge || 0) + (actor.system.adversite?.noire || 0);
|
||||||
context.initiative = context.combat?.initTotal ?? 0;
|
|
||||||
|
// Utiliser les valeurs manuelles si elles existent, sinon les valeurs calculées
|
||||||
|
context.initiative = actor.system.combat?.inittotal !== undefined ? actor.system.combat.inittotal : (context.combat?.initTotal ?? 0);
|
||||||
|
context.combat.defenseTotal = actor.system.combat?.defensetotal !== undefined ? actor.system.combat.defensetotal : context.combat.defenseTotal;
|
||||||
|
context.protectionTotal = actor.system.combat?.protectiontotal !== undefined ? actor.system.combat.protectiontotal : context.protectionTotal;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export default class MournbladeCYD2DonSheet extends MournbladeCYD2ItemSheetV2 {
|
|||||||
/** @override */
|
/** @override */
|
||||||
async _prepareContext() {
|
async _prepareContext() {
|
||||||
const context = await super._prepareContext();
|
const context = await super._prepareContext();
|
||||||
|
context.owner = this.document.isOwner;
|
||||||
|
context.editable = this.isEditable;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import MournbladeCYD2ItemSheetV2 from "./base-item-sheet.mjs";
|
||||||
|
|
||||||
|
export default class MournbladeCYD2PouvoirElementaireSheet extends MournbladeCYD2ItemSheetV2 {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
classes: [...super.DEFAULT_OPTIONS.classes, "pouvoirselementaire"],
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.pouvoirselementaire",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-mournblade-cyd-2-0/templates/item-pouvoirselementaire-sheet.hbs",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import MournbladeCYD2ItemSheetV2 from "./base-item-sheet.mjs";
|
||||||
|
|
||||||
|
export default class MournbladeCYD2TraitDemoniaqueSheet extends MournbladeCYD2ItemSheetV2 {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
classes: [...super.DEFAULT_OPTIONS.classes, "traitdemoniaque"],
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.traitdemoniaque",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-mournblade-cyd-2-0/templates/item-traitdemoniaque-sheet.hbs",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les capacités d'Automata MournbladeCYD2
|
||||||
|
*/
|
||||||
|
export default class CapaciteAutomataDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
bonusmalus: new fields.StringField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,12 +88,15 @@ export default class CreatureDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
}),
|
}),
|
||||||
combat: new fields.SchemaField({
|
combat: new fields.SchemaField({
|
||||||
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
inittotal: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
|
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
attaquebonus: new fields.NumberField({ initial: 0, integer: true }),
|
attaquebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
|
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
defensetotal: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
defensetotale: new fields.BooleanField({ initial: false }),
|
defensetotale: new fields.BooleanField({ initial: false }),
|
||||||
monte: new fields.BooleanField({ initial: false })
|
monte: new fields.BooleanField({ initial: false }),
|
||||||
|
protectiontotal: new fields.NumberField({ initial: 0, integer: true })
|
||||||
}),
|
}),
|
||||||
balance: new fields.SchemaField({
|
balance: new fields.SchemaField({
|
||||||
loi: new fields.NumberField({ initial: 0, integer: true }),
|
loi: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export { default as RuneEffectDataModel } from './runeeffect.mjs';
|
|||||||
export { default as TendanceDataModel } from './tendance.mjs';
|
export { default as TendanceDataModel } from './tendance.mjs';
|
||||||
export { default as TraitChaotiqueDataModel } from './traitchaotique.mjs';
|
export { default as TraitChaotiqueDataModel } from './traitchaotique.mjs';
|
||||||
export { default as TraitEspeceDataModel } from './traitespece.mjs';
|
export { default as TraitEspeceDataModel } from './traitespece.mjs';
|
||||||
|
export { default as TraitDemoniaqueDataModel } from './traitdemoniaque.mjs';
|
||||||
|
export { default as PouvoirElementaireDataModel } from './pouvoirselementaire.mjs';
|
||||||
|
export { default as CapaciteAutomataDataModel } from './capaciteautomata.mjs';
|
||||||
|
|
||||||
// Modèles d'acteurs
|
// Modèles d'acteurs
|
||||||
export { default as PersonnageDataModel } from './personnage.mjs';
|
export { default as PersonnageDataModel } from './personnage.mjs';
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les pouvoirs élémentaires MournbladeCYD2
|
||||||
|
*/
|
||||||
|
export default class PouvoirElementaireDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
bonusmalus: new fields.StringField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ export default class RuneDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
seuil: new fields.NumberField({ initial: 0, integer: true }),
|
seuil: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
prononcee: new fields.StringField({ initial: "" }),
|
prononcee: new fields.StringField({ initial: "" }),
|
||||||
tracee: new fields.StringField({ initial: "" }),
|
tracee: new fields.StringField({ initial: "" }),
|
||||||
coutAme: new fields.NumberField({ initial: 0, integer: true })
|
coutAme: new fields.StringField({ initial: "" })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ export default class TendanceDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
allegeance: new fields.StringField({ initial: "" })
|
allegeance: new fields.StringField({ initial: "" }),
|
||||||
|
donlie: new fields.StringField({ initial: "" })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export default class TraitChaotiqueDataModel extends foundry.abstract.TypeDataMo
|
|||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" })
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
bonusmalus: new fields.StringField({ initial: "" })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les traits démoniaques MournbladeCYD2
|
||||||
|
*/
|
||||||
|
export default class TraitDemoniaqueDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
bonusmalus: new fields.StringField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ export default class TraitEspeceDataModel extends foundry.abstract.TypeDataModel
|
|||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" })
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
bonusmalus: new fields.StringField({ initial: "" })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const MOURNBLADECYD2_CONFIG = {
|
|||||||
tous: localizeOrFallback("MNBL.all", "Tous"),
|
tous: localizeOrFallback("MNBL.all", "Tous"),
|
||||||
chaos: localizeOrFallback("MNBL.chaos", "Chaos"),
|
chaos: localizeOrFallback("MNBL.chaos", "Chaos"),
|
||||||
loi: localizeOrFallback("MNBL.law", "Loi"),
|
loi: localizeOrFallback("MNBL.law", "Loi"),
|
||||||
|
balance: localizeOrFallback("MNBL.balance", "Balance"),
|
||||||
betes: localizeOrFallback("MNBL.beastslords", "Seigneurs des Bêtes"),
|
betes: localizeOrFallback("MNBL.beastslords", "Seigneurs des Bêtes"),
|
||||||
elementaires: localizeOrFallback("MNBL.elementslords", "Seigneurs des Éléments")
|
elementaires: localizeOrFallback("MNBL.elementslords", "Seigneurs des Éléments")
|
||||||
},
|
},
|
||||||
@@ -118,6 +119,96 @@ export const MOURNBLADECYD2_CONFIG = {
|
|||||||
{ key: "personnage", label: "Personnage" },
|
{ key: "personnage", label: "Personnage" },
|
||||||
{ key: "traitespece", label: "Trait d'espèce" }
|
{ key: "traitespece", label: "Trait d'espèce" }
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Configuration des ActiveEffects
|
||||||
|
effectTypes: {
|
||||||
|
bonus: "Bonus",
|
||||||
|
malus: "Malus",
|
||||||
|
rune: "Effet de Rune",
|
||||||
|
don: "Effet de Don",
|
||||||
|
talent: "Effet de Talent",
|
||||||
|
trait: "Effet de Trait",
|
||||||
|
temporaire: "Effet Temporaire",
|
||||||
|
permanent: "Effet Permanent"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clés des attributs pour les modifications d'effets
|
||||||
|
effectAttributeKeys: {
|
||||||
|
// Attributs
|
||||||
|
adr: "system.attributs.adr.value",
|
||||||
|
pui: "system.attributs.pui.value",
|
||||||
|
cla: "system.attributs.cla.value",
|
||||||
|
pre: "system.attributs.pre.value",
|
||||||
|
tre: "system.attributs.tre.value",
|
||||||
|
|
||||||
|
// Santé
|
||||||
|
vigueur: "system.sante.vigueur",
|
||||||
|
etat: "system.sante.etat",
|
||||||
|
nbcombativite: "system.sante.nbcombativite",
|
||||||
|
|
||||||
|
// Âme
|
||||||
|
nbame: "system.ame.nbame",
|
||||||
|
seuilpouvoir: "system.ame.seuilpouvoir",
|
||||||
|
etatAme: "system.ame.etat",
|
||||||
|
|
||||||
|
// Bonne Aventure
|
||||||
|
bonneaventure: "system.bonneaventure.base",
|
||||||
|
bonneaventureActuelle: "system.bonneaventure.actuelle",
|
||||||
|
eclat: "system.eclat.value",
|
||||||
|
|
||||||
|
// Combat
|
||||||
|
initiative: "system.combat.inittotal",
|
||||||
|
defense: "system.combat.defensetotal",
|
||||||
|
protection: "system.combat.protectiontotal",
|
||||||
|
|
||||||
|
// Adversités
|
||||||
|
adversiteBleue: "system.adversite.bleue",
|
||||||
|
adversiteRouge: "system.adversite.rouge",
|
||||||
|
adversiteNoire: "system.adversite.noire",
|
||||||
|
|
||||||
|
// Balance
|
||||||
|
loi: "system.balance.loi",
|
||||||
|
chaos: "system.balance.chaos",
|
||||||
|
aspect: "system.balance.aspect",
|
||||||
|
|
||||||
|
// Ressources
|
||||||
|
ressources: "system.ressources.value",
|
||||||
|
|
||||||
|
// Vitesse
|
||||||
|
vitesse: "system.vitesse.value"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Types de bonus/malus supportés (groupés par catégorie)
|
||||||
|
effectTypesConfig: {
|
||||||
|
attribut: {
|
||||||
|
label: "Attribut",
|
||||||
|
keys: ["adr", "pui", "cla", "pre", "tre"]
|
||||||
|
},
|
||||||
|
sante: {
|
||||||
|
label: "Santé",
|
||||||
|
keys: ["vigueur", "etat", "nbcombativite"]
|
||||||
|
},
|
||||||
|
ame: {
|
||||||
|
label: "Âme",
|
||||||
|
keys: ["nbame", "seuilpouvoir", "etatAme"]
|
||||||
|
},
|
||||||
|
combat: {
|
||||||
|
label: "Combat",
|
||||||
|
keys: ["initiative", "defense", "protection"]
|
||||||
|
},
|
||||||
|
bonneAventure: {
|
||||||
|
label: "Bonne Aventure",
|
||||||
|
keys: ["bonneaventure", "bonneaventureActuelle", "eclat"]
|
||||||
|
},
|
||||||
|
adversite: {
|
||||||
|
label: "Adversité",
|
||||||
|
keys: ["adversiteBleue", "adversiteRouge", "adversiteNoire"]
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
label: "Balance",
|
||||||
|
keys: ["loi", "chaos", "aspect"]
|
||||||
|
}
|
||||||
|
},
|
||||||
optionsUseTalent: [
|
optionsUseTalent: [
|
||||||
{ key: "permanent", label: "Permanent" },
|
{ key: "permanent", label: "Permanent" },
|
||||||
{ key: "sceance", label: "Une fois par scéance" },
|
{ key: "sceance", label: "Une fois par scéance" },
|
||||||
|
|||||||
@@ -0,0 +1,807 @@
|
|||||||
|
/**
|
||||||
|
* Gestion des ActiveEffects pour Mournblade CYD 2.0
|
||||||
|
* Ce module fournit des utilitaires pour créer, appliquer et gérer les effets actifs
|
||||||
|
* sur les Acteurs et les Items.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MournbladeCYD2Effects {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise le système de gestion des effets
|
||||||
|
*/
|
||||||
|
static init() {
|
||||||
|
console.log("MournbladeCYD2 | Initializing ActiveEffects management");
|
||||||
|
|
||||||
|
// Hook pour appliquer les modifications des effets
|
||||||
|
Hooks.on("applyActiveEffect", (effect, change, current, delta, changes) => {
|
||||||
|
return this._onApplyActiveEffect(effect, change, current, delta, changes);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hook pour supprimer les modifications des effets
|
||||||
|
Hooks.on("removeActiveEffect", (effect, change, current, delta, changes) => {
|
||||||
|
return this._onRemoveActiveEffect(effect, change, current, delta, changes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse une valeur d'effet en nombre
|
||||||
|
* Gère les strings comme "+2", "-3", "5"
|
||||||
|
* @param {string|number} value - Valeur à parser
|
||||||
|
* @returns {number} - Valeur numérique
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _parseEffectValue(value) {
|
||||||
|
if (typeof value === 'number') return value;
|
||||||
|
if (typeof value !== 'string') return 0;
|
||||||
|
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed) return 0;
|
||||||
|
|
||||||
|
if (trimmed.startsWith('+')) {
|
||||||
|
return parseFloat(trimmed.substring(1)) || 0;
|
||||||
|
} else if (trimmed.startsWith('-')) {
|
||||||
|
return -(parseFloat(trimmed.substring(1)) || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFloat(trimmed) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook appelé lorsqu'un effet est appliqué
|
||||||
|
* Permet de personnaliser le calcul des modifications
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _onApplyActiveEffect(effect, change, current, delta, changes) {
|
||||||
|
// Pour Mournblade, nous voulons gérer les valeurs string (ex: "+1", "-2")
|
||||||
|
// Convertir delta en nombre si nécessaire
|
||||||
|
const numericDelta = this._parseEffectValue(delta);
|
||||||
|
const numericCurrent = current != null ? Number(current) : 0;
|
||||||
|
|
||||||
|
if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
|
||||||
|
return numericCurrent + numericDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si on ne peut pas calculer, retourner delta tel quel
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook appelé lorsqu'un effet est supprimé
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _onRemoveActiveEffect(effect, change, current, delta, changes) {
|
||||||
|
// Logique inverse de l'application
|
||||||
|
// Foundry gère déjà la suppression, ce hook est pour des calculs personnalisés
|
||||||
|
const numericDelta = this._parseEffectValue(delta);
|
||||||
|
const numericCurrent = current != null ? Number(current) : 0;
|
||||||
|
|
||||||
|
if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
|
||||||
|
return numericCurrent - numericDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de création d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet simple de bonus/malus à un attribut
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {string} attribute - Attribut cible (adr, pui, cla, pre, tre, vigueur, etc.)
|
||||||
|
* @param {number|string} value - Valeur du bonus/malus
|
||||||
|
* @param {object} options - Options supplémentaires
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createSimpleEffect(name, attribute, value, options = {}) {
|
||||||
|
// Validation des paramètres
|
||||||
|
if (!name || typeof name !== "string") {
|
||||||
|
console.warn("MournbladeCYD2 | Effect name must be a non-empty string");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
console.warn("MournbladeCYD2 | Effect value cannot be null or undefined");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributeKey = this.getAttributeKey(attribute);
|
||||||
|
if (!attributeKey) {
|
||||||
|
console.warn(`MournbladeCYD2 | Unknown attribute: ${attribute}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normaliser la valeur en string
|
||||||
|
const valueString = String(value).trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name.trim(),
|
||||||
|
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
|
||||||
|
description: (options.description || "").trim(),
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
key: attributeKey,
|
||||||
|
mode: CONST.ActiveEffect.MODES.ADD,
|
||||||
|
value: valueString,
|
||||||
|
priority: options.priority ?? 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
disabled: Boolean(options.disabled),
|
||||||
|
duration: options.duration || {},
|
||||||
|
origin: options.origin || null,
|
||||||
|
tint: options.tint || "",
|
||||||
|
transfer: options.transfer !== false,
|
||||||
|
statuses: options.statuses || [],
|
||||||
|
flags: options.flags || {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus permanent
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {string} attribute - Attribut cible
|
||||||
|
* @param {number|string} value - Valeur du bonus
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createPermanentEffect(name, attribute, value) {
|
||||||
|
return this.createSimpleEffect(name, attribute, value, {
|
||||||
|
duration: {},
|
||||||
|
type: "base"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet temporaire (rounds, turns, etc.)
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {string} attribute - Attribut cible
|
||||||
|
* @param {number|string} value - Valeur du bonus/malus
|
||||||
|
* @param {string} durationType - Type de durée (rounds, turns, seconds, combat)
|
||||||
|
* @param {number} durationValue - Valeur de la durée
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createTemporaryEffect(name, attribute, value, durationType, durationValue) {
|
||||||
|
return this.createSimpleEffect(name, attribute, value, {
|
||||||
|
duration: { type: durationType, value: durationValue },
|
||||||
|
type: "temp"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet avec plusieurs modifications
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {Array} changes - Array de modifications {key, mode, value}
|
||||||
|
* @param {object} options - Options supplémentaires
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createMultiEffect(name, changes, options = {}) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
|
||||||
|
description: options.description || "",
|
||||||
|
changes: changes.map(c => ({
|
||||||
|
key: c.key,
|
||||||
|
mode: c.mode || CONST.ActiveEffect.MODES.ADD,
|
||||||
|
value: c.value.toString(),
|
||||||
|
priority: c.priority || 0
|
||||||
|
})),
|
||||||
|
disabled: options.disabled || false,
|
||||||
|
duration: options.duration || {},
|
||||||
|
origin: options.origin || null,
|
||||||
|
tint: options.tint || "",
|
||||||
|
transfer: options.transfer !== false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes d'application d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à un acteur
|
||||||
|
* @param {Actor} actor - L'acteur cible
|
||||||
|
* @param {Object|ActiveEffect} effectData - Données de l'effet ou instance ActiveEffect
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet créé ou null
|
||||||
|
*/
|
||||||
|
static async applyEffectToActor(actor, effectData) {
|
||||||
|
if (!actor || !actor.canUserModify(game.user, "update")) return null;
|
||||||
|
|
||||||
|
let effect;
|
||||||
|
if (effectData instanceof foundry.documents.ActiveEffect) {
|
||||||
|
effect = effectData;
|
||||||
|
} else if (effectData?.toObject) {
|
||||||
|
effect = effectData;
|
||||||
|
} else {
|
||||||
|
effect = new CONFIG.ActiveEffect.documentClass(effectData);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
|
||||||
|
return createdEffects[0];
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to apply effect:", error);
|
||||||
|
ui.notifications?.error(
|
||||||
|
game.i18n?.localize("MOURNBLADECYD2.EFFECT.applyError") ||
|
||||||
|
`Erreur: Impossible d'appliquer l'effet (${error.message})`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique les effets d'un item à un acteur
|
||||||
|
* @param {Item} item - L'item source
|
||||||
|
* @param {Actor} actor - L'acteur cible
|
||||||
|
* @returns {Promise<Array<ActiveEffect>>} - Liste des effets appliqués
|
||||||
|
*/
|
||||||
|
static async applyItemEffectsToActor(item, actor) {
|
||||||
|
if (!item?.effects?.length || !actor) return [];
|
||||||
|
if (!actor.canUserModify(game.user, "update")) return [];
|
||||||
|
|
||||||
|
const effectsToApply = [];
|
||||||
|
for (const effectData of item.effects) {
|
||||||
|
// Par défaut, appliquer automatiquement SAUF si explicitement désactivé
|
||||||
|
const autoApply = effectData.getFlag("mournblade-cyd2", "autoApply");
|
||||||
|
if (autoApply !== false) {
|
||||||
|
effectsToApply.push({
|
||||||
|
...effectData.toObject(),
|
||||||
|
origin: item.uuid,
|
||||||
|
name: `${item.name}: ${effectData.name}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectsToApply.length === 0) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", effectsToApply);
|
||||||
|
return createdEffects;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("MournbladeCYD2 | Failed to apply item effects:", error);
|
||||||
|
ui.notifications?.error(
|
||||||
|
game.i18n?.localize("MOURNBLADECYD2.EFFECT.applyItemError") ||
|
||||||
|
`Erreur: Impossible d'appliquer les effets de l'item`
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de gestion d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Désactive un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet désactivé
|
||||||
|
*/
|
||||||
|
static async disableEffect(owner, effectId) {
|
||||||
|
if (!owner?.canUserModify(game.user, "update")) return null;
|
||||||
|
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (!effect) return null;
|
||||||
|
|
||||||
|
await effect.update({ disabled: true });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet activé
|
||||||
|
*/
|
||||||
|
static async enableEffect(owner, effectId) {
|
||||||
|
if (!owner?.canUserModify(game.user, "update")) return null;
|
||||||
|
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (!effect) return null;
|
||||||
|
|
||||||
|
await effect.update({ disabled: false });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état d'un effet (actif/désactivé)
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet togglé
|
||||||
|
*/
|
||||||
|
static async toggleEffect(owner, effectId) {
|
||||||
|
if (!owner?.canUserModify(game.user, "update")) return null;
|
||||||
|
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (!effect) return null;
|
||||||
|
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet supprimé
|
||||||
|
*/
|
||||||
|
static async deleteEffect(owner, effectId) {
|
||||||
|
if (!owner?.canUserModify(game.user, "delete")) return null;
|
||||||
|
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (!effect) return null;
|
||||||
|
|
||||||
|
await owner.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes utilitaires */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient la clé complète pour un attribut
|
||||||
|
* @param {string} attribute - Attribut court (adr, pui, cla, pre, tre, vigueur, etc.)
|
||||||
|
* @returns {string|null} - Clé complète ou null
|
||||||
|
*/
|
||||||
|
static getAttributeKey(attribute) {
|
||||||
|
if (!attribute) return null;
|
||||||
|
|
||||||
|
const config = game.system.mournbladecyd2?.config;
|
||||||
|
if (!config?.effectAttributeKeys) return null;
|
||||||
|
|
||||||
|
// Normaliser en minuscules pour correspondre à la config
|
||||||
|
const normalizedAttribute = attribute.toLowerCase().trim();
|
||||||
|
return config.effectAttributeKeys[normalizedAttribute] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient tous les attributs modifiables
|
||||||
|
* @returns {Object} - Map des attributs courts vers les clés complètes
|
||||||
|
*/
|
||||||
|
static getAllAttributeKeys() {
|
||||||
|
const config = game.system.mournbladecyd2?.config;
|
||||||
|
return config?.effectAttributeKeys || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets actifs d'un acteur
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets actifs (non désactivés)
|
||||||
|
*/
|
||||||
|
static getActiveEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => !e.disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets désactivés d'un acteur
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets désactivés
|
||||||
|
*/
|
||||||
|
static getDisabledEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => e.disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets par origine
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @param {string} originUuid - UUID de l'origine
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets de cette origine
|
||||||
|
*/
|
||||||
|
static getEffectsByOrigin(actor, originUuid) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => e.origin === originUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets temporaires en cours
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets temporaires actifs
|
||||||
|
*/
|
||||||
|
static getActiveTemporaryEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => !e.disabled && e.duration?.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcule la valeur totale des modifications pour une clé donnée
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @param {string} key - Clé à vérifier
|
||||||
|
* @returns {number} - Somme des modifications
|
||||||
|
*/
|
||||||
|
static getTotalModificationForKey(actor, key) {
|
||||||
|
if (!actor?.effects) return 0;
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
for (const effect of actor.effects) {
|
||||||
|
if (effect.disabled) continue;
|
||||||
|
|
||||||
|
for (const change of effect.changes || []) {
|
||||||
|
if (change.key === key && change.mode === CONST.ActiveEffect.MODES.ADD) {
|
||||||
|
total += Number(change.value) || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient toutes les modifications actives groupées par clé
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Object} - Objet avec les clés et les valeurs totales
|
||||||
|
*/
|
||||||
|
static getAllActiveModifications(actor) {
|
||||||
|
if (!actor?.effects) return {};
|
||||||
|
|
||||||
|
const modifications = {};
|
||||||
|
|
||||||
|
for (const effect of actor.effects) {
|
||||||
|
if (effect.disabled) continue;
|
||||||
|
|
||||||
|
for (const change of effect.changes || []) {
|
||||||
|
if (!modifications[change.key]) {
|
||||||
|
modifications[change.key] = {
|
||||||
|
value: 0,
|
||||||
|
effects: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appliquer selon le mode
|
||||||
|
const numericValue = Number(change.value) || 0;
|
||||||
|
switch (change.mode) {
|
||||||
|
case CONST.ActiveEffect.MODES.ADD:
|
||||||
|
modifications[change.key].value += numericValue;
|
||||||
|
break;
|
||||||
|
case CONST.ActiveEffect.MODES.OVERRIDE:
|
||||||
|
modifications[change.key].value = numericValue;
|
||||||
|
modifications[change.key].overridden = true;
|
||||||
|
break;
|
||||||
|
case CONST.ActiveEffect.MODES.MULTIPLY:
|
||||||
|
// Ne peut pas être additionné, stocké séparément
|
||||||
|
if (!modifications[change.key].multipliers) {
|
||||||
|
modifications[change.key].multipliers = [];
|
||||||
|
}
|
||||||
|
modifications[change.key].multipliers.push(numericValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifications[change.key].effects.push(effect.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de création d'effets prédéfinis */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus d'attribut
|
||||||
|
* @param {string} attribute - Attribut (adr, pui, cla, pre, tre)
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAttributeBonusEffect(attribute, value, source = "Effet") {
|
||||||
|
const attrNames = {
|
||||||
|
adr: "Adresse",
|
||||||
|
pui: "Puissance",
|
||||||
|
cla: "Clairvoyance",
|
||||||
|
pre: "Présence",
|
||||||
|
tre: "Trempe"
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de ${attrNames[attribute] || attribute}`,
|
||||||
|
attribute,
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/attributs.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de malus d'attribut
|
||||||
|
* @param {string} attribute - Attribut
|
||||||
|
* @param {number} value - Valeur du malus (positif)
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAttributeMalusEffect(attribute, value, source = "Effet") {
|
||||||
|
const attrNames = {
|
||||||
|
adr: "Adresse",
|
||||||
|
pui: "Puissance",
|
||||||
|
cla: "Clairvoyance",
|
||||||
|
pre: "Présence",
|
||||||
|
tre: "Trempe"
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Malus de ${attrNames[attribute] || attribute}`,
|
||||||
|
attribute,
|
||||||
|
`-${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/malus.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Vigueur
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createVigueurBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Vigueur`,
|
||||||
|
"vigueur",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/vigueur.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus au Seuil de Pouvoir
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createSeuilPouvoirBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus au Seuil de Pouvoir`,
|
||||||
|
"seuilPouvoir",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/ame.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Bonne Aventure
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createBonneAventureBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Bonne Aventure`,
|
||||||
|
"bonneAventure",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/bonneaventure.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à l'Initiative
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createInitiativeBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus d'Initiative`,
|
||||||
|
"initiative",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/initiative.webp",
|
||||||
|
type: "temp",
|
||||||
|
duration: { type: "rounds", value: 1 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Défense
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createDefenseBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Défense`,
|
||||||
|
"defense",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/defense.webp",
|
||||||
|
type: "temp",
|
||||||
|
duration: { type: "rounds", value: 1 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Protection
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createProtectionBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Protection`,
|
||||||
|
"protection",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/protection.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de gestion des statuts */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet qui applique un statut
|
||||||
|
* @param {string} status - Nom du statut
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createStatusEffect(status, source = "Effet") {
|
||||||
|
return {
|
||||||
|
name: `${source}: ${status}`,
|
||||||
|
icon: `systems/fvtt-mournblade-cyd-2-0/assets/icons/status_${status.toLowerCase()}.webp`,
|
||||||
|
description: `Applique le statut ${status}`,
|
||||||
|
changes: [],
|
||||||
|
statuses: [status],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: null,
|
||||||
|
tint: "",
|
||||||
|
transfer: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité bleue
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createAdversiteBleueEffect(value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Bleue: +${value}`,
|
||||||
|
"adversite.bleue",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_bleue.webp",
|
||||||
|
duration: { type: "rounds", value: 1 },
|
||||||
|
statuses: ["adversite-bleue"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité rouge
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createAdversiteRougeEffect(value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Rouge: +${value}`,
|
||||||
|
"adversite.rouge",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_rouge.webp",
|
||||||
|
duration: { type: "rounds", value: 1 },
|
||||||
|
statuses: ["adversite-rouge"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité noire
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createAdversiteNoireEffect(value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Noire: +${value}`,
|
||||||
|
"adversite.noire",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_noire.webp",
|
||||||
|
duration: { type: "rounds", value: 1 },
|
||||||
|
statuses: ["adversite-noire"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes pour les Runes */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de Rune prononcée
|
||||||
|
* @param {Object} rune - Données de la rune
|
||||||
|
* @param {number} pointsAme - Points de pouvoir dépensés
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createRunePrononceeEffect(rune, pointsAme) {
|
||||||
|
if (!rune || !rune.name || pointsAme == null) return null;
|
||||||
|
|
||||||
|
// Utiliser une icône par défaut si l'image de la rune est l'image par défaut
|
||||||
|
const icon = rune.img?.includes('/blank.png') || !rune.img
|
||||||
|
? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp"
|
||||||
|
: rune.img;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: `Rune: ${rune.name} (Prononcée)`,
|
||||||
|
icon: icon,
|
||||||
|
description: rune.system?.description || "",
|
||||||
|
changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
|
||||||
|
disabled: false,
|
||||||
|
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) },
|
||||||
|
origin: rune.uuid || null,
|
||||||
|
tint: "#00ff00",
|
||||||
|
transfer: true,
|
||||||
|
flags: {
|
||||||
|
"mournblade-cyd2": {
|
||||||
|
runeId: rune._id,
|
||||||
|
runeType: "prononcee",
|
||||||
|
pointsAme: pointsAme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de Rune tracée
|
||||||
|
* @param {Object} rune - Données de la rune
|
||||||
|
* @param {number} pointsAme - Points de pouvoir dépensés
|
||||||
|
* @returns {Object|null} - Données de l'effet ou null
|
||||||
|
*/
|
||||||
|
static createRuneTraceeEffect(rune, pointsAme) {
|
||||||
|
if (!rune || !rune.name || pointsAme == null) return null;
|
||||||
|
|
||||||
|
// Utiliser une icône par défaut si l'image de la rune est l'image par défaut
|
||||||
|
const icon = rune.img?.includes('/blank.png') || !rune.img
|
||||||
|
? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp"
|
||||||
|
: rune.img;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: `Rune: ${rune.name} (Tracée)`,
|
||||||
|
icon: icon,
|
||||||
|
description: rune.system?.description || "",
|
||||||
|
changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
|
||||||
|
disabled: false,
|
||||||
|
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) * 2 },
|
||||||
|
origin: rune.uuid || null,
|
||||||
|
tint: "#0000ff",
|
||||||
|
transfer: true,
|
||||||
|
flags: {
|
||||||
|
"mournblade-cyd2": {
|
||||||
|
runeId: rune._id,
|
||||||
|
runeType: "tracee",
|
||||||
|
pointsAme: pointsAme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation automatique
|
||||||
|
Hooks.once("init", () => {
|
||||||
|
MournbladeCYD2Effects.init();
|
||||||
|
});
|
||||||
@@ -16,6 +16,7 @@ import { MournbladeCYD2Item } from "./mournblade-cyd2-item.js";
|
|||||||
import { MournbladeCYD2Automation } from "./mournblade-cyd2-automation.js";
|
import { MournbladeCYD2Automation } from "./mournblade-cyd2-automation.js";
|
||||||
import { MournbladeCYD2TokenHud } from "./mournblade-cyd2-hud.js";
|
import { MournbladeCYD2TokenHud } from "./mournblade-cyd2-hud.js";
|
||||||
import { MOURNBLADECYD2_CONFIG } from "./mournblade-cyd2-config.js";
|
import { MOURNBLADECYD2_CONFIG } from "./mournblade-cyd2-config.js";
|
||||||
|
import { MournbladeCYD2Effects } from "./mournblade-cyd2-effects.js";
|
||||||
|
|
||||||
// Import DataModels
|
// Import DataModels
|
||||||
import * as models from "./models/index.mjs";
|
import * as models from "./models/index.mjs";
|
||||||
@@ -69,11 +70,15 @@ Hooks.once("init", async function () {
|
|||||||
runeeffect: models.RuneEffectDataModel,
|
runeeffect: models.RuneEffectDataModel,
|
||||||
tendance: models.TendanceDataModel,
|
tendance: models.TendanceDataModel,
|
||||||
traitchaotique: models.TraitChaotiqueDataModel,
|
traitchaotique: models.TraitChaotiqueDataModel,
|
||||||
traitespece: models.TraitEspeceDataModel
|
traitespece: models.TraitEspeceDataModel,
|
||||||
|
traitdemoniaque: models.TraitDemoniaqueDataModel,
|
||||||
|
pouvoirselementaire: models.PouvoirElementaireDataModel,
|
||||||
|
capaciteautomata: models.CapaciteAutomataDataModel
|
||||||
}
|
}
|
||||||
game.system.mournbladecyd2 = {
|
game.system.mournbladecyd2 = {
|
||||||
MournbladeCYD2Utility,
|
MournbladeCYD2Utility,
|
||||||
MournbladeCYD2Automation,
|
MournbladeCYD2Automation,
|
||||||
|
MournbladeCYD2Effects,
|
||||||
config: MOURNBLADECYD2_CONFIG
|
config: MOURNBLADECYD2_CONFIG
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +104,9 @@ Hooks.once("init", async function () {
|
|||||||
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TendanceSheet, { types: ["tendance"], makeDefault: true });
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TendanceSheet, { types: ["tendance"], makeDefault: true });
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TraitChaotiqueSheet, { types: ["traitchaotique"], makeDefault: true });
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TraitChaotiqueSheet, { types: ["traitchaotique"], makeDefault: true });
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TraitEspeceSheet, { types: ["traitespece"], makeDefault: true });
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TraitEspeceSheet, { types: ["traitespece"], makeDefault: true });
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2TraitDemoniaqueSheet, { types: ["traitdemoniaque"], makeDefault: true });
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2PouvoirElementaireSheet, { types: ["pouvoirselementaire"], makeDefault: true });
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-mournblade-cyd-2-0", sheets.MournbladeCYD2CapaciteAutomataSheet, { types: ["capaciteautomata"], makeDefault: true });
|
||||||
|
|
||||||
MournbladeCYD2Utility.init()
|
MournbladeCYD2Utility.init()
|
||||||
MournbladeCYD2Automation.init()
|
MournbladeCYD2Automation.init()
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export class MournbladeCYD2Utility {
|
|||||||
Handlebars.registerHelper('mul', function (a, b) {
|
Handlebars.registerHelper('mul', function (a, b) {
|
||||||
return parseInt(a) * parseInt(b);
|
return parseInt(a) * parseInt(b);
|
||||||
})
|
})
|
||||||
|
Handlebars.registerHelper('subtract', function (a, b) {
|
||||||
|
return parseInt(a) - parseInt(b);
|
||||||
|
})
|
||||||
Handlebars.registerHelper('select', function(value, options) {
|
Handlebars.registerHelper('select', function(value, options) {
|
||||||
const html = options.fn(this);
|
const html = options.fn(this);
|
||||||
const escaped = String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const escaped = String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
@@ -195,6 +198,8 @@ export class MournbladeCYD2Utility {
|
|||||||
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs',
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs',
|
||||||
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-prix.hbs',
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-prix.hbs',
|
||||||
'systems/fvtt-mournblade-cyd-2-0/templates/partial-automation.hbs',
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-automation.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs',
|
||||||
'systems/fvtt-mournblade-cyd-2-0/templates/hud-adversites.hbs',
|
'systems/fvtt-mournblade-cyd-2-0/templates/hud-adversites.hbs',
|
||||||
]
|
]
|
||||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||||
|
|||||||
@@ -2718,6 +2718,13 @@ li {
|
|||||||
background: rgba(10, 20, 55, 0.6);
|
background: rgba(10, 20, 55, 0.6);
|
||||||
color: #a0c0ff;
|
color: #a0c0ff;
|
||||||
}
|
}
|
||||||
|
.fvtt-mournblade-cyd-2-0.actor .sheet-header .header-stat-cards .stat-card.card-combat {
|
||||||
|
border-top-color: #805010;
|
||||||
|
}
|
||||||
|
.fvtt-mournblade-cyd-2-0.actor .sheet-header .header-stat-cards .stat-card.card-combat .stat-card-title {
|
||||||
|
background: rgba(50, 35, 10, 0.6);
|
||||||
|
color: #ffe8a0;
|
||||||
|
}
|
||||||
.fvtt-mournblade-cyd-2-0.actor .sheet-header .header-stat-cards .stat-card .stat-card-title {
|
.fvtt-mournblade-cyd-2-0.actor .sheet-header .header-stat-cards .stat-card .stat-card-title {
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|||||||
+4
-1
@@ -51,7 +51,10 @@
|
|||||||
"runeeffect": { "htmlFields": ["description"] },
|
"runeeffect": { "htmlFields": ["description"] },
|
||||||
"tendance": { "htmlFields": ["description"] },
|
"tendance": { "htmlFields": ["description"] },
|
||||||
"traitchaotique": { "htmlFields": ["description"] },
|
"traitchaotique": { "htmlFields": ["description"] },
|
||||||
"traitespece": { "htmlFields": ["description"] }
|
"traitespece": { "htmlFields": ["description"] },
|
||||||
|
"traitdemoniaque": { "htmlFields": ["description"] },
|
||||||
|
"pouvoirselementaire": { "htmlFields": ["description"] },
|
||||||
|
"capaciteautomata": { "htmlFields": ["description"] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
<a class="item" data-tab="dons">Dons & Pactes</a>
|
<a class="item" data-tab="dons">Dons & Pactes</a>
|
||||||
<a class="item" data-tab="combat">Combat</a>
|
<a class="item" data-tab="combat">Combat</a>
|
||||||
<a class="item" data-tab="equipement">{{localize "MNBL.equipment"}}</a>
|
<a class="item" data-tab="equipement">{{localize "MNBL.equipment"}}</a>
|
||||||
|
<a class="item" data-tab="effects">Effets</a>
|
||||||
<a class="item" data-tab="biodata">Bio&Notes</a>
|
<a class="item" data-tab="biodata">Bio&Notes</a>
|
||||||
</nav>
|
</nav>
|
||||||
<hr>
|
<hr>
|
||||||
@@ -690,6 +691,10 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{!-- Biography Tab --}}
|
{{!-- Biography Tab --}}
|
||||||
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="detail-label">{{localize "MNBL.soulcost"}} :</span>
|
<span class="detail-label">{{localize "MNBL.soulcost"}} :</span>
|
||||||
<span class="detail-value">{{runeAmeCout}} pts</span>
|
<span class="detail-value">{{rune.system.coutAme}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if bonusRoll}}
|
{{#if bonusRoll}}
|
||||||
|
|||||||
@@ -35,24 +35,49 @@
|
|||||||
<label>Niveaux</label>
|
<label>Niveaux</label>
|
||||||
<input type="text" name="system.sante.nbcombativite" value="{{system.sante.nbcombativite}}" data-dtype="Number" />
|
<input type="text" name="system.sante.nbcombativite" value="{{system.sante.nbcombativite}}" data-dtype="Number" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="stat-field">
|
||||||
|
<label>Seuil de Vigueur</label>
|
||||||
|
<input type="text" name="system.sante.vigueur" value="{{system.sante.vigueur}}" data-dtype="Number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- ÂME --}}
|
||||||
|
<div class="stat-card card-ame">
|
||||||
|
<div class="stat-card-title">✦ Âme <span class="stat-sub">(Seuil {{system.ame.seuilpouvoir}})</span></div>
|
||||||
|
<div class="stat-card-content">
|
||||||
|
<div class="stat-field">
|
||||||
|
<label>État</label>
|
||||||
|
<select name="system.ame.etat">
|
||||||
|
{{selectOptions ameList selected=system.ame.etat valueAttr="value" nameAttr="value" labelAttr="label"}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="stat-field">
|
||||||
|
<label>Niveaux</label>
|
||||||
|
<input type="text" name="system.ame.nbame" value="{{system.ame.nbame}}" data-dtype="Number" />
|
||||||
|
</div>
|
||||||
|
<div class="stat-field">
|
||||||
|
<label>Seuil de Pouvoir</label>
|
||||||
|
<input type="text" name="system.ame.seuilpouvoir" value="{{system.ame.seuilpouvoir}}" data-dtype="Number" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- COMBAT --}}
|
{{!-- COMBAT --}}
|
||||||
<div class="stat-card card-ame">
|
<div class="stat-card card-combat">
|
||||||
<div class="stat-card-title">⚡ Combat</div>
|
<div class="stat-card-title">⚡ Combat</div>
|
||||||
<div class="stat-card-content">
|
<div class="stat-card-content">
|
||||||
<div class="stat-field">
|
<div class="stat-field">
|
||||||
<label>Init.</label>
|
<label>Init.</label>
|
||||||
<span class="stat-derived-value">{{initiative}}</span>
|
<input type="text" name="system.combat.inittotal" value="{{system.combat.inittotal}}" data-dtype="Number" placeholder="{{initiative}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-field">
|
<div class="stat-field">
|
||||||
<label>Déf.</label>
|
<label>Déf.</label>
|
||||||
<span class="stat-derived-value">{{combat.defenseTotal}}</span>
|
<input type="text" name="system.combat.defensetotal" value="{{system.combat.defensetotal}}" data-dtype="Number" placeholder="{{combat.defenseTotal}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-field">
|
<div class="stat-field">
|
||||||
<label>Prot.</label>
|
<label>Prot.</label>
|
||||||
<span class="stat-derived-value">{{protectionTotal}}</span>
|
<input type="text" name="system.combat.protectiontotal" value="{{system.combat.protectiontotal}}" data-dtype="Number" placeholder="{{protectionTotal}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,6 +92,7 @@
|
|||||||
<a class="item" data-tab="competences">Compétences</a>
|
<a class="item" data-tab="competences">Compétences</a>
|
||||||
<a class="item" data-tab="talents">Talents</a>
|
<a class="item" data-tab="talents">Talents</a>
|
||||||
<a class="item" data-tab="armes">Armes</a>
|
<a class="item" data-tab="armes">Armes</a>
|
||||||
|
<a class="item" data-tab="effects">Effets</a>
|
||||||
<a class="item" data-tab="biodata">Bio&Notes</a>
|
<a class="item" data-tab="biodata">Bio&Notes</a>
|
||||||
</nav>
|
</nav>
|
||||||
<hr>
|
<hr>
|
||||||
@@ -161,6 +187,60 @@
|
|||||||
<span class="adversite-total-value">−{{adversiteTotal}}</span>
|
<span class="adversite-total-value">−{{adversiteTotal}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Traits Chaotiques</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createItem" data-type="traitchaotique" title="Ajouter un Trait Chaotique">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{#each traitsChaotiques as |trait key|}}
|
||||||
|
<li class="item flexrow" data-item-id="{{trait._id}}" data-item-type="traitchaotique">
|
||||||
|
<img class="item-name-img" src="{{trait.img}}" />
|
||||||
|
<span class="item-name-label competence-name">{{trait.name}}</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editItem" title="Éditer"><i class="fas fa-edit"></i></a>
|
||||||
|
<a class="item-control" data-action="deleteItem" title="Supprimer"><i class="fas fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Traits d'Espèce</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createItem" data-type="traitespece" title="Ajouter un Trait d'Espèce">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{#each traitsEspeces as |trait key|}}
|
||||||
|
<li class="item flexrow" data-item-id="{{trait._id}}" data-item-type="traitespece">
|
||||||
|
<img class="item-name-img" src="{{trait.img}}" />
|
||||||
|
<span class="item-name-label competence-name">{{trait.name}}</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editItem" title="Éditer"><i class="fas fa-edit"></i></a>
|
||||||
|
<a class="item-control" data-action="deleteItem" title="Supprimer"><i class="fas fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -343,6 +423,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{!-- Bio&Notes Tab --}}
|
{{!-- Bio&Notes Tab --}}
|
||||||
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
||||||
<div class="sheet-box color-bg-archetype">
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<section>
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs}} {{
|
||||||
|
> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs}} {{!-- Sheet
|
||||||
|
Body --}}
|
||||||
|
|
||||||
|
<section class="sheet-body">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label">Bonus/Malus </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<input type="text" class="padd-right status-small-label color-class-common" name="system.bonusmalus"
|
||||||
|
value="{{system.bonusmalus}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Sacrifices</h3>
|
<h3>Sacrifices et Tendances liées</h3>
|
||||||
|
|
||||||
<div class="small-editor item-text-long-line">
|
<div class="small-editor item-text-long-line">
|
||||||
{{editor sacrifice target="system.sacrifice" button=true owner=owner
|
{{editor sacrifice target="system.sacrifice" button=true owner=owner
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<section>
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs}} {{
|
||||||
|
> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs}} {{!-- Sheet
|
||||||
|
Body --}}
|
||||||
|
|
||||||
|
<section class="sheet-body">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label">Bonus/Malus </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<input type="text" class="padd-right status-small-label color-class-common" name="system.bonusmalus"
|
||||||
|
value="{{system.bonusmalus}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
@@ -62,11 +62,11 @@
|
|||||||
>Coût en Pouvoir :
|
>Coût en Pouvoir :
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="text"
|
||||||
class="padd-right status-small-label color-class-common item-field-label-long2"
|
class="padd-right status-small-label color-class-common item-field-label-long2"
|
||||||
name="system.coutAme"
|
name="system.coutAme"
|
||||||
value="{{system.coutAme}}"
|
value="{{system.coutAme}}"
|
||||||
data-dtype="Number"
|
data-dtype="String"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -54,5 +54,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
{{#if item.effects.length}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
@@ -23,6 +23,11 @@
|
|||||||
selected=system.allegeance}}
|
selected=system.allegeance}}
|
||||||
</select>
|
</select>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label item-field-label-long">Don lié : </label>
|
||||||
|
<input type="text" class="status-small-label color-class-common item-field-label-long"
|
||||||
|
name="system.donlie" value="{{system.donlie}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -6,7 +6,17 @@
|
|||||||
<section class="sheet-body">
|
<section class="sheet-body">
|
||||||
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
<div class="tab details" data-group="primary" data-tab="details"></div>
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label">Bonus/Malus </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<input type="text" class="padd-right status-small-label color-class-common" name="system.bonusmalus"
|
||||||
|
value="{{system.bonusmalus}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<section>
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs}} {{
|
||||||
|
> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs}} {{!-- Sheet
|
||||||
|
Body --}}
|
||||||
|
|
||||||
|
<section class="sheet-body">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label">Bonus/Malus </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<input type="text" class="padd-right status-small-label color-class-common" name="system.bonusmalus"
|
||||||
|
value="{{system.bonusmalus}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
@@ -6,6 +6,16 @@
|
|||||||
<section class="sheet-body">
|
<section class="sheet-body">
|
||||||
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
<div class="tab details" data-group="primary" data-tab="details"></div>
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label">Bonus/Malus </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<input type="text" class="padd-right status-small-label color-class-common" name="system.bonusmalus"
|
||||||
|
value="{{system.bonusmalus}}" data-dtype="String" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
{{!-- Partial pour l'affichage des ActiveEffects --}}
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
|
||||||
|
{{!-- En-tête --}}
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Effets Actifs</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createEffect" title="Ajouter un effet">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{{!-- Affiche un message si aucun effet --}}
|
||||||
|
{{#if (not actor.effects.length)}}
|
||||||
|
<li class="item flexrow">
|
||||||
|
<span class="item-name-label competence-name" style="color: #888; font-style: italic;">Aucun effet actif</span>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Liste des effets --}}
|
||||||
|
{{#each actor.effects as |effect|}}
|
||||||
|
<li class="item flexrow" data-effect-id="{{effect.id}}" {{#if effect.disabled}}style="opacity: 0.6;"{{/if}}>
|
||||||
|
{{!-- Icône de l'effet --}}
|
||||||
|
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp'" />
|
||||||
|
|
||||||
|
{{!-- Nom et description de l'effet --}}
|
||||||
|
<div class="flexcol item-name-label">
|
||||||
|
<span class="item-name-label competence-name">
|
||||||
|
{{effect.name}}
|
||||||
|
{{#if effect.disabled}}<i class="fas fa-ban" style="color: #ff5555; margin-left: 5px;" title="Désactivé"></i>{{/if}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{!-- Affichage compact des modifications --}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<span class="predilection-text" style="font-size: 0.85em; color: #aaa;">
|
||||||
|
{{#each effect.changes as |change index|}}
|
||||||
|
{{#if (eq change.mode 0)}}(+{{/if}}{{#if (eq change.mode 1)}}(*{{/if}}{{#if (eq change.mode 2)}}={{/if}}{{change.value}}{{#if (eq change.mode 0)}}){{/if}}{{#if (eq change.mode 1)}}){{/if}}
|
||||||
|
{{#unless (eq index (subtract effect.changes.length 1))}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
{{#each effect.statuses as |status|}}
|
||||||
|
{{#if (and (ne status "") (not (eq index 0)))}}, {{/if}}
|
||||||
|
<i class="fas fa-exclamation-triangle" style="color: #ffaa00;" title="{{status}}"></i>
|
||||||
|
{{/each}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage de la durée --}}
|
||||||
|
{{#if effect.duration.units}}
|
||||||
|
<span class="item-field-label-short" style="font-size: 0.85em;">
|
||||||
|
{{#if (eq effect.duration.units "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "turns")}}🎭{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "seconds")}}⏱️{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "combat")}}⚔️{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "scene")}}📜{{/if}}
|
||||||
|
{{effect.duration.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Contrôles --}}
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editEffect" data-effect-id="{{effect.id}}" title="Éditer">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="toggleEffect" data-effect-id="{{effect.id}}" title="{{#if effect.disabled}}Activer{{else}}Désactiver{{/if}}">
|
||||||
|
<i class="fas fa-{{#if effect.disabled}}check{{else}}times{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="deleteEffect" data-effect-id="{{effect.id}}" title="Supprimer">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage détaillé des effets actifs --}}
|
||||||
|
{{#if actor.effects.length}}
|
||||||
|
<div class="sheet-box color-bg-archetype effect-summary">
|
||||||
|
<h4 class="section-title">Résumé des modifications</h4>
|
||||||
|
<div class="effect-modifications">
|
||||||
|
{{#each actor.effects as |effect|}}
|
||||||
|
{{#if (not effect.disabled)}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<div class="effect-category">
|
||||||
|
<strong>{{effect.name}}:</strong>
|
||||||
|
{{#each effect.changes as |change|}}
|
||||||
|
<span class="effect-modification">
|
||||||
|
{{change.key}}:
|
||||||
|
{{#if (eq change.mode 0)}}+{{/if}}
|
||||||
|
{{#if (eq change.mode 1)}}*{{/if}}
|
||||||
|
{{#if (eq change.mode 2)}}={{/if}}
|
||||||
|
{{change.value}}
|
||||||
|
{{#if (eq change.mode 0)}}{{/if}}
|
||||||
|
{{#if (eq change.mode 1)}}{{/if}}
|
||||||
|
</span>{{#unless @last}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
@@ -13,13 +13,13 @@
|
|||||||
<li class="automation-item item flexrow" data-automation-field="eventtype" data-automation-index="{{key}}">
|
<li class="automation-item item flexrow" data-automation-field="eventtype" data-automation-index="{{key}}">
|
||||||
<label class="generic-label item-field-label-medium">Evènement</label>
|
<label class="generic-label item-field-label-medium">Evènement</label>
|
||||||
<select class="item-field-label-long automation-edit-field" type="text" data-automation-index="{{key}}" data-automation-field="eventtype"
|
<select class="item-field-label-long automation-edit-field" type="text" data-automation-index="{{key}}" data-automation-field="eventtype"
|
||||||
value="{{automation.eventtype}}" data-dtype="String">
|
name="system.automations.{{key}}.eventtype" value="{{automation.eventtype}}" data-dtype="String">
|
||||||
{{selectOptions @root.config.optionsAutomationEvent selected=automation.eventtype valueAttr="key" nameAttr="key" labelAttr="label"}}
|
{{selectOptions @root.config.optionsAutomationEvent selected=automation.eventtype valueAttr="key" nameAttr="key" labelAttr="label"}}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label class="generic-label item-field-label-medium"> </label>
|
<label class="generic-label item-field-label-medium"> </label>
|
||||||
|
|
||||||
<a class="item-control item-field-label-medium delete-automation" title="Supprimer" data-automation-index="{{key}}"><i
|
<a class="item-control item-field-label-medium delete-automation" title="Supprimer" data-automation-index="{{key}}" data-action="deleteAutomation"><i
|
||||||
class="fas fa-trash"></i></a>
|
class="fas fa-trash"></i></a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
{{#if (eq automation.eventtype "on-drop")}}
|
{{#if (eq automation.eventtype "on-drop")}}
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Compétence/Attribut</label>
|
<label class="generic-label item-field-label-long">Compétence/Attribut</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="competence" value="{{automation.competence}}" data-dtype="String" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="competence" name="system.automations.{{key}}.competence" value="{{automation.competence}}" data-dtype="String" />
|
||||||
</li>
|
</li>
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Valeur minimum</label>
|
<label class="generic-label item-field-label-long">Valeur minimum</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="minLevel" value="{{automation.minLevel}}" data-dtype="Number" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="minLevel" name="system.automations.{{key}}.minLevel" value="{{automation.minLevel}}" data-dtype="Number" />
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
@@ -39,28 +39,28 @@
|
|||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Désignation</label>
|
<label class="generic-label item-field-label-long">Désignation</label>
|
||||||
<select class="item-field-label-long automation-edit-field" type="text" data-automation-index="{{key}}" data-automation-field="bonusname"
|
<select class="item-field-label-long automation-edit-field" type="text" data-automation-index="{{key}}" data-automation-field="bonusname"
|
||||||
value="{{automation.bonusname}}" data-dtype="String">
|
name="system.automations.{{key}}.bonusname" value="{{automation.bonusname}}" data-dtype="String">
|
||||||
{{selectOptions @root.config.optionsBonusPermanent selected=automation.bonusname valueAttr="key" nameAttr="key" labelAttr="label"}}
|
{{selectOptions @root.config.optionsBonusPermanent selected=automation.bonusname valueAttr="key" nameAttr="key" labelAttr="label"}}
|
||||||
</select>
|
</select>
|
||||||
</li>
|
</li>
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Bonus</label>
|
<label class="generic-label item-field-label-long">Bonus</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="bonus" value="{{automation.bonus}}" data-dtype="Number" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="bonus" name="system.automations.{{key}}.bonus" value="{{automation.bonus}}" data-dtype="Number" />
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq automation.eventtype "prepare-roll")}}
|
{{#if (eq automation.eventtype "prepare-roll")}}
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Compétence/Attribut</label>
|
<label class="generic-label item-field-label-long">Compétence/Attribut</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="competence" value="{{automation.competence}}" data-dtype="String" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="competence" name="system.automations.{{key}}.competence" value="{{automation.competence}}" data-dtype="String" />
|
||||||
</li>
|
</li>
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Bonus</label>
|
<label class="generic-label item-field-label-long">Bonus</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="bonus" value="{{automation.bonus}}" data-dtype="Number" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="bonus" name="system.automations.{{key}}.bonus" value="{{automation.bonus}}" data-dtype="Number" />
|
||||||
</li>
|
</li>
|
||||||
<li class="automation-item item flexrow">
|
<li class="automation-item item flexrow">
|
||||||
<label class="generic-label item-field-label-long">Coût en BA</label>
|
<label class="generic-label item-field-label-long">Coût en BA</label>
|
||||||
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="baCost" value="{{automation.baCost}}" data-dtype="Number" />
|
<input type="text" class="item-field-label-medium automation-edit-field" data-automation-index="{{key}}" data-automation-field="baCost" name="system.automations.{{key}}.baCost" value="{{automation.baCost}}" data-dtype="Number" />
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
@@ -69,6 +69,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<li class="flexrow item">
|
<li class="flexrow item">
|
||||||
<button id="add-automation" class="chat-card-button">Ajouter une automatisation</button>
|
<button id="add-automation" class="chat-card-button" data-action="addAutomation">Ajouter une automatisation</button>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{{!-- Partial pour l'affichage des effets sur les items --}}
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
|
||||||
|
{{!-- En-tête --}}
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Effets Actifs</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createEffect" title="Ajouter un effet">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{{!-- Affiche un message si aucun effet --}}
|
||||||
|
{{#if (not item.effects.length)}}
|
||||||
|
<li class="item flexrow">
|
||||||
|
<span class="item-name-label competence-name" style="color: #888; font-style: italic;">Aucun effet sur cet item</span>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Liste des effets --}}
|
||||||
|
{{#each item.effects as |effect|}}
|
||||||
|
<li class="item flexrow" data-effect-id="{{effect.id}}" {{#if effect.disabled}}style="opacity: 0.6;"{{/if}}>
|
||||||
|
{{!-- Icône de l'effet --}}
|
||||||
|
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp'" />
|
||||||
|
|
||||||
|
{{!-- Nom et description de l'effet --}}
|
||||||
|
<div class="flexcol item-name-label">
|
||||||
|
<span class="item-name-label competence-name">
|
||||||
|
{{effect.name}}
|
||||||
|
{{#if effect.disabled}}<i class="fas fa-ban" style="color: #ff5555; margin-left: 5px;" title="Désactivé"></i>{{/if}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{!-- Affichage compact des modifications --}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<span class="predilection-text" style="font-size: 0.85em; color: #aaa;">
|
||||||
|
{{#each effect.changes as |change index|}}
|
||||||
|
{{change.key}}: {{change.value}}
|
||||||
|
{{#unless (eq index (subtract effect.changes.length 1))}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage de la durée --}}
|
||||||
|
{{#if effect.duration.units}}
|
||||||
|
<span class="item-field-label-short" style="font-size: 0.85em;">
|
||||||
|
{{#if (eq effect.duration.units "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "turns")}}🎭{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "seconds")}}⏱️{{/if}}
|
||||||
|
{{#if (eq effect.duration.units "combat")}}⚔️{{/if}}
|
||||||
|
{{effect.duration.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Contrôles --}}
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editEffect" data-effect-id="{{effect.id}}" title="Éditer">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="toggleEffect" data-effect-id="{{effect.id}}" title="{{#if effect.disabled}}Activer{{else}}Désactiver{{/if}}">
|
||||||
|
<i class="fas fa-{{#if effect.disabled}}check{{else}}times{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="deleteEffect" data-effect-id="{{effect.id}}" title="Supprimer">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
{{!-- Sheet Tab Navigation --}}
|
{{!-- Sheet Tab Navigation --}}
|
||||||
<hr />
|
<hr />
|
||||||
<nav class="sheet-tabs tabs" data-group="primary">
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
<a class="item" data-tab="description">Description</a>
|
<a class="item" data-tab="description" aria-label="{{localize 'MNBL.description'}}">{{localize "MNBL.description"}}</a>
|
||||||
<a class="item" data-tab="details">Details</a>
|
<a class="item" data-tab="details" aria-label="{{localize 'MNBL.details'}}">{{localize "MNBL.details"}}</a>
|
||||||
|
<a class="item" data-tab="effects" aria-label="{{localize 'MOURNBLADECYD2.EFFECT.activeEffects'}}">{{localize "MOURNBLADECYD2.EFFECT.activeEffects"}}</a>
|
||||||
</nav>
|
</nav>
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
@@ -0,0 +1,313 @@
|
|||||||
|
/**
|
||||||
|
* Script de test pour vérifier que tous les partials Handlebars sont correctement préchargés
|
||||||
|
* Exécuter avec : node test-templates.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Fonction pour simuler le chargement des templates
|
||||||
|
function loadTemplates(templatePaths) {
|
||||||
|
const loaded = [];
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
for (const templatePath of templatePaths) {
|
||||||
|
// Remplacer le préfixe systems/ par le chemin réel
|
||||||
|
const realPath = templatePath.replace('systems/fvtt-mournblade-cyd-2-0/templates/', 'templates/');
|
||||||
|
const fullPath = path.join(__dirname, realPath);
|
||||||
|
|
||||||
|
// Vérifier si le fichier existe
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
loaded.push(templatePath);
|
||||||
|
} else {
|
||||||
|
errors.push(`Template introuvable: ${templatePath} (cherché: ${fullPath})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loaded, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour extraire les partials utilisés dans les templates
|
||||||
|
function extractPartialsFromTemplate(content, basePath) {
|
||||||
|
const partialRegex = /{{\s*>[\s\S]*?systems\/fvtt-mournblade-cyd-2-0\/templates\/([^\s]+\.hbs)\s*}}/g;
|
||||||
|
const partials = new Set();
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = partialRegex.exec(content)) !== null) {
|
||||||
|
partials.add(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(partials);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour scanner tous les templates et trouver les partials utilisés
|
||||||
|
function scanAllTemplates() {
|
||||||
|
const templatesDir = path.join(__dirname, 'templates');
|
||||||
|
const files = fs.readdirSync(templatesDir).filter(f => f.endsWith('.hbs'));
|
||||||
|
const allPartials = new Set();
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(templatesDir, file);
|
||||||
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const partials = extractPartialsFromTemplate(content);
|
||||||
|
partials.forEach(p => allPartials.add(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(allPartials);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liste actuelle des templates préchargés
|
||||||
|
const currentPreloadedTemplates = [
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/editor-notes-gm.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-prix.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-automation.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs',
|
||||||
|
'systems/fvtt-mournblade-cyd-2-0/templates/hud-adversites.hbs',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Exécuter le test
|
||||||
|
console.log('=== Test de chargement des templates Handlebars ===\n');
|
||||||
|
|
||||||
|
// 1. Vérifier que tous les templates préchargés existent
|
||||||
|
console.log('1. Vérification des templates préchargés :');
|
||||||
|
const { loaded, errors } = loadTemplates(currentPreloadedTemplates);
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.log(' ❌ Erreurs trouvées :');
|
||||||
|
errors.forEach(err => console.log(` - ${err}`));
|
||||||
|
} else {
|
||||||
|
console.log(' ✅ Tous les templates préchargés existent');
|
||||||
|
loaded.forEach(t => console.log(` ✓ ${t}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Scanner tous les templates pour trouver les partials utilisés
|
||||||
|
console.log('\n2. Scan des partials utilisés dans les templates :');
|
||||||
|
const usedPartials = scanAllTemplates();
|
||||||
|
usedPartials.forEach(p => console.log(` - ${p}`));
|
||||||
|
|
||||||
|
// 3. Vérifier que tous les partials utilisés sont préchargés
|
||||||
|
console.log('\n3. Vérification que tous les partials utilisés sont préchargés :');
|
||||||
|
const preloadedPartials = currentPreloadedTemplates.map(p => path.basename(p));
|
||||||
|
const missingPartials = usedPartials.filter(p => !preloadedPartials.includes(p));
|
||||||
|
|
||||||
|
if (missingPartials.length > 0) {
|
||||||
|
console.log(' ❌ Partials manquants dans le préchargement :');
|
||||||
|
missingPartials.forEach(p => console.log(` - ${p}`));
|
||||||
|
} else {
|
||||||
|
console.log(' ✅ Tous les partials utilisés sont préchargés');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Vérification JSON
|
||||||
|
console.log('\n4. Vérification du fichier lang/fr.json :');
|
||||||
|
try {
|
||||||
|
const frJson = require('./lang/fr.json');
|
||||||
|
console.log(' ✅ Le fichier lang/fr.json est valide');
|
||||||
|
} catch (err) {
|
||||||
|
console.log(' ❌ Erreur dans lang/fr.json :', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Vérification des appels API dépréciés
|
||||||
|
console.log('\n5. Vérification des appels API dépréciés (ActiveEffectDialog) :');
|
||||||
|
const deprecatedPattern = /foundry\.applications\.api\.ActiveEffectDialog/g;
|
||||||
|
const jsFiles = [
|
||||||
|
'modules/applications/sheets/base-actor-sheet.mjs',
|
||||||
|
'modules/applications/sheets/base-item-sheet.mjs'
|
||||||
|
];
|
||||||
|
|
||||||
|
let deprecatedFound = false;
|
||||||
|
for (const file of jsFiles) {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, file), 'utf8');
|
||||||
|
if (deprecatedPattern.test(content)) {
|
||||||
|
console.log(` ❌ Fichier ${file} contient ActiveEffectDialog (déprécié)`);
|
||||||
|
deprecatedFound = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Fichier introuvable, ignorer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deprecatedFound) {
|
||||||
|
console.log(' ✅ Aucun appel à ActiveEffectDialog (API dépréciée) trouvé');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Vérification des références à l'icône manquante effect.webp
|
||||||
|
console.log('\n6. Vérification des références à effect.webp (icône manquante) :');
|
||||||
|
const effectWebpPattern = /effect\.webp/g;
|
||||||
|
const allJsFiles = [
|
||||||
|
'modules/applications/sheets/base-actor-sheet.mjs',
|
||||||
|
'modules/applications/sheets/base-item-sheet.mjs',
|
||||||
|
'modules/mournblade-cyd2-effects.js'
|
||||||
|
];
|
||||||
|
const hbsFiles = [
|
||||||
|
'templates/partial-active-effects.hbs',
|
||||||
|
'templates/partial-item-effects.hbs'
|
||||||
|
];
|
||||||
|
|
||||||
|
let effectWebpFound = false;
|
||||||
|
[...allJsFiles, ...hbsFiles].forEach(file => {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, file), 'utf8');
|
||||||
|
if (effectWebpPattern.test(content)) {
|
||||||
|
console.log(` ❌ Fichier ${file} contient une référence à effect.webp`);
|
||||||
|
effectWebpFound = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Fichier introuvable, ignorer
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!effectWebpFound) {
|
||||||
|
console.log(' ✅ Aucune référence à effect.webp (icône manquante) trouvée');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Vérification de la propriété dépréciée duration.type
|
||||||
|
console.log('\n7. Vérification de l\'utilisation de duration.type (déprécié en v14) :');
|
||||||
|
const durationTypePattern = /duration\.type/g;
|
||||||
|
const allHbsFiles = [
|
||||||
|
'templates/partial-active-effects.hbs',
|
||||||
|
'templates/partial-item-effects.hbs',
|
||||||
|
'templates/item-arme-sheet.hbs'
|
||||||
|
];
|
||||||
|
|
||||||
|
let durationTypeFound = false;
|
||||||
|
allHbsFiles.forEach(file => {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, file), 'utf8');
|
||||||
|
if (durationTypePattern.test(content)) {
|
||||||
|
console.log(` ❌ Fichier ${file} utilise duration.type (déprécié)`);
|
||||||
|
durationTypeFound = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Fichier introuvable, ignorer
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!durationTypeFound) {
|
||||||
|
console.log(' ✅ Aucune utilisation de duration.type (utilise duration.units)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Vérification des clés i18n EFFECT dans lang/fr.json
|
||||||
|
console.log('\n8. Vérification des clés i18n EFFECT :');
|
||||||
|
const frJson = require('./lang/fr.json');
|
||||||
|
const effectKeys = Object.keys(frJson.EFFECT || {});
|
||||||
|
|
||||||
|
const requiredEffectKeys = [
|
||||||
|
'new', 'edit', 'delete', 'deleteConfirm', 'deleteConfirmText',
|
||||||
|
'deleteError', 'create', 'createError', 'applyError',
|
||||||
|
'applyItemError', 'selectActor', 'toggleError',
|
||||||
|
'name', 'icon', 'description', 'changes', 'addChange',
|
||||||
|
'duration', 'durationType', 'durationValue', 'disabled',
|
||||||
|
'transfer', 'noDuration', 'rounds', 'turns', 'seconds',
|
||||||
|
'combat', 'scene', 'attribute', 'value', 'mode',
|
||||||
|
'modeAdd', 'modeMultiply', 'modeOverride', 'modeUpgrade',
|
||||||
|
'modeDowngrade', 'activeEffects', 'noActiveEffects',
|
||||||
|
'effectSummary', 'toggleEffect'
|
||||||
|
];
|
||||||
|
|
||||||
|
let missingKeys = [];
|
||||||
|
for (const key of requiredEffectKeys) {
|
||||||
|
if (!effectKeys.includes(key)) {
|
||||||
|
missingKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingKeys.length === 0) {
|
||||||
|
console.log(` ✅ Toutes les clés EFFECT sont présentes (${effectKeys.length} clés)`);
|
||||||
|
} else {
|
||||||
|
console.log(` ❌ Clés EFFECT manquantes : ${missingKeys.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Vérification des clés i18n MNBL dans lang/fr.json
|
||||||
|
console.log('\n9. Vérification des clés i18n MNBL :');
|
||||||
|
const mnblKeys = Object.keys(frJson.MNBL || {});
|
||||||
|
|
||||||
|
const requiredMnblKeys = [
|
||||||
|
'all', 'allegiance', 'balance', 'beastslords', 'chaos',
|
||||||
|
'difficulty', 'duration', 'details', 'description',
|
||||||
|
'elementslords', 'equipment', 'examples', 'exercisedskills',
|
||||||
|
'highlanguage', 'initiateTalents', 'law', 'mode', 'mainattribute',
|
||||||
|
'none', 'aguerriTalents', 'prerequisitesAguerri', 'prerequisitesMaitre',
|
||||||
|
'prerequisites', 'maitreTalents', 'pronounced', 'pronouncedrune',
|
||||||
|
'pronouncerune', 'rune', 'soulcost', 'soulpoints', 'traced',
|
||||||
|
'tracedrune', 'tracerune'
|
||||||
|
];
|
||||||
|
|
||||||
|
let missingMnblKeys = [];
|
||||||
|
for (const key of requiredMnblKeys) {
|
||||||
|
if (!mnblKeys.includes(key)) {
|
||||||
|
missingMnblKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingMnblKeys.length === 0) {
|
||||||
|
console.log(` ✅ Toutes les clés MNBL sont présentes (${mnblKeys.length} clés)`);
|
||||||
|
} else {
|
||||||
|
console.log(` ❌ Clés MNBL manquantes : ${missingMnblKeys.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. Vérification de l'utilisation du helper subtract
|
||||||
|
console.log('\n10. Vérification de l\'utilisation du helper subtract :');
|
||||||
|
const subtractPattern = /subtract/g;
|
||||||
|
const subtractFiles = [
|
||||||
|
'templates/partial-active-effects.hbs',
|
||||||
|
'templates/partial-item-effects.hbs'
|
||||||
|
];
|
||||||
|
|
||||||
|
let subtractUsed = false;
|
||||||
|
let subtractRegistered = false;
|
||||||
|
|
||||||
|
// Vérifier que le helper est utilisé dans les templates
|
||||||
|
subtractFiles.forEach(file => {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, file), 'utf8');
|
||||||
|
if (subtractPattern.test(content)) {
|
||||||
|
subtractUsed = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Fichier introuvable, ignorer
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Vérifier que le helper est enregistré
|
||||||
|
try {
|
||||||
|
const utilityContent = fs.readFileSync(path.join(__dirname, 'modules/mournblade-cyd2-utility.js'), 'utf8');
|
||||||
|
if (/Handlebars\.registerHelper\('subtract'/g.test(utilityContent)) {
|
||||||
|
subtractRegistered = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Fichier introuvable, ignorer
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtractUsed && subtractRegistered) {
|
||||||
|
console.log(' ✅ Helper subtract est enregistré et utilisé');
|
||||||
|
} else if (subtractUsed && !subtractRegistered) {
|
||||||
|
console.log(' ❌ Helper subtract est utilisé mais pas enregistré');
|
||||||
|
} else if (!subtractUsed) {
|
||||||
|
console.log(' ✅ Helper subtract n\'est pas utilisé (ou plus utilisé)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Résumé
|
||||||
|
console.log('\n=== Résumé ===');
|
||||||
|
console.log(`Templates préchargés: ${loaded.length}`);
|
||||||
|
console.log(`Partials utilisés: ${usedPartials.length}`);
|
||||||
|
console.log(`Partials manquants: ${missingPartials.length}`);
|
||||||
|
console.log(`Fichier JSON valide: ${errors.length === 0 ? 'Oui' : 'Non'}`);
|
||||||
|
console.log(`API dépréciée utilisée: ${deprecatedFound ? 'Oui' : 'Non'}`);
|
||||||
|
console.log(`Référence à effect.webp: ${effectWebpFound ? 'Oui' : 'Non'}`);
|
||||||
|
console.log(`Utilisation de duration.type: ${durationTypeFound ? 'Oui' : 'Non'}`);
|
||||||
|
console.log(`Clés i18n EFFECT: ${missingKeys.length === 0 ? 'Complet' : 'Manquantes'}`);
|
||||||
|
console.log(`Clés i18n MNBL: ${missingMnblKeys.length === 0 ? 'Complet' : 'Manquantes'}`);
|
||||||
|
console.log(`Helper subtract: ${(subtractUsed && subtractRegistered) ? 'OK' : (subtractUsed ? 'Manquant' : 'Non utilisé')}`);
|
||||||
|
|
||||||
|
if (errors.length === 0 && missingPartials.length === 0 && !deprecatedFound && !effectWebpFound && !durationTypeFound && missingKeys.length === 0 && missingMnblKeys.length === 0 && (subtractUsed ? subtractRegistered : true)) {
|
||||||
|
console.log('\n✅ Toutes les vérifications ont réussi !');
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
console.log('\n❌ Certaines vérifications ont échoué !');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user