25 Commits

Author SHA1 Message Date
uberwald 92ea0164a2 Fix: Corrections diverses pour Mournblade CYD 2.0
- Modification du popup de bienvenue : simplification de la mention des droits (jeu Titam)
- Ajout du champ 'Coût en Pouvoir des invocations' dans l'onglet Sorcellerie pour suivre le coût des invocations en cours
- Amélioration de l'onglet Détails des Profils : remplacement des sections de texte simples par des sheet-box pour un meilleur rendu visuel
- Ajout de la clé i18n 'coutPouvoirInvocations' dans lang/fr.json
- Ajout du champ coutPouvoirInvocations dans le schéma de données Personnage

Corrections basées sur les demandes : popup, onglet Sorcellerie, onglet Détails Profils

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:52:50 +02:00
uberwald 0425ccf723 feat: Ajout carte Combat sur fiche de personnage
- Ajout d'une carte Combat (case bleue) dans l'en-tête de la fiche
  de personnage, similaire à celle des créatures
- Affiche en temps réel :
  * Initiative (calculée : Adresse + bonus)
  * Défense (calculée : base + bonus + protection - adversité)
  * Protection (calculée à partir des armures/boucliers équipés)
- Ajout des clés i18n pour les libellés abrégés :
  * MNBL.initShort: Init.
  * MNBL.defShort: Déf.
  * MNBL.protShort: Prot.

Ces valeurs sont déjà calculées par getCombatValues() et
protectionTotal dans le contexte de la feuille.

Permet aux joueurs de voir d'un coup d'œil leurs
caractéristiques de combat actuelles.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:39:14 +02:00
uberwald 37ff6ebf1d feat: Ajout section Invocations en cours dans l'onglet Sorcellerie
- Ajout du champ system.sorcellerie.invocationsencours (HTMLField)
- Ajout de la clé i18n SORCELLERIE.invocationsencours
- Ajout de la section dans le template avec éditeur riche
- Permet de noter les invocations actives avec leur coût en Pouvoir

L'éditeur riche permet de:
- Glisser-déposer des liens vers des objets du compendium
- Formater le texte (gras, italique, listes)
- Noter le coût temporaire en Pouvoir pour chaque invocation

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:34:08 +02:00
uberwald 1c73faeb00 feat: Ajout onglet Sorcellerie sur la fiche de personnage
- Ajout d'un nouvel onglet 'Sorcellerie' dans la navigation
- Déplacement de la section Runes de 'Dons & Pactes' vers 'Sorcellerie'
- Ajout de 3 nouvelles sections avec éditeurs riches :
  * Créatures invoquées
  * Démons liés
  * Enchantements / Automata
- Ajout des champs système pour stocker ces informations (HTMLField)
- Ajout des clés i18n sous le namespace SORCELLERIE

