Second round de corrections et améliorations
This commit is contained in:
231
src/module/applications/sheets/creature-sheet.mjs
Normal file
231
src/module/applications/sheets/creature-sheet.mjs
Normal file
@@ -0,0 +1,231 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
|
||||
/** Convert Traveller dice notation (e.g. "2D", "4D+2", "3D6") to FoundryVTT formula */
|
||||
function normalizeDice(formula) {
|
||||
if (!formula) return "1d6";
|
||||
return formula
|
||||
.replace(/(\d*)D(\d*)([+-]\d+)?/gi, (_, count, sides, mod) => {
|
||||
const n = count || "1";
|
||||
const d = sides || "6";
|
||||
return mod ? `${n}d${d}${mod}` : `${n}d${d}`;
|
||||
});
|
||||
}
|
||||
|
||||
export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "creature", "nopad"],
|
||||
position: {
|
||||
width: 720,
|
||||
height: 600,
|
||||
},
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "TYPES.Actor.creature",
|
||||
},
|
||||
actions: {
|
||||
...super.DEFAULT_OPTIONS.actions,
|
||||
rollAttack: TravellerCreatureSheet.#onRollAttack,
|
||||
rollSkill: TravellerCreatureSheet.#onRollSkill,
|
||||
addSkill: TravellerCreatureSheet.#onAddRow,
|
||||
deleteSkill: TravellerCreatureSheet.#onDeleteRow,
|
||||
addAttack: TravellerCreatureSheet.#onAddRow,
|
||||
deleteAttack: TravellerCreatureSheet.#onDeleteRow,
|
||||
addTrait: TravellerCreatureSheet.#onAddRow,
|
||||
deleteTrait: TravellerCreatureSheet.#onDeleteRow,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/mgt2/templates/actors/creature-sheet.html",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "skills" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
|
||||
context.sizeLabel = this._getSizeLabel(actor.system.life.max);
|
||||
context.sizeTraitLabel = this._getSizeTrait(actor.system.life.max);
|
||||
context.config = CONFIG.MGT2;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_getSizeLabel(pdv) {
|
||||
if (pdv <= 2) return "Souris / Rat";
|
||||
if (pdv <= 5) return "Chat";
|
||||
if (pdv <= 7) return "Blaireau / Chien";
|
||||
if (pdv <= 13) return "Chimpanzé / Chèvre";
|
||||
if (pdv <= 28) return "Humain";
|
||||
if (pdv <= 35) return "Vache / Cheval";
|
||||
if (pdv <= 49) return "Requin";
|
||||
if (pdv <= 70) return "Rhinocéros";
|
||||
if (pdv <= 90) return "Éléphant";
|
||||
if (pdv <= 125) return "Carnosaure";
|
||||
return "Sauropode / Baleine";
|
||||
}
|
||||
|
||||
_getSizeTrait(pdv) {
|
||||
if (pdv <= 2) return "Petit (−4)";
|
||||
if (pdv <= 5) return "Petit (−3)";
|
||||
if (pdv <= 7) return "Petit (−2)";
|
||||
if (pdv <= 13) return "Petit (−1)";
|
||||
if (pdv <= 28) return "—";
|
||||
if (pdv <= 35) return "Grand (+1)";
|
||||
if (pdv <= 49) return "Grand (+2)";
|
||||
if (pdv <= 70) return "Grand (+3)";
|
||||
if (pdv <= 90) return "Grand (+4)";
|
||||
if (pdv <= 125) return "Grand (+5)";
|
||||
return "Grand (+6)";
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── Roll Handlers
|
||||
|
||||
/** Roll an attack (damage) with optional difficulty dialog */
|
||||
static async #onRollAttack(event, target) {
|
||||
const index = parseInt(target.dataset.index ?? 0);
|
||||
const actor = this.document;
|
||||
const attack = actor.system.attacks[index];
|
||||
if (!attack) return;
|
||||
|
||||
const rollFormula = normalizeDice(attack.damage);
|
||||
|
||||
const roll = await new Roll(rollFormula).evaluate();
|
||||
const total = roll.total;
|
||||
|
||||
await roll.toMessage({
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
flavor: `<strong>${actor.name}</strong> — ${attack.name}`,
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
});
|
||||
}
|
||||
|
||||
/** Roll a skill check (2d6 + level vs difficulty) */
|
||||
static async #onRollSkill(event, target) {
|
||||
const index = parseInt(target.dataset.index ?? 0);
|
||||
const actor = this.document;
|
||||
const skill = actor.system.skills[index];
|
||||
if (!skill) return;
|
||||
|
||||
const htmlContent = await renderTemplate(
|
||||
"systems/mgt2/templates/actors/creature-roll-prompt.html",
|
||||
{
|
||||
skillName: skill.name,
|
||||
skillLevel: skill.level,
|
||||
config: CONFIG.MGT2
|
||||
}
|
||||
);
|
||||
|
||||
const result = await foundry.applications.api.DialogV2.wait({
|
||||
window: { title: game.i18n.localize("MGT2.Creature.RollSkill") + " — " + skill.name },
|
||||
content: htmlContent,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
{
|
||||
action: "boon",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Boon"),
|
||||
callback: (event, button, dialog) => {
|
||||
const fd = new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object;
|
||||
fd.diceModifier = "dl";
|
||||
return fd;
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "roll",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Roll"),
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
default: true,
|
||||
callback: (event, button, dialog) =>
|
||||
new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object
|
||||
},
|
||||
{
|
||||
action: "bane",
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Bane"),
|
||||
callback: (event, button, dialog) => {
|
||||
const fd = new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object;
|
||||
fd.diceModifier = "dh";
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!result) return;
|
||||
|
||||
const dm = parseInt(result.dm ?? 0) + (skill.level ?? 0);
|
||||
const modifier = result.diceModifier ?? "";
|
||||
const difficultyTarget = parseInt(result.difficulty ?? 8);
|
||||
const difficultyLabel = result.difficultyLabel ?? "";
|
||||
|
||||
const diceFormula = modifier ? `3d6${modifier}` : "2d6";
|
||||
const fullFormula = dm !== 0 ? `${diceFormula} + ${dm}` : diceFormula;
|
||||
|
||||
const roll = await new Roll(fullFormula).evaluate();
|
||||
const success = roll.total >= difficultyTarget;
|
||||
|
||||
const chatData = {
|
||||
creatureName: actor.name,
|
||||
creatureImg: actor.img,
|
||||
rollLabel: skill.name.toUpperCase(),
|
||||
formula: fullFormula,
|
||||
total: roll.total,
|
||||
tooltip: await roll.getTooltip(),
|
||||
difficulty: difficultyTarget,
|
||||
difficultyLabel,
|
||||
success,
|
||||
failure: !success,
|
||||
modifiers: dm !== 0 ? [`DM ${dm >= 0 ? "+" : ""}${dm}`] : [],
|
||||
};
|
||||
|
||||
const chatContent = await renderTemplate(
|
||||
"systems/mgt2/templates/chat/creature-roll.html",
|
||||
chatData
|
||||
);
|
||||
|
||||
await ChatMessage.create({
|
||||
content: chatContent,
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
rolls: [roll],
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
});
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── CRUD Handlers
|
||||
|
||||
static async #onAddRow(event, target) {
|
||||
const prop = target.dataset.prop;
|
||||
if (!prop) return;
|
||||
const actor = this.document;
|
||||
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
|
||||
arr.push(this._getDefaultRow(prop));
|
||||
await actor.update({ [`system.${prop}`]: arr });
|
||||
}
|
||||
|
||||
static async #onDeleteRow(event, target) {
|
||||
const prop = target.dataset.prop;
|
||||
const index = parseInt(target.dataset.index);
|
||||
if (!prop || isNaN(index)) return;
|
||||
const actor = this.document;
|
||||
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
|
||||
arr.splice(index, 1);
|
||||
await actor.update({ [`system.${prop}`]: arr });
|
||||
}
|
||||
|
||||
_getDefaultRow(prop) {
|
||||
switch (prop) {
|
||||
case "skills": return { name: "", level: 0, note: "" };
|
||||
case "attacks": return { name: "", damage: "1D", description: "" };
|
||||
case "traits": return { name: "", value: "", description: "" };
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user