Refactor: Eliminate code duplication for currency conversion and item pricing

- Add static calculateItemValueSC() and getItemValueSC() helpers to MournbladeCYD2Utility
  to centralize currency conversion logic (1 PO = 100 SC, 1 PA = 10 SC)
- Refactor computeRichesse() and computeValeurEquipement() in Actor to use shared helpers
- Add localizeAllegiance Handlebars helper to display allegiance values localized
  (tous->Tous, chaos->Chaos, loi->Loi, betes->Bêtes, elementaires->Élémentaires)
- Add joinPredilections helper to fix comma display after single Predilection
- Create BaseItemWithPriceDataModel base class for items with pricing fields
  (prixpo, prixca, prixsc, rarete, quantite, equipped)
- Update arme, equipement, protection, monnaie models to extend base class
- Update actor-sheet and creature-sheet templates to use new helpers
- Update partial-item-prix.hbs to display total item value in SC
- Add item-base-sheet.hbs template for future item sheet inheritance

Fixes:
- Allegiance values now display properly localized in Dons & Pactes tabs
- Predilections no longer show trailing comma with single entry
- Equipment value totals now update correctly when items are added/modified
- Currency conversion logic centralized and consistent across the system

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-06-07 20:52:46 +02:00
parent 0d4bd37f30
commit 4c33607b2b
12 changed files with 105 additions and 53 deletions
+4 -8
View File
@@ -1,17 +1,13 @@
/**
* Data model pour les armes MournbladeCYD2
*/
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
import { BaseItemWithPriceDataModel } from "./base-item.mjs";
export default class ArmeDataModel extends BaseItemWithPriceDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false }),
...super.defineSchema(),
typearme: new fields.StringField({ initial: "" }),
armenaturelle: new fields.BooleanField({ initial: false }),
armefortune: new fields.BooleanField({ initial: false }),
+18
View File
@@ -9,3 +9,21 @@ export default class BaseItemDataModel extends foundry.abstract.TypeDataModel {
};
}
}
/**
* Data model de base pour les items avec prix MournbladeCYD2
*/
export class BaseItemWithPriceDataModel extends BaseItemDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}
+4 -8
View File
@@ -1,17 +1,13 @@
/**
* Data model pour les équipements MournbladeCYD2
*/
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
import { BaseItemWithPriceDataModel } from "./base-item.mjs";
export default class EquipementDataModel extends BaseItemWithPriceDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
...super.defineSchema()
};
}
}
+4 -8
View File
@@ -1,17 +1,13 @@
/**
* Data model pour les monnaies MournbladeCYD2
*/
export default class MonnaieDataModel extends foundry.abstract.TypeDataModel {
import { BaseItemWithPriceDataModel } from "./base-item.mjs";
export default class MonnaieDataModel extends BaseItemWithPriceDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
...super.defineSchema()
};
}
}
+4 -8
View File
@@ -1,17 +1,13 @@
/**
* Data model pour les protections MournbladeCYD2
*/
export default class ProtectionDataModel extends foundry.abstract.TypeDataModel {
import { BaseItemWithPriceDataModel } from "./base-item.mjs";
export default class ProtectionDataModel extends BaseItemWithPriceDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false }),
...super.defineSchema(),
protection: new fields.NumberField({ initial: 0, integer: true }),
adversitepoids: new fields.NumberField({ initial: 0, integer: true })
};
+2 -6
View File
@@ -634,9 +634,7 @@ export class MournbladeCYD2Actor extends Actor {
let valueSC = 0
for (let monnaie of this.items) {
if (monnaie.type == "monnaie") {
valueSC += Number(monnaie.system.prixsc) * Number(monnaie.system.quantite)
valueSC += (Number(monnaie.system.prixca) * Number(monnaie.system.quantite)) * 10
valueSC += (Number(monnaie.system.prixpo) * Number(monnaie.system.quantite)) * 100
valueSC += MournbladeCYD2Utility.getItemValueSC(monnaie)
}
}
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
@@ -647,9 +645,7 @@ export class MournbladeCYD2Actor extends Actor {
let valueSC = 0
for (let equip of this.items) {
if (equip.type == "equipement" || equip.type == "arme" || equip.type == "protection") {
valueSC += Number(equip.system.prixsc) * Number(equip.system.quantite ?? 1)
valueSC += (Number(equip.system.prixca) * Number(equip.system.quantite ?? 1)) * 10
valueSC += (Number(equip.system.prixpo) * Number(equip.system.quantite ?? 1)) * 100
valueSC += MournbladeCYD2Utility.getItemValueSC(equip)
}
}
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
+38 -2
View File
@@ -54,13 +54,49 @@ export class MournbladeCYD2Utility {
// Conversion selon le lore Mournblade :
// 1 SA (Sou d'Argent / Pièce d'Argent) = 10 PB (Pièces de Bronze / Sous de Cuivre)
// 1 PO (Pièce d'Or) = 10 SA = 100 PB
// Donc : 1 CA = 10 SC, 1 PO = 100 SC
Handlebars.registerHelper('calculateItemValueSC', function (prixpo, prixca, prixsc) {
// Donc : 1 PA (Pièce d'Argent) = 10 SC, 1 PO (Pièce d'Or) = 100 SC
static calculateItemValueSC(prixpo, prixca, prixsc) {
const po = parseInt(prixpo) || 0;
const ca = parseInt(prixca) || 0;
const sc = parseInt(prixsc) || 0;
return po * 100 + ca * 10 + sc;
}
// Helper Handlebars pour les templates
Handlebars.registerHelper('calculateItemValueSC', function (prixpo, prixca, prixsc) {
return MournbladeCYD2Utility.calculateItemValueSC(prixpo, prixca, prixsc);
});
// Helper pour localiser les valeurs d'allégeance
Handlebars.registerHelper('localizeAllegiance', function (value) {
if (!value) return value;
const allegianceMap = {
'tous': 'MNBL.all',
'chaos': 'MNBL.chaos',
'loi': 'MNBL.law',
'betes': 'MNBL.betes',
'elementaires': 'MNBL.elementaires',
'balance': 'MNBL.balance'
};
const key = allegianceMap[value?.toLowerCase?.()] || value;
return game.i18n?.localize?.(key) ?? value;
});
// Helper pour joindre les prédilections sans virgule superflue
Handlebars.registerHelper('joinPredilections', function (predilections) {
if (!predilections || !Array.isArray(predilections)) return '';
return predilections
.filter(pred => pred && pred.acquise && !pred.used)
.map(pred => pred.name)
.join(', ');
});
// Helper pour calculer la valeur SC d'un item avec quantité
static getItemValueSC(item) {
const value = this.calculateItemValueSC(item.system?.prixpo, item.system?.prixca, item.system?.prixsc);
const quantity = item.system?.quantite || 1;
return value * quantity;
}
Handlebars.registerHelper('select', function(value, options) {
const html = options.fn(this);