Structure de l'onglet Sorcellerie :
1. Runes (liste d'items, comme auparavant)
2. Créatures invoquées (éditeur riche)
3. Démons liés (éditeur riche)
4. Enchantements / Automata (éditeur riche)

Cela permet aux sorciers d'avoir plus d'espace pour noter leurs
nombreuses runes, invocations, etc. et d'y glisser-déposer des liens
vers des objets du compendium.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:14:30 +02:00
uberwald 0c42b6ab34 feat: Amélioration de l'onglet Détails des Profils
- Conversion des champs Compétences, Talents (Initié/Aguerri/Maître)
  et Prérequis (Aguerri/Maître) en HTMLField pour permettre
  l'utilisation de l'éditeur riche
- Remplacement des textarea à fond marron par des éditeurs blancs
  (comme pour les Sacrifices des Dons)
- Permet maintenant de glisser-déposer des liens vers des objets
  du compendium (Compétences, Talents, etc.)
- Structure cohérente avec le template des Dons

Modèles modifiés:
- modules/models/profil.mjs: conversion StringField → HTMLField
- templates/item-profil-sheet.hbs: remplacement textarea → éditeur

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:07:01 +02:00
uberwald d6e7b62c31 Docs: Mise à jour du pop-up de bienvenue
- Suppression du lien vers titam-france.fr
- Ajout du lien vers lahiette.com pour les règles PAO 0.9
- Conservation de la mention Titam dans la section Droits

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 14:03:36 +02:00
uberwald a3f304c77b Fix: Correction des clés i18n et affichage de l'onglet Effets
- Correction du namespace i18n: MOURNBLADECYD2.EFFECT → EFFECT
- Suppression de la condition {{#if item.effects.length}} dans tous les templates d'items
- Ajout de la clé EFFECT.noItemEffects pour les items sans effets
- Remplacement des textes en dur par des clés i18n dans les partials
- Mise à jour de toutes les références dans le code JavaScript

Cela corrige:
1. Les clés i18n manquantes (namespace incohérent)
2. L'onglet Effets des items qui était vide quand l'item n'avait pas d'effets

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 09:47:22 +02:00
uberwald 1b81b0a3ac Corrections§upgrade diverses 2026-06-07 09:43:37 +02:00
uberwald 3ff2b8e9bb Docs: Add MNBL i18n and effects tab fixes to documentation
- Documented missing MNBL.details and MNBL.description keys
- Documented effects tab visibility fix
- Updated file list and impact section

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:53:53 +02:00
uberwald 38525c3257 Fix: Add missing MNBL i18n keys and show effects tab always
- Added MNBL.details and MNBL.description to lang/fr.json
- Removed conditional display of effects tab in partial-item-nav.hbs
- Effects tab now always visible in item sheets
- Added MNBL i18n keys verification to test script

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:53:31 +02:00
uberwald f035bcfae2 Docs: Add i18n EFFECT keys fix to documentation
- Documented the missing i18n localization keys
- Updated file list and impact section

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:51:32 +02:00
uberwald a8bf356d20 Fix: Add missing i18n EFFECT keys to lang/fr.json
Added missing localization keys for ActiveEffect management:
- 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

Added test verification for EFFECT i18n keys

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:51:12 +02:00
uberwald cd70b70088 Docs: Add subtract helper fix to documentation
- Documented the missing subtract helper issue
- Updated file list and impact section

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:49:15 +02:00
uberwald 14763cc5b3 Fix: Add missing Handlebars subtract helper
- Added subtract helper: parseInt(a) - parseInt(b)
- Helper is used in partial-active-effects.hbs and partial-item-effects.hbs
- Registered in MournbladeCYD2Utility.init() alongside other helpers
- Added test verification for subtract helper registration

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:48:55 +02:00
uberwald 0258c2e8b7 Docs: Update with duration.type to duration.units fix
- Documented the ActiveEffectDuration property deprecation fix
- Updated file list and impact section

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:42:05 +02:00
uberwald 9b3d34c5d7 Fix: Replace deprecated duration.type with duration.units (Foundry v14+)
- ActiveEffectDuration#type was renamed to #units in Foundry VTT v14
- Updated partial-active-effects.hbs to use duration.units
- Updated partial-item-effects.hbs to use duration.units
- Added test verification for duration.type usage
- Support for duration.type will be removed in v16

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:41:46 +02:00
uberwald 335238df3d Docs: Update CORRECTIONS.md with effect.webp icon fix
- Documented the effect.webp missing icon issue
- Updated file list with all corrected files
- Updated impact section

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:36:40 +02:00
uberwald a1519e7a60 Fix: Replace missing effect.webp icon with existing capacite.webp
- effect.webp icon was missing, causing infinite 404 errors
- Replaced all references with capacite.webp which exists
- Fixed in base-actor-sheet.mjs, base-item-sheet.mjs, mournblade-cyd2-effects.js
- Fixed in partial-active-effects.hbs and partial-item-effects.hbs templates
- Updated test script to check for effect.webp references

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:36:12 +02:00
uberwald e55b5cbe15 Test: Add check for deprecated ActiveEffectDialog API usage
- Added verification for deprecated ActiveEffectDialog.create() calls
- Updated test script to check all critical fixes

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:34:06 +02:00
uberwald f28719fc6f Fix: Replace deprecated ActiveEffectDialog.create() with createEmbeddedDocuments
- ActiveEffectDialog.create() was removed in Foundry VTT v14
- Replaced with direct document.createEmbeddedDocuments() call
- After creation, opens the effect sheet for editing
- Fixed in both base-actor-sheet.mjs and base-item-sheet.mjs

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:33:44 +02:00
uberwald d0423b2017 Fix: Add missing Handlebars partials to preload function
- Added partial-active-effects.hbs and partial-item-effects.hbs to preloadHandlebarsTemplates()
- These partials were used in sheets but not preloaded, causing render errors
- Fixed lang/fr.json formatting (added closing brace and newline)
- Added test script to validate template loading
- Added CORRECTIONS.md documentation

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 00:26:07 +02:00
uberwald 156672d853 ActiveEffects: Add complete ActiveEffects management system
- New: modules/mournblade-cyd2-effects.js with utility methods for creating, applying, and managing effects
- New: templates/partial-active-effects.hbs for displaying actor effects
- New: templates/partial-item-effects.hbs for displaying item effects
- Update: modules/mournblade-cyd2-config.js with effect configuration (types, attribute keys, categories)
- Update: templates/actor-sheet.hbs and creature-sheet.hbs with Effects tab
- Update: templates/partial-item-nav.hbs with conditional Effects tab
- Update: templates/item-talent-sheet.hbs with Effects tab content
- Update: base-actor-sheet.mjs with effect action handlers (create, edit, delete, toggle)
- Update: base-item-sheet.mjs with effect action handlers and context
- Update: modules/mournblade-cyd2-main.js to import and expose MournbladeCYD2Effects
- Update: lang/fr.json with effect-related translations

Features:
- Support for creating permanent and temporary effects
- Support for attribute modifications (ADR, PUI, CLA, PRE, TRE, etc.)
- Support for health, soul, combat, and adversity modifications
- Support for status effects
- Support for runes (pronounced and traced) effects
- Toggle to enable/disable effects
- Duration tracking (rounds, turns, seconds, combat, scene)
- Display of all active modifications summary

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-06 22:51:31 +02:00
uberwald 5ab03920d6 Corrections diverses pour CYD2.0
- Fix: Remplacement de 'Points d'Âme' par 'Points de Pouvoir' dans les messages de chat
- Fix: Ajout de la case Âme avec Seuil de Pouvoir dans les fiches de créature
- Fix: Ajout du champ Seuil de Pouvoir éditable dans les fiches de créature
- Fix: Initiative, Défense et Protection maintenant éditables dans les fiches de créature
- Fix: Ajout du champ Bonus/Malus aux templates de Traits chaotiques et d'espèce
- Fix: Le champ coût en Pouvoir des Runes accepte maintenant du texte (StringField)
- Fix: Rafraîchissement des fiches après drop d'items pour afficher les Traits
- Fix: Ajout des nouveaux types d'items (Trait Démoniaque, Pouvoir Élémentaire, Capacité d'Automata)
- New: Ajout des modèles et templates pour les nouveaux types d'items
- New: Intégration complète des nouveaux types dans les fichiers de configuration

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-06 22:08:53 +02:00
uberwald 9dd6fbd2e7 Fix: Affichage des Traits et corrections diverses
- Fix: Ajout des sections Traits Chaotiques et Traits d'Espèce dans les fiches de créature
  - Ajout de context.traitsChaotiques et context.traitsEspeces dans la feuille de créature
  - Ajout des sections dans le template creature-sheet.hbs avec boutons d'ajout
- Fix: Bouton Ajouter Automatisation fonctionnel dans les Talents
  - Ajout des actions addAutomation et deleteAutomation dans base-item-sheet.mjs
  - Ajout des méthodes #onAddAutomation et #onDeleteAutomation
  - Ajout des attributs data-action sur les boutons du template partial-automation.hbs
  - Ajout des attributs name sur les champs d'automatisation
- Ajout du champ Don lié dans les Tendances
  - Ajout de donlie dans le modèle tendance.mjs
  - Ajout du champ Don lié dans le template item-tendance-sheet.hbs
- Ajout de la catégorie Balance dans les options d'allégeance
  - Ajout dans mournblade-cyd2-config.js
  - Ajout de la traduction dans lang/fr.json
- Correction du libellé Coût en Âme → Coût en points de pouvoir
- Correction du bug de l'éditeur Sacrifices dans les Dons
  - Ajout de owner et editable dans le contexte
  - Renommage de Sacrifices en Sacrifices et Tendances liées
- Ajout de la case Âme avec Seuil de Pouvoir dans les fiches de créature
- Rendre Initiative, Défense et Protection éditables dans les fiches de créature
  - Ajout des champs inittotal, defensetotal, protectiontotal dans creature.mjs
  - Modification du contexte pour prioriser les valeurs manuelles
  - Remplacement des spans par des inputs dans le template

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-06 20:30:04 +02:00
uberwald 76ed974352 Fix: Correction du libellé Coût en Âme et ajout de la catégorie Balance
- Correction du libellé 'Coût en Âme' en 'Coût en points de pouvoir' pour les runes
- Fix: Ajout des gestionnaires d'événements pour les automatisations des Talents
  - Ajout des actions addAutomation et deleteAutomation dans base-item-sheet.mjs
  - Ajout des attributs data-action sur les boutons du template partial-automation.hbs
  - Ajout des attributs name sur les champs d'automatisation pour la sauvegarde
- Ajout de la catégorie 'Balance' dans les options d'allégeance pour Dons et Tendances

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-06 18:32:10 +02:00
158 changed files with 3072 additions and 410 deletions
+458
View File
@@ -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
+74 -3
View File
@@ -20,7 +20,26 @@
"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"
}
},
"SORCELLERIE": {
"tab": "Sorcellerie",
"runes": "Runes",
"creaturesinvoquees": "Créatures invoquées",
"demonslies": "Démons liés",
"enchantements": "Enchantements / Automata",
"invocationsencours": "Invocations en cours",
"coutPouvoirInvocations": "Coût en Pouvoir des invocations"
},
"SHEETS": {
"Item": {
"traitdemoniaque": "Trait Démoniaque",
"pouvoirselementaire": "Pouvoir Élémentaire",
"capaciteautomata": "Capacité d'Automata"
} }
}, },
"MOURNBLADE": { "MOURNBLADE": {
@@ -32,10 +51,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 +78,58 @@
"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",
"initiative": "Initiative",
"initShort": "Init.",
"defense": "Défense",
"defShort": "Déf.",
"protection": "Protection",
"protShort": "Prot."
},
"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",
"noItemEffects": "Aucun effet sur cet item",
"effectSummary": "Résumé des modifications",
"toggleEffect": "Activer/Désactiver"
} }
} }
+3
View File
@@ -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("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("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("EFFECT.deleteConfirm") || "Supprimer l'effet",
content: game.i18n.localize("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("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("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
} }
+195 -2
View File
@@ -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("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("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("EFFECT.deleteConfirm") || "Supprimer l'effet",
content: game.i18n.localize("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("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("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("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;
}
}
+12
View File
@@ -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: "" })
};
}
}
+4 -1
View File
@@ -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 }),
+3
View File
@@ -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';
+9
View File
@@ -102,6 +102,15 @@ export default class PersonnageDataModel extends foundry.abstract.TypeDataModel
marge: new fields.NumberField({ initial: 0, integer: true }), marge: new fields.NumberField({ initial: 0, integer: true }),
pointschaos: new fields.NumberField({ initial: 0, integer: true }), pointschaos: new fields.NumberField({ initial: 0, integer: true }),
pointsloi: new fields.NumberField({ initial: 0, integer: true }) pointsloi: new fields.NumberField({ initial: 0, integer: true })
}),
// Sorcellerie
sorcellerie: new fields.SchemaField({
runes: new fields.HTMLField({ initial: "" }),
creaturesinvoquees: new fields.HTMLField({ initial: "" }),
demonslies: new fields.HTMLField({ initial: "" }),
enchantements: new fields.HTMLField({ initial: "" }),
invocationsencours: new fields.HTMLField({ initial: "" }),
coutPouvoirInvocations: new fields.NumberField({ initial: 0, integer: true })
}) })
}; };
} }
+12
View File
@@ -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: "" })
};
}
}
+6 -6
View File
@@ -10,12 +10,12 @@ export default class ProfilDataModel extends foundry.abstract.TypeDataModel {
attribut1: new fields.StringField({ initial: "" }), attribut1: new fields.StringField({ initial: "" }),
attribut2: new fields.StringField({ initial: "" }), attribut2: new fields.StringField({ initial: "" }),
attribut3: new fields.StringField({ initial: "" }), attribut3: new fields.StringField({ initial: "" }),
competences: new fields.StringField({ initial: "" }), competences: new fields.HTMLField({ initial: "" }),
talentsinitie: new fields.StringField({ initial: "" }), talentsinitie: new fields.HTMLField({ initial: "" }),
prerequisaguerri: new fields.StringField({ initial: "" }), prerequisaguerri: new fields.HTMLField({ initial: "" }),
talentsaguerri: new fields.StringField({ initial: "" }), talentsaguerri: new fields.HTMLField({ initial: "" }),
prerequismaitre: new fields.StringField({ initial: "" }), prerequismaitre: new fields.HTMLField({ initial: "" }),
talentsmaitre: new fields.StringField({ initial: "" }), talentsmaitre: new fields.HTMLField({ initial: "" }),
equipement: new fields.StringField({ initial: "" }) equipement: new fields.StringField({ initial: "" })
}; };
} }
+1 -1
View File
@@ -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: "" })
}; };
} }
} }
+2 -1
View File
@@ -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: "" })
}; };
} }
} }
+2 -1
View File
@@ -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: "" })
}; };
} }
} }
+12
View File
@@ -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: "" })
};
}
}
+2 -1
View File
@@ -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: "" })
}; };
} }
} }
+91
View File
@@ -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" },
+807
View File
@@ -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("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("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();
});
+11 -3
View File
@@ -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()
@@ -123,14 +131,14 @@ function welcomeMessage() {
<div class="section-text"> <div class="section-text">
<strong>Livres nécessaires</strong> <strong>Livres nécessaires</strong>
<p>Les livres de Mournblade sont nécessaires pour jouer. Supplément de conversion CYD 2.0 requis.</p> <p>Les livres de Mournblade sont nécessaires pour jouer. Supplément de conversion CYD 2.0 requis.</p>
<a class="welcome-link" href="https://www.titam-france.fr" target="_blank"><i class="fas fa-external-link-alt"></i>titam-france.fr</a> <a class="welcome-link" href="https://www.lahiette.com/leratierbretonnien/mournblade-et-le-cyd-2-0/" target="_blank"><i class="fas fa-external-link-alt"></i>Règles PAO 0.9</a>
</div> </div>
</div> </div>
<div class="welcome-section"> <div class="welcome-section">
<div class="section-icon"><i class="fas fa-copyright"></i></div> <div class="section-icon"><i class="fas fa-copyright"></i></div>
<div class="section-text"> <div class="section-text">
<strong>Droits</strong> <strong>Droits</strong>
<p>Mournblade est un jeu publié par Titam France / Sombres Projets, tous les droits leur appartiennent.</p> <p>Mournblade est un jeu Titam.</p>
</div> </div>
</div> </div>
<div class="welcome-section"> <div class="welcome-section">
+5
View File
@@ -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);
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000410 MANIFEST-000431
+3 -7
View File
@@ -1,7 +1,3 @@
2026/05/24-16:13:27.948127 7fdf5bfff6c0 Recovering log #409 2026/06/07-00:21:50.466229 7f15cf3fe6c0 Recovering log #429
2026/05/24-16:13:27.958589 7fdf5bfff6c0 Delete type=0 #409 2026/06/07-00:21:50.475486 7f15cf3fe6c0 Delete type=3 #427
2026/05/24-16:13:27.958609 7fdf5bfff6c0 Delete type=3 #408 2026/06/07-00:21:50.475515 7f15cf3fe6c0 Delete type=0 #429
2026/05/24-16:49:30.908639 7fdf5affd6c0 Level-0 table #413: started
2026/05/24-16:49:30.908660 7fdf5affd6c0 Level-0 table #413: 0 bytes OK
2026/05/24-16:49:30.941518 7fdf5affd6c0 Manual compaction at level-0 from '!journal!gVybbv17TFY8o3Y4' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)
2026/05/24-16:49:30.959159 7fdf5affd6c0 Manual compaction at level-1 from '!journal!gVybbv17TFY8o3Y4' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/05/24-16:10:55.778032 7f1c57fff6c0 Recovering log #407 2026/06/07-00:01:17.675368 7f15ce3fc6c0 Recovering log #425
2026/05/24-16:10:55.794066 7f1c57fff6c0 Delete type=0 #407 2026/06/07-00:01:17.685411 7f15ce3fc6c0 Delete type=3 #423
2026/05/24-16:10:55.794127 7f1c57fff6c0 Delete type=3 #406 2026/06/07-00:01:17.685428 7f15ce3fc6c0 Delete type=0 #425
2026/06/07-00:21:46.748268 7f15cdbfb6c0 Level-0 table #430: started
2026/06/07-00:21:46.748282 7f15cdbfb6c0 Level-0 table #430: 0 bytes OK
2026/06/07-00:21:46.755214 7f15cdbfb6c0 Delete type=0 #428
2026/06/07-00:21:46.768389 7f15cdbfb6c0 Manual compaction at level-0 from '!journal!gVybbv17TFY8o3Y4' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000402 MANIFEST-000426
+3 -20
View File
@@ -1,20 +1,3 @@
2026/05/24-16:13:27.805283 7fdf5bfff6c0 Recovering log #401 2026/06/07-00:21:50.343006 7f15cebfd6c0 Recovering log #424
2026/05/24-16:13:27.815296 7fdf5bfff6c0 Delete type=0 #401 2026/06/07-00:21:50.352418 7f15cebfd6c0 Delete type=3 #422
2026/05/24-16:13:27.815331 7fdf5bfff6c0 Delete type=3 #400 2026/06/07-00:21:50.352440 7f15cebfd6c0 Delete type=0 #424
2026/05/24-16:49:30.661093 7fdf5affd6c0 Level-0 table #405: started
2026/05/24-16:49:30.661158 7fdf5affd6c0 Level-0 table #405: 0 bytes OK
2026/05/24-16:49:30.698495 7fdf5affd6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at '!items!wv5EiePmPTpqFutt' @ 287 : 1
2026/05/24-16:49:30.698499 7fdf5affd6c0 Compacting 1@0 + 0@1 files
2026/05/24-16:49:30.701559 7fdf5affd6c0 Generated table #406@0: 48 keys, 14533 bytes
2026/05/24-16:49:30.701564 7fdf5affd6c0 Compacted 1@0 + 0@1 files => 14533 bytes
2026/05/24-16:49:30.707420 7fdf5affd6c0 compacted to: files[ 0 1 1 0 0 0 0 ]
2026/05/24-16:49:30.707454 7fdf5affd6c0 Delete type=2 #392
2026/05/24-16:49:30.707535 7fdf5affd6c0 Manual compaction at level-0 from '!items!wv5EiePmPTpqFutt' @ 287 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
2026/05/24-16:49:30.707559 7fdf5affd6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at '!items!wv5EiePmPTpqFutt' @ 287 : 1
2026/05/24-16:49:30.707561 7fdf5affd6c0 Compacting 1@1 + 1@2 files
2026/05/24-16:49:30.710996 7fdf5affd6c0 Generated table #407@1: 48 keys, 14533 bytes
2026/05/24-16:49:30.711014 7fdf5affd6c0 Compacted 1@1 + 1@2 files => 14533 bytes
2026/05/24-16:49:30.717230 7fdf5affd6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/24-16:49:30.717309 7fdf5affd6c0 Delete type=2 #235
2026/05/24-16:49:30.717368 7fdf5affd6c0 Delete type=2 #406
2026/05/24-16:49:30.739563 7fdf5affd6c0 Manual compaction at level-1 from '!items!wv5EiePmPTpqFutt' @ 287 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/05/24-16:10:54.653592 7f2f52bfe6c0 Recovering log #399 2026/06/07-00:01:17.549317 7f15ce3fc6c0 Recovering log #420
2026/05/24-16:10:54.665351 7f2f52bfe6c0 Delete type=0 #399 2026/06/07-00:01:17.559821 7f15ce3fc6c0 Delete type=3 #418
2026/05/24-16:10:54.665381 7f2f52bfe6c0 Delete type=3 #398 2026/06/07-00:01:17.559847 7f15ce3fc6c0 Delete type=0 #420
2026/06/07-00:21:46.679739 7f15cdbfb6c0 Level-0 table #425: started
2026/06/07-00:21:46.679750 7f15cdbfb6c0 Level-0 table #425: 0 bytes OK
2026/06/07-00:21:46.685835 7f15cdbfb6c0 Delete type=0 #423
2026/06/07-00:21:46.698903 7f15cdbfb6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000302 MANIFEST-000325
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.843303 7fdfa8dfe6c0 Recovering log #301 2026/06/07-00:21:50.374568 7f15cf3fe6c0 Recovering log #323
2026/05/24-16:13:27.854272 7fdfa8dfe6c0 Delete type=0 #301 2026/06/07-00:21:50.384637 7f15cf3fe6c0 Delete type=3 #321
2026/05/24-16:13:27.854314 7fdfa8dfe6c0 Delete type=3 #299 2026/06/07-00:21:50.384658 7f15cf3fe6c0 Delete type=0 #323
2026/05/24-16:49:30.749361 7fdf5affd6c0 Level-0 table #305: started
2026/05/24-16:49:30.749380 7fdf5affd6c0 Level-0 table #305: 0 bytes OK
2026/05/24-16:49:30.773159 7fdf5affd6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at '!items!zzz9JrtWjELdoAfK' @ 150 : 1
2026/05/24-16:49:30.773164 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.776884 7fdf5affd6c0 Generated table #306@0: 30 keys, 15949 bytes
2026/05/24-16:49:30.776900 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 15949 bytes
2026/05/24-16:49:30.782834 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.782894 7fdf5affd6c0 Delete type=2 #288
2026/05/24-16:49:30.782981 7fdf5affd6c0 Delete type=2 #300
2026/05/24-16:49:30.800757 7fdf5affd6c0 Manual compaction at level-0 from '!items!zzz9JrtWjELdoAfK' @ 150 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:54.877151 7f57f93ff6c0 Recovering log #298 2026/06/07-00:01:17.584289 7f15ce3fc6c0 Recovering log #319
2026/05/24-16:10:54.877440 7f57f93ff6c0 Level-0 table #300: started 2026/06/07-00:01:17.594286 7f15ce3fc6c0 Delete type=3 #317
2026/05/24-16:10:54.881592 7f57f93ff6c0 Level-0 table #300: 15949 bytes OK 2026/06/07-00:01:17.594301 7f15ce3fc6c0 Delete type=0 #319
2026/05/24-16:10:54.891689 7f57f93ff6c0 Delete type=0 #298 2026/06/07-00:21:46.698908 7f15cdbfb6c0 Level-0 table #324: started
2026/05/24-16:10:54.891758 7f57f93ff6c0 Delete type=3 #297 2026/06/07-00:21:46.698917 7f15cdbfb6c0 Level-0 table #324: 0 bytes OK
2026/06/07-00:21:46.704788 7f15cdbfb6c0 Delete type=0 #322
2026/06/07-00:21:46.710794 7f15cdbfb6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000302 MANIFEST-000325
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.829466 7fdf5b7fe6c0 Recovering log #301 2026/06/07-00:21:50.363711 7f15ce3fc6c0 Recovering log #323
2026/05/24-16:13:27.840345 7fdf5b7fe6c0 Delete type=0 #301 2026/06/07-00:21:50.373124 7f15ce3fc6c0 Delete type=3 #321
2026/05/24-16:13:27.840390 7fdf5b7fe6c0 Delete type=3 #299 2026/06/07-00:21:50.373150 7f15ce3fc6c0 Delete type=0 #323
2026/05/24-16:49:30.733360 7fdf5affd6c0 Level-0 table #305: started
2026/05/24-16:49:30.733392 7fdf5affd6c0 Level-0 table #305: 0 bytes OK
2026/05/24-16:49:30.756430 7fdf5affd6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at '!items!y47dBO3Mf5Pn7tOd' @ 275 : 1
2026/05/24-16:49:30.756454 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.759808 7fdf5affd6c0 Generated table #306@0: 55 keys, 9796 bytes
2026/05/24-16:49:30.759829 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 9796 bytes
2026/05/24-16:49:30.766114 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.766193 7fdf5affd6c0 Delete type=2 #288
2026/05/24-16:49:30.766365 7fdf5affd6c0 Delete type=2 #300
2026/05/24-16:49:30.783021 7fdf5affd6c0 Manual compaction at level-0 from '!items!y47dBO3Mf5Pn7tOd' @ 275 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:54.804401 7fc086bff6c0 Recovering log #298 2026/06/07-00:01:17.573032 7f15cfbff6c0 Recovering log #319
2026/05/24-16:10:54.804603 7fc086bff6c0 Level-0 table #300: started 2026/06/07-00:01:17.582308 7f15cfbff6c0 Delete type=3 #317
2026/05/24-16:10:54.809175 7fc086bff6c0 Level-0 table #300: 9796 bytes OK 2026/06/07-00:01:17.582329 7f15cfbff6c0 Delete type=0 #319
2026/05/24-16:10:54.820050 7fc086bff6c0 Delete type=0 #298 2026/06/07-00:21:46.660469 7f15cdbfb6c0 Level-0 table #324: started
2026/05/24-16:10:54.820128 7fc086bff6c0 Delete type=3 #297 2026/06/07-00:21:46.660487 7f15cdbfb6c0 Level-0 table #324: 0 bytes OK
2026/06/07-00:21:46.667102 7f15cdbfb6c0 Delete type=0 #322
2026/06/07-00:21:46.679674 7f15cdbfb6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000418 MANIFEST-000442
+3 -20
View File
@@ -1,20 +1,3 @@
2026/05/24-16:13:27.793795 7fdf5b7fe6c0 Recovering log #417 2026/06/07-00:21:50.331328 7f15cf3fe6c0 Recovering log #440
2026/05/24-16:13:27.803165 7fdf5b7fe6c0 Delete type=0 #417 2026/06/07-00:21:50.341554 7f15cf3fe6c0 Delete type=3 #438
2026/05/24-16:13:27.803205 7fdf5b7fe6c0 Delete type=3 #416 2026/06/07-00:21:50.341580 7f15cf3fe6c0 Delete type=0 #440
2026/05/24-16:49:30.654051 7fdf5affd6c0 Level-0 table #421: started
2026/05/24-16:49:30.654105 7fdf5affd6c0 Level-0 table #421: 0 bytes OK
2026/05/24-16:49:30.688223 7fdf5affd6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at '!items!z1HtkvazCGHut7cz' @ 336 : 1
2026/05/24-16:49:30.688226 7fdf5affd6c0 Compacting 1@0 + 0@1 files
2026/05/24-16:49:30.692355 7fdf5affd6c0 Generated table #422@0: 48 keys, 20398 bytes
2026/05/24-16:49:30.692374 7fdf5affd6c0 Compacted 1@0 + 0@1 files => 20398 bytes
2026/05/24-16:49:30.698404 7fdf5affd6c0 compacted to: files[ 0 1 1 0 0 0 0 ]
2026/05/24-16:49:30.698435 7fdf5affd6c0 Delete type=2 #410
2026/05/24-16:49:30.707532 7fdf5affd6c0 Manual compaction at level-0 from '!items!z1HtkvazCGHut7cz' @ 336 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)
2026/05/24-16:49:30.717401 7fdf5affd6c0 Manual compaction at level-1 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at '!items!z1HtkvazCGHut7cz' @ 336 : 1
2026/05/24-16:49:30.717404 7fdf5affd6c0 Compacting 1@1 + 1@2 files
2026/05/24-16:49:30.720557 7fdf5affd6c0 Generated table #423@1: 48 keys, 20398 bytes
2026/05/24-16:49:30.720563 7fdf5affd6c0 Compacted 1@1 + 1@2 files => 20398 bytes
2026/05/24-16:49:30.726463 7fdf5affd6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/24-16:49:30.726537 7fdf5affd6c0 Delete type=2 #352
2026/05/24-16:49:30.726638 7fdf5affd6c0 Delete type=2 #422
2026/05/24-16:49:30.739587 7fdf5affd6c0 Manual compaction at level-1 from '!items!z1HtkvazCGHut7cz' @ 336 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/05/24-16:10:55.365745 7f834bfff6c0 Recovering log #415 2026/06/07-00:01:17.537677 7f15cfbff6c0 Recovering log #436
2026/05/24-16:10:55.376057 7f834bfff6c0 Delete type=0 #415 2026/06/07-00:01:17.547496 7f15cfbff6c0 Delete type=3 #434
2026/05/24-16:10:55.376117 7f834bfff6c0 Delete type=3 #414 2026/06/07-00:01:17.547526 7f15cfbff6c0 Delete type=0 #436
2026/06/07-00:21:46.685907 7f15cdbfb6c0 Level-0 table #441: started
2026/06/07-00:21:46.685917 7f15cdbfb6c0 Level-0 table #441: 0 bytes OK
2026/06/07-00:21:46.691937 7f15cdbfb6c0 Delete type=0 #439
2026/06/07-00:21:46.704830 7f15cdbfb6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000418 MANIFEST-000442
+3 -20
View File
@@ -1,20 +1,3 @@
2026/05/24-16:13:27.895777 7fdfa95ff6c0 Recovering log #417 2026/06/07-00:21:50.420841 7f15cfbff6c0 Recovering log #440
2026/05/24-16:13:27.904649 7fdfa95ff6c0 Delete type=0 #417 2026/06/07-00:21:50.430740 7f15cfbff6c0 Delete type=3 #438
2026/05/24-16:13:27.904673 7fdfa95ff6c0 Delete type=3 #416 2026/06/07-00:21:50.430770 7f15cfbff6c0 Delete type=0 #440
2026/05/24-16:49:30.833777 7fdf5affd6c0 Level-0 table #421: started
2026/05/24-16:49:30.833795 7fdf5affd6c0 Level-0 table #421: 0 bytes OK
2026/05/24-16:49:30.850864 7fdf5affd6c0 Manual compaction at level-0 from '!items!26mRstKhCJoXkhu1' @ 72057594037927935 : 1 .. '!items!tFQqcxmkS3MT6ASE' @ 0 : 0; will stop at '!items!tFQqcxmkS3MT6ASE' @ 105 : 1
2026/05/24-16:49:30.850868 7fdf5affd6c0 Compacting 1@0 + 0@1 files
2026/05/24-16:49:30.854607 7fdf5affd6c0 Generated table #422@0: 15 keys, 30233 bytes
2026/05/24-16:49:30.854633 7fdf5affd6c0 Compacted 1@0 + 0@1 files => 30233 bytes
2026/05/24-16:49:30.862029 7fdf5affd6c0 compacted to: files[ 0 1 1 0 0 0 0 ]
2026/05/24-16:49:30.862120 7fdf5affd6c0 Delete type=2 #410
2026/05/24-16:49:30.878767 7fdf5affd6c0 Manual compaction at level-0 from '!items!tFQqcxmkS3MT6ASE' @ 105 : 1 .. '!items!tFQqcxmkS3MT6ASE' @ 0 : 0; will stop at (end)
2026/05/24-16:49:30.890474 7fdf5affd6c0 Manual compaction at level-1 from '!items!26mRstKhCJoXkhu1' @ 72057594037927935 : 1 .. '!items!tFQqcxmkS3MT6ASE' @ 0 : 0; will stop at '!items!tFQqcxmkS3MT6ASE' @ 105 : 1
2026/05/24-16:49:30.890480 7fdf5affd6c0 Compacting 1@1 + 1@2 files
2026/05/24-16:49:30.895249 7fdf5affd6c0 Generated table #423@1: 15 keys, 30233 bytes
2026/05/24-16:49:30.895261 7fdf5affd6c0 Compacted 1@1 + 1@2 files => 30233 bytes
2026/05/24-16:49:30.908330 7fdf5affd6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/24-16:49:30.908435 7fdf5affd6c0 Delete type=2 #352
2026/05/24-16:49:30.908535 7fdf5affd6c0 Delete type=2 #422
2026/05/24-16:49:30.915370 7fdf5affd6c0 Manual compaction at level-1 from '!items!tFQqcxmkS3MT6ASE' @ 105 : 1 .. '!items!tFQqcxmkS3MT6ASE' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/05/24-16:10:55.440137 7fc32b7ef6c0 Recovering log #415 2026/06/07-00:01:17.628038 7f15cf3fe6c0 Recovering log #436
2026/05/24-16:10:55.459685 7fc32b7ef6c0 Delete type=0 #415 2026/06/07-00:01:17.637109 7f15cf3fe6c0 Delete type=3 #434
2026/05/24-16:10:55.459751 7fc32b7ef6c0 Delete type=3 #414 2026/06/07-00:01:17.637121 7f15cf3fe6c0 Delete type=0 #436
2026/06/07-00:21:46.761269 7f15cdbfb6c0 Level-0 table #441: started
2026/06/07-00:21:46.761294 7f15cdbfb6c0 Level-0 table #441: 0 bytes OK
2026/06/07-00:21:46.768307 7f15cdbfb6c0 Delete type=0 #439
2026/06/07-00:21:46.777465 7f15cdbfb6c0 Manual compaction at level-0 from '!items!26mRstKhCJoXkhu1' @ 72057594037927935 : 1 .. '!items!tFQqcxmkS3MT6ASE' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000302 MANIFEST-000325
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.817130 7fdfa8dfe6c0 Recovering log #301 2026/06/07-00:21:50.353793 7f15cfbff6c0 Recovering log #323
2026/05/24-16:13:27.827508 7fdfa8dfe6c0 Delete type=0 #301 2026/06/07-00:21:50.362811 7f15cfbff6c0 Delete type=3 #321
2026/05/24-16:13:27.827563 7fdfa8dfe6c0 Delete type=3 #299 2026/06/07-00:21:50.362829 7f15cfbff6c0 Delete type=0 #323
2026/05/24-16:49:30.726685 7fdf5affd6c0 Level-0 table #305: started
2026/05/24-16:49:30.726704 7fdf5affd6c0 Level-0 table #305: 0 bytes OK
2026/05/24-16:49:30.739601 7fdf5affd6c0 Manual compaction at level-0 from '!items!2hD1DQVeCIQIXFU7' @ 72057594037927935 : 1 .. '!items!veoS6Gtzj6Dq087V' @ 0 : 0; will stop at '!items!veoS6Gtzj6Dq087V' @ 35 : 1
2026/05/24-16:49:30.739606 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.742829 7fdf5affd6c0 Generated table #306@0: 7 keys, 2881 bytes
2026/05/24-16:49:30.742848 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 2881 bytes
2026/05/24-16:49:30.749097 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.749193 7fdf5affd6c0 Delete type=2 #288
2026/05/24-16:49:30.749309 7fdf5affd6c0 Delete type=2 #300
2026/05/24-16:49:30.773136 7fdf5affd6c0 Manual compaction at level-0 from '!items!veoS6Gtzj6Dq087V' @ 35 : 1 .. '!items!veoS6Gtzj6Dq087V' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:54.729976 7f0993fff6c0 Recovering log #298 2026/06/07-00:01:17.561757 7f15cf3fe6c0 Recovering log #319
2026/05/24-16:10:54.730135 7f0993fff6c0 Level-0 table #300: started 2026/06/07-00:01:17.571010 7f15cf3fe6c0 Delete type=3 #317
2026/05/24-16:10:54.734161 7f0993fff6c0 Level-0 table #300: 2881 bytes OK 2026/06/07-00:01:17.571033 7f15cf3fe6c0 Delete type=0 #319
2026/05/24-16:10:54.744747 7f0993fff6c0 Delete type=0 #298 2026/06/07-00:21:46.667152 7f15cdbfb6c0 Level-0 table #324: started
2026/05/24-16:10:54.744817 7f0993fff6c0 Delete type=3 #297 2026/06/07-00:21:46.667166 7f15cdbfb6c0 Level-0 table #324: 0 bytes OK
2026/06/07-00:21:46.673526 7f15cdbfb6c0 Delete type=0 #322
2026/06/07-00:21:46.679679 7f15cdbfb6c0 Manual compaction at level-0 from '!items!2hD1DQVeCIQIXFU7' @ 72057594037927935 : 1 .. '!items!veoS6Gtzj6Dq087V' @ 0 : 0; will stop at (end)
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000302 MANIFEST-000325
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.881867 7fdf5bfff6c0 Recovering log #301 2026/06/07-00:21:50.409088 7f15ce3fc6c0 Recovering log #323
2026/05/24-16:13:27.892914 7fdf5bfff6c0 Delete type=0 #301 2026/06/07-00:21:50.419461 7f15ce3fc6c0 Delete type=3 #321
2026/05/24-16:13:27.892950 7fdf5bfff6c0 Delete type=3 #299 2026/06/07-00:21:50.419490 7f15ce3fc6c0 Delete type=0 #323
2026/05/24-16:49:30.800774 7fdf5affd6c0 Level-0 table #305: started
2026/05/24-16:49:30.800796 7fdf5affd6c0 Level-0 table #305: 0 bytes OK
2026/05/24-16:49:30.823354 7fdf5affd6c0 Manual compaction at level-0 from '!items!1JqWbEkHUoKXbsgn' @ 72057594037927935 : 1 .. '!items!xnCf2xIPzdsUoBTy' @ 0 : 0; will stop at '!items!xnCf2xIPzdsUoBTy' @ 225 : 1
2026/05/24-16:49:30.823360 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.827700 7fdf5affd6c0 Generated table #306@0: 45 keys, 33963 bytes
2026/05/24-16:49:30.827710 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 33963 bytes
2026/05/24-16:49:30.833611 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.833659 7fdf5affd6c0 Delete type=2 #288
2026/05/24-16:49:30.833739 7fdf5affd6c0 Delete type=2 #300
2026/05/24-16:49:30.850855 7fdf5affd6c0 Manual compaction at level-0 from '!items!xnCf2xIPzdsUoBTy' @ 225 : 1 .. '!items!xnCf2xIPzdsUoBTy' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:54.953569 7f78713ff6c0 Recovering log #298 2026/06/07-00:01:17.617058 7f15ce3fc6c0 Recovering log #319
2026/05/24-16:10:54.953939 7f78713ff6c0 Level-0 table #300: started 2026/06/07-00:01:17.626625 7f15ce3fc6c0 Delete type=3 #317
2026/05/24-16:10:54.958319 7f78713ff6c0 Level-0 table #300: 33963 bytes OK 2026/06/07-00:01:17.626639 7f15ce3fc6c0 Delete type=0 #319
2026/05/24-16:10:54.968428 7f78713ff6c0 Delete type=0 #298 2026/06/07-00:21:46.716945 7f15cdbfb6c0 Level-0 table #324: started
2026/05/24-16:10:54.968509 7f78713ff6c0 Delete type=3 #297 2026/06/07-00:21:46.716955 7f15cdbfb6c0 Level-0 table #324: 0 bytes OK
2026/06/07-00:21:46.723471 7f15cdbfb6c0 Delete type=0 #322
2026/06/07-00:21:46.729892 7f15cdbfb6c0 Manual compaction at level-0 from '!items!1JqWbEkHUoKXbsgn' @ 72057594037927935 : 1 .. '!items!xnCf2xIPzdsUoBTy' @ 0 : 0; will stop at (end)
Binary file not shown.
View File
View File
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000250 MANIFEST-000273
+3 -14
View File
@@ -1,14 +1,3 @@
2026/05/24-16:13:27.934950 7fdf5b7fe6c0 Recovering log #249 2026/06/07-00:21:50.454761 7f15ce3fc6c0 Recovering log #271
2026/05/24-16:13:27.944727 7fdf5b7fe6c0 Delete type=0 #249 2026/06/07-00:21:50.464634 7f15ce3fc6c0 Delete type=3 #269
2026/05/24-16:13:27.944766 7fdf5b7fe6c0 Delete type=3 #248 2026/06/07-00:21:50.464657 7f15ce3fc6c0 Delete type=0 #271
2026/05/24-16:49:30.868686 7fdf5affd6c0 Level-0 table #253: started
2026/05/24-16:49:30.871770 7fdf5affd6c0 Level-0 table #253: 1897 bytes OK
2026/05/24-16:49:30.908620 7fdf5affd6c0 Manual compaction at level-0 from '!scenes!dYKdGdh2PbtXs32a' @ 72057594037927935 : 1 .. '!scenes.levels!dYKdGdh2PbtXs32a.defaultLevel0000' @ 0 : 0; will stop at (end)
2026/05/24-16:49:30.915400 7fdf5affd6c0 Manual compaction at level-1 from '!scenes!dYKdGdh2PbtXs32a' @ 72057594037927935 : 1 .. '!scenes.levels!dYKdGdh2PbtXs32a.defaultLevel0000' @ 0 : 0; will stop at '!scenes.levels!mfosNsLsHN5Pf4TO.defaultLevel0000' @ 104 : 0
2026/05/24-16:49:30.915405 7fdf5affd6c0 Compacting 1@1 + 1@2 files
2026/05/24-16:49:30.918981 7fdf5affd6c0 Generated table #254@1: 2 keys, 1628 bytes
2026/05/24-16:49:30.918988 7fdf5affd6c0 Compacted 1@1 + 1@2 files => 1628 bytes
2026/05/24-16:49:30.925116 7fdf5affd6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2026/05/24-16:49:30.925157 7fdf5affd6c0 Delete type=2 #223
2026/05/24-16:49:30.925248 7fdf5affd6c0 Delete type=2 #253
2026/05/24-16:49:30.941527 7fdf5affd6c0 Manual compaction at level-1 from '!scenes.levels!mfosNsLsHN5Pf4TO.defaultLevel0000' @ 104 : 0 .. '!scenes.levels!dYKdGdh2PbtXs32a.defaultLevel0000' @ 0 : 0; will stop at (end)
+7 -3
View File
@@ -1,3 +1,7 @@
2026/05/24-16:10:55.855003 7fcff97ec6c0 Recovering log #247 2026/06/07-00:01:17.661008 7f15cf3fe6c0 Recovering log #267
2026/05/24-16:10:55.867026 7fcff97ec6c0 Delete type=0 #247 2026/06/07-00:01:17.670919 7f15cf3fe6c0 Delete type=3 #265
2026/05/24-16:10:55.867082 7fcff97ec6c0 Delete type=3 #246 2026/06/07-00:01:17.670935 7f15cf3fe6c0 Delete type=0 #267
2026/06/07-00:21:46.723582 7f15cdbfb6c0 Level-0 table #272: started
2026/06/07-00:21:46.723594 7f15cdbfb6c0 Level-0 table #272: 0 bytes OK
2026/06/07-00:21:46.729834 7f15cdbfb6c0 Delete type=0 #270
2026/06/07-00:21:46.742119 7f15cdbfb6c0 Manual compaction at level-0 from '!scenes!dYKdGdh2PbtXs32a' @ 72057594037927935 : 1 .. '!scenes.levels!dYKdGdh2PbtXs32a.defaultLevel0000' @ 0 : 0; will stop at (end)
Binary file not shown.
View File
View File
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000208 MANIFEST-000231
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.781963 7fdf5b7fe6c0 Recovering log #207 2026/06/07-00:21:50.321397 7f15ce3fc6c0 Recovering log #229
2026/05/24-16:13:27.792095 7fdf5b7fe6c0 Delete type=0 #207 2026/06/07-00:21:50.330366 7f15ce3fc6c0 Delete type=3 #227
2026/05/24-16:13:27.792113 7fdf5b7fe6c0 Delete type=3 #205 2026/06/07-00:21:50.330389 7f15ce3fc6c0 Delete type=0 #229
2026/05/24-16:49:30.640321 7fdf5affd6c0 Level-0 table #211: started
2026/05/24-16:49:30.640388 7fdf5affd6c0 Level-0 table #211: 0 bytes OK
2026/05/24-16:49:30.668355 7fdf5affd6c0 Manual compaction at level-0 from '!items!6bmjc4MUduGs9s6n' @ 72057594037927935 : 1 .. '!items!t692JcsGHG4YJIlM' @ 0 : 0; will stop at '!items!t692JcsGHG4YJIlM' @ 49 : 1
2026/05/24-16:49:30.668380 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.671868 7fdf5affd6c0 Generated table #212@0: 8 keys, 3873 bytes
2026/05/24-16:49:30.671886 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 3873 bytes
2026/05/24-16:49:30.678060 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.678393 7fdf5affd6c0 Delete type=2 #194
2026/05/24-16:49:30.678468 7fdf5affd6c0 Delete type=2 #206
2026/05/24-16:49:30.707523 7fdf5affd6c0 Manual compaction at level-0 from '!items!t692JcsGHG4YJIlM' @ 49 : 1 .. '!items!t692JcsGHG4YJIlM' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:55.284487 7f38c97ee6c0 Recovering log #204 2026/06/07-00:01:17.527640 7f15cf3fe6c0 Recovering log #225
2026/05/24-16:10:55.284651 7f38c97ee6c0 Level-0 table #206: started 2026/06/07-00:01:17.536413 7f15cf3fe6c0 Delete type=3 #223
2026/05/24-16:10:55.296249 7f38c97ee6c0 Level-0 table #206: 3873 bytes OK 2026/06/07-00:01:17.536429 7f15cf3fe6c0 Delete type=0 #225
2026/05/24-16:10:55.307332 7f38c97ee6c0 Delete type=0 #204 2026/06/07-00:21:46.673575 7f15cdbfb6c0 Level-0 table #230: started
2026/05/24-16:10:55.307395 7f38c97ee6c0 Delete type=3 #203 2026/06/07-00:21:46.673592 7f15cdbfb6c0 Level-0 table #230: 0 bytes OK
2026/06/07-00:21:46.679599 7f15cdbfb6c0 Delete type=0 #228
2026/06/07-00:21:46.685902 7f15cdbfb6c0 Manual compaction at level-0 from '!items!6bmjc4MUduGs9s6n' @ 72057594037927935 : 1 .. '!items!t692JcsGHG4YJIlM' @ 0 : 0; will stop at (end)
Binary file not shown.
View File
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000300 MANIFEST-000323
+3 -13
View File
@@ -1,13 +1,3 @@
2026/05/24-16:13:27.770260 7fdfa95ff6c0 Recovering log #299 2026/06/07-00:21:50.310068 7f15cfbff6c0 Recovering log #321
2026/05/24-16:13:27.780183 7fdfa95ff6c0 Delete type=0 #299 2026/06/07-00:21:50.320158 7f15cfbff6c0 Delete type=3 #319
2026/05/24-16:13:27.780244 7fdfa95ff6c0 Delete type=3 #297 2026/06/07-00:21:50.320186 7f15cfbff6c0 Delete type=0 #321
2026/05/24-16:49:30.647265 7fdf5affd6c0 Level-0 table #303: started
2026/05/24-16:49:30.647318 7fdf5affd6c0 Level-0 table #303: 0 bytes OK
2026/05/24-16:49:30.678500 7fdf5affd6c0 Manual compaction at level-0 from '!items!0LlzDyCurJedqeyG' @ 72057594037927935 : 1 .. '!items!tq6mEgXog7h4VyWk' @ 0 : 0; will stop at '!items!tq6mEgXog7h4VyWk' @ 75 : 1
2026/05/24-16:49:30.678503 7fdf5affd6c0 Compacting 1@0 + 1@1 files
2026/05/24-16:49:30.681724 7fdf5affd6c0 Generated table #304@0: 15 keys, 8091 bytes
2026/05/24-16:49:30.681731 7fdf5affd6c0 Compacted 1@0 + 1@1 files => 8091 bytes
2026/05/24-16:49:30.688059 7fdf5affd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/05/24-16:49:30.688095 7fdf5affd6c0 Delete type=2 #286
2026/05/24-16:49:30.688197 7fdf5affd6c0 Delete type=2 #298
2026/05/24-16:49:30.707528 7fdf5affd6c0 Manual compaction at level-0 from '!items!tq6mEgXog7h4VyWk' @ 75 : 1 .. '!items!tq6mEgXog7h4VyWk' @ 0 : 0; will stop at (end)
+7 -5
View File
@@ -1,5 +1,7 @@
2026/05/24-16:10:55.204766 7fed9e3ff6c0 Recovering log #296 2026/06/07-00:01:17.516491 7f15cf3fe6c0 Recovering log #317
2026/05/24-16:10:55.204926 7fed9e3ff6c0 Level-0 table #298: started 2026/06/07-00:01:17.526454 7f15cf3fe6c0 Delete type=3 #315
2026/05/24-16:10:55.209216 7fed9e3ff6c0 Level-0 table #298: 8091 bytes OK 2026/06/07-00:01:17.526475 7f15cf3fe6c0 Delete type=0 #317
2026/05/24-16:10:55.219909 7fed9e3ff6c0 Delete type=0 #296 2026/06/07-00:21:46.653448 7f15cdbfb6c0 Level-0 table #322: started
2026/05/24-16:10:55.219959 7fed9e3ff6c0 Delete type=3 #295 2026/06/07-00:21:46.653485 7f15cdbfb6c0 Level-0 table #322: 0 bytes OK
2026/06/07-00:21:46.660396 7f15cdbfb6c0 Delete type=0 #320
2026/06/07-00:21:46.679667 7f15cdbfb6c0 Manual compaction at level-0 from '!items!0LlzDyCurJedqeyG' @ 72057594037927935 : 1 .. '!items!tq6mEgXog7h4VyWk' @ 0 : 0; will stop at (end)

Some files were not shown because too many files have changed in this diff Show More