feat(creation): archétypes Voyageurs + hiérarchie conteneurs + alertes
Archétypes : - Sélection par cartes cliquables à l'étape Haut-Rêve (12 voyageurs du compendium + création libre), avec image et nom - Choix haut-rêvant verrouillé quand un archétype est sélectionné - _appliquerArchetype pré-remplit caracs, beauté, attributs, compétences (par _id), sorts, équipement (par nom) et fortune (total monnaies de l'archétype) ; image token de l'archétype à la création - _resetArchetype (création libre) remet tout aux valeurs par défaut - Récapitulatif : ligne Archétype Économie des troncs : - _economieTroncs applique la règle des compétences troncs (Corps à corps/Dague/Esquive et armes) au budget de 3000 XP (le Baladin passe de 3160 à 3000 exactement) Hiérarchie des conteneurs : - La structure (dont contenu des conteneurs) est prise depuis l'item voyageur ; mapping archetypeID -> equipID -> nouvelID créé - Les system.contenu des conteneurs sont reconstruits après création (sac à dos -> 11 contenus, ceinturon -> outre -> eau, etc.) Fix budget équipement : - La condition de blocage (depenseDelta > 0 && > restant) permet les réductions même quand le budget est négatif Alertes items non mappés : - Les éléments de l'archétype non retrouvés dans les compendiums sont collectés et signalés par notification toast + bandeaux d'avertissement aux étapes Haut-Rêve et Équipement
This commit is contained in:
@@ -8737,6 +8737,50 @@ body {
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content textarea.attr-input {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid .archetype-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--color-border-light-tertiary);
|
||||
border-radius: 0.3rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid .archetype-card:hover {
|
||||
background: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid .archetype-card.selected {
|
||||
border-color: var(--wizard-accent-gold);
|
||||
background: var(--wizard-step-active);
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid .archetype-card img {
|
||||
width: 4.5rem;
|
||||
height: 4.5rem;
|
||||
object-fit: cover;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-grid .archetype-card .archetype-name {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.1;
|
||||
min-height: 2.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .archetype-lock {
|
||||
margin-left: 0.5rem;
|
||||
font-style: italic;
|
||||
color: var(--wizard-accent-dream);
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-content .equip-header {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@@ -8830,6 +8874,17 @@ body {
|
||||
width: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-warning {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-warning ul {
|
||||
margin: 0;
|
||||
padding-left: 1.2rem;
|
||||
list-style: none;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-warning li {
|
||||
padding: 0.15rem 0;
|
||||
}
|
||||
.system-foundryvtt-reve-de-dragon .rdd-creation-personnage .wizard-erreurs {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
@@ -3560,6 +3560,49 @@
|
||||
.attr-input { width: 100%; }
|
||||
textarea.attr-input { box-sizing: border-box; }
|
||||
|
||||
.archetype-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
.archetype-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--color-border-light-tertiary);
|
||||
border-radius: 0.3rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
&:hover { background: rgba(0, 0, 0, 0.12); }
|
||||
&.selected {
|
||||
border-color: var(--wizard-accent-gold);
|
||||
background: var(--wizard-step-active);
|
||||
}
|
||||
img {
|
||||
width: 4.5rem;
|
||||
height: 4.5rem;
|
||||
object-fit: cover;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
.archetype-name {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.1;
|
||||
min-height: 2.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.archetype-lock {
|
||||
margin-left: 0.5rem;
|
||||
font-style: italic;
|
||||
color: var(--wizard-accent-dream);
|
||||
}
|
||||
|
||||
.equip-header {
|
||||
margin-bottom: 0.5rem;
|
||||
.budget-labels {
|
||||
@@ -3630,6 +3673,18 @@
|
||||
}
|
||||
|
||||
/* ==================== ERREURS ==================== */
|
||||
.wizard-warning {
|
||||
margin: 0.5rem 0;
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1.2rem;
|
||||
list-style: none;
|
||||
}
|
||||
li {
|
||||
padding: 0.15rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-erreurs {
|
||||
margin: 0.5rem 0;
|
||||
ul {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { SystemCompendiums } from "../../settings/system-compendiums.js";
|
||||
import { RdDItemTete } from "../../documents/_module.mjs";
|
||||
import { Grammar } from "../../grammar.js";
|
||||
import { coutCompetenceCreation, coutSortCreation, sortAccessible, niveauxAccessibles } from "./cout-creation.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import { coutCompetenceCreation, coutPourAtteindre, coutSortCreation, sortAccessible, niveauxAccessibles } from "./cout-creation.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
@@ -17,6 +18,11 @@ const TOTAL_CARACS_CIBLE = 160;
|
||||
const TOTAL_XP_PRELIMINAIRE = 3000;
|
||||
const MAX_XP_SORTS = 300;
|
||||
const FORTUNE_DEFAUT = 50;
|
||||
|
||||
const TRONCS_COMPETENCES = [
|
||||
["Esquive", "Dague", "Corps à corps"],
|
||||
["Epée à 1 main", "Epée à 2 mains", "Hache à 1 main", "Hache à 2 mains", "Lance", "Masse à 1 main", "Masse à 2 mains"],
|
||||
];
|
||||
const NIVEAU_MAX_CREATION = 3;
|
||||
|
||||
const CARACS_PRIMAIRES = [
|
||||
@@ -63,18 +69,22 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
this.data = {
|
||||
name: "",
|
||||
hautRevant: false,
|
||||
archetype: null,
|
||||
caracs: Object.fromEntries(CARACS_PRIMAIRES.map(c => [c, 10])),
|
||||
beaute: 10,
|
||||
competences: [],
|
||||
sortsChoisis: [],
|
||||
attributs: { sexe: "", age: "", taille: "", poids: "", yeux: "", cheveux: "", main: "droitier", heure: "", biographie: "", description: "" },
|
||||
equipement: { fortune: FORTUNE_DEFAUT, selection: {}, filtreType: "tous", recherche: "" },
|
||||
equipement: { fortune: FORTUNE_DEFAUT, selection: {}, filtreType: "tous", recherche: "", nonMappes: [] },
|
||||
};
|
||||
this._competencesBase = [];
|
||||
this._sortsDispo = [];
|
||||
this._equipement = [];
|
||||
this._raceHumain = null;
|
||||
this._donHautReve = null;
|
||||
this._voyageurs = [];
|
||||
this._archetypeImg = null;
|
||||
this._archetypeIdMap = new Map();
|
||||
this._precharge = false;
|
||||
}
|
||||
|
||||
@@ -133,23 +143,33 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
if (!this._donHautReve) {
|
||||
this._donHautReve = await RdDItemTete.teteDonDeHautReve();
|
||||
}
|
||||
if (this._voyageurs.length === 0) {
|
||||
const pack = game.packs.get("foundryvtt-reve-de-dragon.voyageurs");
|
||||
if (pack) {
|
||||
this._voyageurs = await pack.getDocuments();
|
||||
}
|
||||
}
|
||||
if (this._equipement.length === 0) {
|
||||
const items = await SystemCompendiums.getItems('equipement');
|
||||
this._equipement = items
|
||||
.filter(it => it.type !== 'monnaie')
|
||||
.map(it => ({
|
||||
_id: it._id,
|
||||
name: it.name,
|
||||
type: it.type,
|
||||
img: it.img,
|
||||
system: {
|
||||
.map(it => {
|
||||
const structure = { ...it.system };
|
||||
return {
|
||||
_id: it._id,
|
||||
name: it.name,
|
||||
type: it.type,
|
||||
img: it.img,
|
||||
structure,
|
||||
system: {
|
||||
cout: Number(it.system.cout ?? 0),
|
||||
encombrement: Number(it.system.encombrement ?? 0),
|
||||
quantite: it.system.quantite ?? 1,
|
||||
},
|
||||
cout: Number(it.system.cout ?? 0),
|
||||
encombrement: Number(it.system.encombrement ?? 0),
|
||||
quantite: it.system.quantite ?? 1,
|
||||
},
|
||||
cout: Number(it.system.cout ?? 0),
|
||||
encombrement: Number(it.system.encombrement ?? 0),
|
||||
}));
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +196,113 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
}));
|
||||
}
|
||||
|
||||
async _appliquerArchetype(voyageurId) {
|
||||
const voyageur = this._voyageurs.find(v => v.id === voyageurId) || this._voyageurs.find(v => v.name === voyageurId);
|
||||
if (!voyageur) {
|
||||
this.data.archetype = null;
|
||||
return;
|
||||
}
|
||||
this.data.archetype = { id: voyageur.id, name: voyageur.name };
|
||||
const system = voyageur.system;
|
||||
|
||||
// Caractéristiques + beauté
|
||||
for (const c of CARACS_PRIMAIRES) {
|
||||
const v = system.carac?.[c]?.value;
|
||||
if (typeof v === "number") this.data.caracs[c] = v;
|
||||
}
|
||||
if (typeof system.beaute === "number") this.data.beaute = system.beaute;
|
||||
|
||||
// Attributs physiques
|
||||
for (const key of ["sexe", "age", "taille", "poids", "yeux", "cheveux", "main", "heure"]) {
|
||||
if (system[key] !== undefined && system[key] !== null) {
|
||||
this.data.attributs[key] = String(system[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Haut-rêvant : forcé par la présence du don
|
||||
const aDonHautReve = voyageur.items.some(i => i.type === ITEM_TYPES.tete && Grammar.equalsInsensitive(i.name, "Don de haut-rêve"));
|
||||
this.data.hautRevant = aDonHautReve;
|
||||
|
||||
// Compétences : mapper par _id (IDs identiques au compendium compétences)
|
||||
const itemsCompetences = voyageur.items.filter(i => i.type === 'competence');
|
||||
for (const comp of this.data.competences) {
|
||||
const arItem = itemsCompetences.find(i => i._id === comp._id) || itemsCompetences.find(i => Grammar.equalsInsensitive(i.name, comp.name));
|
||||
if (arItem && typeof arItem.system.niveau === "number") {
|
||||
comp.system.niveau = arItem.system.niveau;
|
||||
}
|
||||
}
|
||||
|
||||
// Sorts (si HR) : charger les sorts et pré-remplir
|
||||
this.data.sortsChoisis = [];
|
||||
if (this.data.hautRevant) {
|
||||
if (this._sortsDispo.length === 0) {
|
||||
this._sortsDispo = await this._chargerSorts();
|
||||
}
|
||||
const sortsArchetype = voyageur.items.filter(i => i.type === 'sort');
|
||||
this.data.equipement.nonMappes = [];
|
||||
for (const sortAr of sortsArchetype) {
|
||||
const sort = this._sortsDispo.find(s => Grammar.equalsInsensitive(s.name, sortAr.name));
|
||||
if (sort && !this.data.sortsChoisis.includes(sort._id)) {
|
||||
this.data.sortsChoisis.push(sort._id);
|
||||
} else if (!sort) {
|
||||
this.data.equipement.nonMappes.push(sortAr.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Équipement : mapper par nom, fortune = total monnaies de l'archétype.
|
||||
// La structure (dont le contenu des conteneurs) est prise depuis l'item
|
||||
// du voyageur, les coûts/encombrements depuis le compendium équipement.
|
||||
this.data.equipement.selection = {};
|
||||
this._archetypeIdMap = new Map();
|
||||
const itemsEquip = voyageur.items.filter(i => ![ITEM_TYPES.competence, ITEM_TYPES.monnaie, ITEM_TYPES.sort, ITEM_TYPES.tete].includes(i.type));
|
||||
for (const item of itemsEquip) {
|
||||
const equip = this._equipement.find(e => Grammar.equalsInsensitive(e.name, item.name));
|
||||
if (equip) {
|
||||
const quantite = Number(item.system.quantite ?? 1);
|
||||
if (quantite > 0) {
|
||||
const structure = { ...item.system };
|
||||
delete structure._id;
|
||||
this._archetypeIdMap.set(item.id, equip._id);
|
||||
this.data.equipement.selection[equip._id] = {
|
||||
item: { ...equip, structure },
|
||||
quantite,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.data.equipement.nonMappes.push(item.name);
|
||||
}
|
||||
}
|
||||
const fortune = voyageur.items
|
||||
.filter(i => i.type === ITEM_TYPES.monnaie)
|
||||
.reduce((sum, m) => sum + (Number(m.system.cout ?? 0) * Number(m.system.quantite ?? 0)), 0);
|
||||
this.data.equipement.fortune = Number(fortune.toFixed(2));
|
||||
|
||||
// Token / image
|
||||
this._archetypeImg = voyageur.img;
|
||||
|
||||
if (this.data.equipement.nonMappes.length > 0) {
|
||||
ui.notifications.warn(`Archétype « ${voyageur.name} » : ${this.data.equipement.nonMappes.length} élément(s) non retrouvé(s) dans les compendiums (${this.data.equipement.nonMappes.join(", ")}).`);
|
||||
}
|
||||
}
|
||||
|
||||
_resetArchetype() {
|
||||
this.data.archetype = null;
|
||||
this.data.caracs = Object.fromEntries(CARACS_PRIMAIRES.map(c => [c, 10]));
|
||||
this.data.beaute = 10;
|
||||
this.data.attributs = { sexe: "", age: "", taille: "", poids: "", yeux: "", cheveux: "", main: "droitier", heure: "", biographie: "", description: "" };
|
||||
this.data.hautRevant = false;
|
||||
for (const comp of this.data.competences) {
|
||||
comp.system.niveau = comp.system.base;
|
||||
}
|
||||
this.data.sortsChoisis = [];
|
||||
this.data.equipement.selection = {};
|
||||
this.data.equipement.fortune = FORTUNE_DEFAUT;
|
||||
this.data.equipement.nonMappes = [];
|
||||
this._archetypeImg = null;
|
||||
this._archetypeIdMap = new Map();
|
||||
}
|
||||
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
const el = this.element;
|
||||
@@ -197,6 +324,17 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
this._precharge = false;
|
||||
this.render({ force: true });
|
||||
}));
|
||||
el.querySelectorAll(".archetype-card").forEach(card =>
|
||||
card.addEventListener("click", async event => {
|
||||
const id = event.currentTarget.dataset.archetypeId;
|
||||
if (!id) {
|
||||
this._resetArchetype();
|
||||
} else {
|
||||
await this._appliquerArchetype(id);
|
||||
}
|
||||
this._precharge = false;
|
||||
this.render({ force: true });
|
||||
}));
|
||||
break;
|
||||
|
||||
case ETAPE_CARACTERISTIQUES:
|
||||
@@ -345,7 +483,7 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
} else {
|
||||
const budget = this._budgetEquipement();
|
||||
const depenseDelta = quantite * item.cout - (this.data.equipement.selection[itemId]?.quantite ?? 0) * item.cout;
|
||||
if (depenseDelta > budget.restant) {
|
||||
if (depenseDelta > 0 && depenseDelta > budget.restant) {
|
||||
ui.notifications.warn(`Budget dépassé : ${depenseDelta.toFixed(2)} sols nécessaires, ${budget.restant.toFixed(2)} disponibles.`);
|
||||
const input = el.querySelector(`input.equip-qte[data-item-id="${itemId}"]`);
|
||||
if (input) input.value = this.data.equipement.selection[itemId]?.quantite ?? 0;
|
||||
@@ -532,13 +670,29 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
if (c.isDraconic && !this.data.hautRevant) return sum;
|
||||
return sum + coutCompetenceCreation(c, c.system.niveau);
|
||||
}, 0);
|
||||
const economieTroncs = this._economieTroncs();
|
||||
const coutSorts = this._budgetSorts().depense;
|
||||
return {
|
||||
depense,
|
||||
restant: TOTAL_XP_PRELIMINAIRE - depense - coutSorts,
|
||||
depense: depense - economieTroncs,
|
||||
restant: TOTAL_XP_PRELIMINAIRE - (depense - economieTroncs) - coutSorts,
|
||||
};
|
||||
}
|
||||
|
||||
_economieTroncs() {
|
||||
let economie = 0;
|
||||
for (const tronc of TRONCS_COMPETENCES) {
|
||||
const couts = tronc
|
||||
.map(nom => this.data.competences.find(c => Grammar.equalsInsensitive(c.name, nom)))
|
||||
.filter(c => c)
|
||||
.map(c => coutPourAtteindre(c.system.base, Math.min(c.system.niveau, 0)))
|
||||
.sort((a, b) => a - b);
|
||||
if (couts.length > 1) {
|
||||
economie += couts.slice(0, couts.length - 1).reduce((a, b) => a + b, 0);
|
||||
}
|
||||
}
|
||||
return economie;
|
||||
}
|
||||
|
||||
_budgetSorts() {
|
||||
if (!this.data.hautRevant) return { depense: 0, restant: MAX_XP_SORTS };
|
||||
const depense = this.data.sortsChoisis.reduce((sum, id) => {
|
||||
@@ -640,6 +794,7 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
const actor = await Actor.create({
|
||||
name: this.data.name || "Nouveau personnage",
|
||||
type: 'personnage',
|
||||
img: this._archetypeImg,
|
||||
system: {
|
||||
carac: caracsData,
|
||||
beaute: this.data.beaute,
|
||||
@@ -724,18 +879,48 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
async _creerEquipementEtFortune(actor) {
|
||||
const selection = Object.values(this.data.equipement.selection).filter(sel => sel.quantite > 0);
|
||||
if (selection.length > 0) {
|
||||
const items = selection.map(sel => {
|
||||
// Créer les items individuellement (les IDs sont générés par Foundry)
|
||||
// puis mapper ancien ID compendium -> nouveau ID par nom pour reconstruire
|
||||
// la hiérarchie des conteneurs.
|
||||
const itemsCrees = [];
|
||||
for (const sel of selection) {
|
||||
const item = this._equipement.find(it => it._id === sel.item._id);
|
||||
if (!item) return null;
|
||||
return {
|
||||
if (!item) continue;
|
||||
const system = {
|
||||
quantite: sel.quantite,
|
||||
cout: item.cout,
|
||||
encombrement: item.encombrement,
|
||||
...(item.structure ? { ...item.structure } : {}),
|
||||
};
|
||||
delete system.contenu; // recréé après le mapping
|
||||
delete system._id;
|
||||
const created = await actor.createEmbeddedDocuments('Item', [{
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
img: item.img,
|
||||
system: { quantite: sel.quantite, cout: item.cout, encombrement: item.encombrement },
|
||||
};
|
||||
}).filter(it => it);
|
||||
if (items.length > 0) {
|
||||
await actor.createEmbeddedDocuments('Item', items);
|
||||
system,
|
||||
}]);
|
||||
if (created[0]) {
|
||||
itemsCrees.push({ ancienId: item._id, nouveau: created[0] });
|
||||
}
|
||||
}
|
||||
const idMap = new Map(itemsCrees.map(c => [c.ancienId, c.nouveau.id]));
|
||||
// Mapper les contenus des conteneurs vers les nouveaux IDs
|
||||
const conteneurs = Object.values(this.data.equipement.selection)
|
||||
.map(sel => sel.item)
|
||||
.filter(item => item.type === 'conteneur' && Array.isArray(item.structure?.contenu) && item.structure.contenu.length > 0);
|
||||
for (const conteneur of conteneurs) {
|
||||
const nouveauConteneur = actor.items.get(idMap.get(conteneur._id));
|
||||
if (!nouveauConteneur) continue;
|
||||
const nouveauxContenus = conteneur.structure.contenu
|
||||
.map(ancienIdEnfant => {
|
||||
const equipId = this._archetypeIdMap.get(ancienIdEnfant) ?? ancienIdEnfant;
|
||||
return idMap.get(equipId);
|
||||
})
|
||||
.filter(id => id);
|
||||
if (nouveauxContenus.length > 0) {
|
||||
await nouveauConteneur.update({ 'system.contenu': nouveauxContenus });
|
||||
}
|
||||
}
|
||||
}
|
||||
const budget = this._budgetEquipement();
|
||||
@@ -771,6 +956,7 @@ export class CreationPersonnageWizard extends HandlebarsApplicationMixin(foundry
|
||||
TOTAL_XP_PRELIMINAIRE,
|
||||
MAX_XP_SORTS,
|
||||
FORTUNE_DEFAUT,
|
||||
voyageurs: this._voyageurs.map(v => ({ id: v.id, name: v.name, img: v.img })),
|
||||
};
|
||||
switch (this.step) {
|
||||
case ETAPE_COMPETENCES:
|
||||
|
||||
@@ -21,9 +21,35 @@
|
||||
<p>Avant de commencer la création, choix fondamental : votre personnage sera-t-il haut-rêvant (magicien) ou non ? Le don de haut-rêve s'acquiert dans l'enfance ; un personnage adulte n'aura pratiquement aucune chance de le devenir ensuite.</p>
|
||||
<p>Le principe de création est identique, mais les haut-rêvants doivent réserver des points pour les compétences de magie (Draconic) et pour les sorts.</p>
|
||||
<div class="form-group">
|
||||
<label><input type="radio" name="hautRevant" value="false" {{#unless data.hautRevant}}checked{{/unless}} /> Non haut-rêvant</label>
|
||||
<label><input type="radio" name="hautRevant" value="true" {{#if data.hautRevant}}checked{{/if}} /> Haut-rêvant</label>
|
||||
<label><input type="radio" name="hautRevant" value="false" {{#unless data.hautRevant}}checked{{/unless}} {{#if data.archetype}}disabled{{/if}} /> Non haut-rêvant</label>
|
||||
<label><input type="radio" name="hautRevant" value="true" {{#if data.hautRevant}}checked{{/if}} {{#if data.archetype}}disabled{{/if}} /> Haut-rêvant</label>
|
||||
{{#if data.archetype}}<span class="archetype-lock">(archétype sélectionné : choix verrouillé)</span>{{/if}}
|
||||
</div>
|
||||
|
||||
<h3>Archétype (Voyageurs)</h3>
|
||||
<p>Vous pouvez bâtir votre personnage sur un archétype du compendium Voyageurs : ses caractéristiques, compétences, sorts, équipement et fortune seront pré-remplis, puis personnalisables.</p>
|
||||
<div class="archetype-grid">
|
||||
<div class="archetype-card {{#unless data.archetype}}selected{{/unless}}" data-archetype-id="">
|
||||
<img src="systems/foundryvtt-reve-de-dragon/icons/voyageurs/personnage.webp" alt="Aucun" />
|
||||
<span class="archetype-name">Création libre</span>
|
||||
</div>
|
||||
{{#each voyageurs}}
|
||||
<div class="archetype-card {{#if (eq ../data.archetype.id this.id)}}selected{{/if}}" data-archetype-id="{{this.id}}">
|
||||
<img src="{{this.img}}" alt="{{this.name}}" />
|
||||
<span class="archetype-name">{{this.name}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if data.equipement.nonMappes.length}}
|
||||
<div class="wizard-warning notification warning">
|
||||
<strong>Éléments de l'archétype « {{data.archetype.name}} » non retrouvés dans les compendiums :</strong>
|
||||
<ul>
|
||||
{{#each data.equipement.nonMappes}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{!-- Étape 1 : Caractéristiques --}}
|
||||
@@ -181,6 +207,16 @@
|
||||
{{#if (eq step 5)}}
|
||||
<h3>Équipement & Richesse de départ</h3>
|
||||
<p>Votre personnage commence avec une fortune de {{FORTUNE_DEFAUT}} sols (ajustable). Achetez vos vêtements, armes et équipement, puis gardez le reste en espèces.</p>
|
||||
{{#if data.equipement.nonMappes.length}}
|
||||
<div class="wizard-warning notification warning">
|
||||
<strong>Éléments de l'archétype non retrouvés dans les compendiums :</strong>
|
||||
<ul>
|
||||
{{#each data.equipement.nonMappes}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="equip-header">
|
||||
<div class="form-group">
|
||||
<label>Fortune (sols)</label>
|
||||
@@ -244,6 +280,7 @@
|
||||
<h4>Identité</h4>
|
||||
<p><strong>Nom :</strong> {{data.name}}</p>
|
||||
<p><strong>Haut-rêvant :</strong> {{#if data.hautRevant}}Oui{{else}}Non{{/if}}</p>
|
||||
{{#if data.archetype}}<p><strong>Archétype :</strong> {{data.archetype.name}}</p>{{/if}}
|
||||
</div>
|
||||
<div class="recap-section">
|
||||
<h4>Caractéristiques</h4>
|
||||
|
||||
Reference in New Issue
Block a user