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:
@@ -1,17 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Data model pour les armes MournbladeCYD2
|
* 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() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
...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 }),
|
|
||||||
typearme: new fields.StringField({ initial: "" }),
|
typearme: new fields.StringField({ initial: "" }),
|
||||||
armenaturelle: new fields.BooleanField({ initial: false }),
|
armenaturelle: new fields.BooleanField({ initial: false }),
|
||||||
armefortune: new fields.BooleanField({ initial: false }),
|
armefortune: new fields.BooleanField({ initial: false }),
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Data model pour les équipements MournbladeCYD2
|
* 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() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
...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 })
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Data model pour les monnaies MournbladeCYD2
|
* 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() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
...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 })
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Data model pour les protections MournbladeCYD2
|
* 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() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
...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 }),
|
|
||||||
protection: new fields.NumberField({ initial: 0, integer: true }),
|
protection: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
adversitepoids: new fields.NumberField({ initial: 0, integer: true })
|
adversitepoids: new fields.NumberField({ initial: 0, integer: true })
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -634,9 +634,7 @@ export class MournbladeCYD2Actor extends Actor {
|
|||||||
let valueSC = 0
|
let valueSC = 0
|
||||||
for (let monnaie of this.items) {
|
for (let monnaie of this.items) {
|
||||||
if (monnaie.type == "monnaie") {
|
if (monnaie.type == "monnaie") {
|
||||||
valueSC += Number(monnaie.system.prixsc) * Number(monnaie.system.quantite)
|
valueSC += MournbladeCYD2Utility.getItemValueSC(monnaie)
|
||||||
valueSC += (Number(monnaie.system.prixca) * Number(monnaie.system.quantite)) * 10
|
|
||||||
valueSC += (Number(monnaie.system.prixpo) * Number(monnaie.system.quantite)) * 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
|
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
|
||||||
@@ -647,9 +645,7 @@ export class MournbladeCYD2Actor extends Actor {
|
|||||||
let valueSC = 0
|
let valueSC = 0
|
||||||
for (let equip of this.items) {
|
for (let equip of this.items) {
|
||||||
if (equip.type == "equipement" || equip.type == "arme" || equip.type == "protection") {
|
if (equip.type == "equipement" || equip.type == "arme" || equip.type == "protection") {
|
||||||
valueSC += Number(equip.system.prixsc) * Number(equip.system.quantite ?? 1)
|
valueSC += MournbladeCYD2Utility.getItemValueSC(equip)
|
||||||
valueSC += (Number(equip.system.prixca) * Number(equip.system.quantite ?? 1)) * 10
|
|
||||||
valueSC += (Number(equip.system.prixpo) * Number(equip.system.quantite ?? 1)) * 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
|
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
|
||||||
|
|||||||
@@ -54,13 +54,49 @@ export class MournbladeCYD2Utility {
|
|||||||
// Conversion selon le lore Mournblade :
|
// Conversion selon le lore Mournblade :
|
||||||
// 1 SA (Sou d'Argent / Pièce d'Argent) = 10 PB (Pièces de Bronze / Sous de Cuivre)
|
// 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
|
// 1 PO (Pièce d'Or) = 10 SA = 100 PB
|
||||||
// Donc : 1 CA = 10 SC, 1 PO = 100 SC
|
// Donc : 1 PA (Pièce d'Argent) = 10 SC, 1 PO (Pièce d'Or) = 100 SC
|
||||||
Handlebars.registerHelper('calculateItemValueSC', function (prixpo, prixca, prixsc) {
|
static calculateItemValueSC(prixpo, prixca, prixsc) {
|
||||||
const po = parseInt(prixpo) || 0;
|
const po = parseInt(prixpo) || 0;
|
||||||
const ca = parseInt(prixca) || 0;
|
const ca = parseInt(prixca) || 0;
|
||||||
const sc = parseInt(prixsc) || 0;
|
const sc = parseInt(prixsc) || 0;
|
||||||
return po * 100 + ca * 10 + sc;
|
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) {
|
Handlebars.registerHelper('select', function(value, options) {
|
||||||
const html = options.fn(this);
|
const html = options.fn(this);
|
||||||
|
|||||||
@@ -273,11 +273,7 @@
|
|||||||
data-attr-key="tochoose">{{skill.name}}</a></span>
|
data-attr-key="tochoose">{{skill.name}}</a></span>
|
||||||
|
|
||||||
<span class="predilection-text">
|
<span class="predilection-text">
|
||||||
{{#each skill.system.predilections as |pred key|}}
|
{{joinPredilections skill.system.predilections}}
|
||||||
{{#if (and pred.acquise (not pred.used))}}
|
|
||||||
{{pred.name}}{{#unless (eq key (subtract skill.system.predilections.length 1))}}, {{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -336,7 +332,7 @@
|
|||||||
<li class="item flexrow " data-item-id="{{don._id}}" data-item-type="don">
|
<li class="item flexrow " data-item-id="{{don._id}}" data-item-type="don">
|
||||||
<img class="item-name-img" src="{{don.img}}" />
|
<img class="item-name-img" src="{{don.img}}" />
|
||||||
<span class="item-name-label competence-name">{{don.name}}</span>
|
<span class="item-name-label competence-name">{{don.name}}</span>
|
||||||
<span class="item-field-label-short">{{don.system.allegeance}}</span>
|
<span class="item-field-label-short">{{localizeAllegiance don.system.allegeance}}</span>
|
||||||
<div class="item-filler"> </div>
|
<div class="item-filler"> </div>
|
||||||
<div class="item-controls item-controls-fixed">
|
<div class="item-controls item-controls-fixed">
|
||||||
<a class="item-control" data-action="editItem" title="Edit Item"><i class="fas fa-edit"></i></a>
|
<a class="item-control" data-action="editItem" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||||
@@ -365,7 +361,7 @@
|
|||||||
<li class="item flexrow " data-item-id="{{pacte._id}}" data-item-type="pacte">
|
<li class="item flexrow " data-item-id="{{pacte._id}}" data-item-type="pacte">
|
||||||
<img class="item-name-img" src="{{pacte.img}}" />
|
<img class="item-name-img" src="{{pacte.img}}" />
|
||||||
<span class="item-name-label competence-name">{{pacte.name}}</span>
|
<span class="item-name-label competence-name">{{pacte.name}}</span>
|
||||||
<span class="item-field-label-short">{{pacte.system.allegeance}}</span>
|
<span class="item-field-label-short">{{localizeAllegiance pacte.system.allegeance}}</span>
|
||||||
<div class="item-filler"> </div>
|
<div class="item-filler"> </div>
|
||||||
<div class="item-controls item-controls-fixed">
|
<div class="item-controls item-controls-fixed">
|
||||||
<a class="item-control" data-action="editItem" title="Edit Item"><i class="fas fa-edit"></i></a>
|
<a class="item-control" data-action="editItem" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||||
|
|||||||
@@ -266,9 +266,7 @@
|
|||||||
<a data-action="rollCompetence" data-attr-key="tochoose">{{skill.name}}</a>
|
<a data-action="rollCompetence" data-attr-key="tochoose">{{skill.name}}</a>
|
||||||
</span>
|
</span>
|
||||||
<span class="predilection-text">
|
<span class="predilection-text">
|
||||||
{{#each skill.system.predilections as |pred key|}}
|
{{joinPredilections skill.system.predilections}}
|
||||||
{{#if (and pred.acquise (not pred.used))}}{{pred.name}}{{#unless (eq key (subtract skill.system.predilections.length 1))}}, {{/unless}}{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<select class="status-small-label color-class-common edit-item-data competence-niveau"
|
<select class="status-small-label color-class-common edit-item-data competence-niveau"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<section>
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-header.hbs}}
|
||||||
|
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-nav.hbs}}
|
||||||
|
|
||||||
|
{{!-- Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-description.hbs}}
|
||||||
|
|
||||||
|
<div class="tab details" data-group="primary" data-tab="details">
|
||||||
|
{{! Content to be overridden by specific item templates }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
@@ -24,4 +24,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="flexrow item">
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label item-field-label-long">Prix </label>
|
||||||
<label class="generic-label item-field-label-long">Prix (PA) </label>
|
|
||||||
|
|
||||||
<input type="text" class="numeric-input item-field-label-short" name="system.prixpo" value="{{system.prixpo}}"
|
<input type="text" class="numeric-input item-field-label-short" name="system.prixpo" value="{{system.prixpo}}"
|
||||||
data-dtype="Number" />
|
data-dtype="Number" />
|
||||||
@@ -30,4 +29,8 @@
|
|||||||
<input type="text" class="numeric-input item-field-label-short" name="system.prixsc" value="{{system.prixsc}}"
|
<input type="text" class="numeric-input item-field-label-short" name="system.prixsc" value="{{system.prixsc}}"
|
||||||
data-dtype="Number" />
|
data-dtype="Number" />
|
||||||
<label class="generic-label item-field-label-short">SC </label>
|
<label class="generic-label item-field-label-short">SC </label>
|
||||||
|
</li>
|
||||||
|
<li class="flexrow item">
|
||||||
|
<label class="generic-label item-field-label-long">Valeur totale </label>
|
||||||
|
<span class="item-field-label-long">{{calculateItemValueSC system.prixpo system.prixca system.prixsc}} SC</span>
|
||||||
</li>
|
</li>
|
||||||
Reference in New Issue
Block a user