Ajout des fonctions de gestion des soins
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
import { RollPromptHelper } from "../../roll-prompt.js";
|
||||
import { MGT2Helper } from "../../helper.js";
|
||||
|
||||
const { renderTemplate } = foundry.applications.handlebars;
|
||||
|
||||
/** Convert Traveller dice notation (e.g. "2D", "4D+2", "3D6") to FoundryVTT formula */
|
||||
function normalizeDice(formula) {
|
||||
@@ -46,13 +50,16 @@ export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "skills" }
|
||||
tabGroups = { primary: "combat" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
const enrich = (html) => foundry.applications.ux.TextEditor.implementation.enrichHTML(html ?? "", { async: true });
|
||||
|
||||
context.enrichedBiography = await enrich(actor.system.biography);
|
||||
context.enrichedNotes = await enrich(actor.system.notes);
|
||||
context.sizeLabel = this._getSizeLabel(actor.system.life.max);
|
||||
context.sizeTraitLabel = this._getSizeTrait(actor.system.life.max);
|
||||
context.config = CONFIG.MGT2;
|
||||
@@ -88,101 +95,36 @@ export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
return "Grand (+6)";
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── Roll Handlers
|
||||
// ───────────────────────────────────────────────────────── Roll Helpers
|
||||
|
||||
/** 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;
|
||||
static async #postCreatureRoll({ actor, roll, rollLabel, dm, difficulty, difficultyLabel, rollMode, extraTooltip }) {
|
||||
const diffTarget = MGT2Helper.getDifficultyValue(difficulty ?? "Average");
|
||||
const hasDifficulty = !!difficulty;
|
||||
const success = hasDifficulty ? roll.total >= diffTarget : true;
|
||||
const effect = roll.total - diffTarget;
|
||||
const effectStr = (effect >= 0 ? "+" : "") + effect;
|
||||
|
||||
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 diceRawTotal = roll.dice.reduce((s, d) => s + d.total, 0);
|
||||
const breakdownParts = [game.i18n.localize("MGT2.Chat.Roll.Dice") + " " + diceRawTotal];
|
||||
if (dm !== 0) breakdownParts.push(`DM ${dm >= 0 ? "+" : ""}${dm}`);
|
||||
if (hasDifficulty) breakdownParts.push(game.i18n.localize("MGT2.Chat.Roll.Effect") + " " + effectStr);
|
||||
if (extraTooltip) breakdownParts.push(extraTooltip);
|
||||
const rollBreakdown = breakdownParts.join(" | ");
|
||||
|
||||
const chatData = {
|
||||
creatureName: actor.name,
|
||||
creatureImg: actor.img,
|
||||
rollLabel: skill.name.toUpperCase(),
|
||||
formula: fullFormula,
|
||||
rollLabel,
|
||||
formula: roll.formula,
|
||||
total: roll.total,
|
||||
tooltip: await roll.getTooltip(),
|
||||
difficulty: difficultyTarget,
|
||||
difficultyLabel,
|
||||
success,
|
||||
failure: !success,
|
||||
rollBreakdown,
|
||||
difficulty: hasDifficulty ? diffTarget : null,
|
||||
difficultyLabel: difficultyLabel ?? MGT2Helper.getDifficultyDisplay(difficulty),
|
||||
success: hasDifficulty ? success : null,
|
||||
failure: hasDifficulty ? !success : null,
|
||||
effect: hasDifficulty ? effect : null,
|
||||
effectStr: hasDifficulty ? effectStr : null,
|
||||
modifiers: dm !== 0 ? [`DM ${dm >= 0 ? "+" : ""}${dm}`] : [],
|
||||
};
|
||||
|
||||
@@ -195,8 +137,125 @@ export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
content: chatContent,
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
rolls: [roll],
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
rollMode: rollMode ?? game.settings.get("core", "rollMode"),
|
||||
});
|
||||
|
||||
return { success, effect, total: roll.total };
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── Roll Handlers
|
||||
|
||||
/** Roll a skill check (2d6 + level vs difficulty) — uses unified dialog */
|
||||
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 result = await RollPromptHelper.roll({
|
||||
isCreature: true,
|
||||
showSkillSelector: false,
|
||||
skillName: skill.name,
|
||||
skillLevel: skill.level,
|
||||
difficulty: "Average",
|
||||
title: game.i18n.localize("MGT2.Creature.RollSkill") + " — " + skill.name,
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
const customDM = parseInt(result.customDM ?? "0", 10) || 0;
|
||||
const skillLevel = parseInt(skill.level ?? 0, 10) || 0;
|
||||
const dm = skillLevel + customDM;
|
||||
const diceModifier = result.diceModifier ?? "";
|
||||
|
||||
// Build formula exactly like character-sheet: parts joined without spaces
|
||||
const parts = [];
|
||||
if (diceModifier) {
|
||||
parts.push("3d6", diceModifier);
|
||||
} else {
|
||||
parts.push("2d6");
|
||||
}
|
||||
if (dm !== 0) parts.push(MGT2Helper.getFormulaDM(dm));
|
||||
const fullFormula = parts.join("");
|
||||
|
||||
const roll = await new Roll(fullFormula).evaluate();
|
||||
const rollLabel = `${skill.name.toUpperCase()} (${skillLevel >= 0 ? "+" : ""}${skillLevel})`;
|
||||
|
||||
const tooltipParts = [`Dés: ${roll.dice.reduce((s, d) => s + d.total, 0)}`];
|
||||
if (skillLevel !== 0) tooltipParts.push(`${skill.name} ${skillLevel >= 0 ? "+" : ""}${skillLevel}`);
|
||||
if (customDM !== 0) tooltipParts.push(`MD perso ${customDM >= 0 ? "+" : ""}${customDM}`);
|
||||
|
||||
await TravellerCreatureSheet.#postCreatureRoll({
|
||||
actor, roll, rollLabel,
|
||||
dm,
|
||||
difficulty: result.difficulty,
|
||||
rollMode: result.rollMode,
|
||||
extraTooltip: tooltipParts.join(" | "),
|
||||
});
|
||||
}
|
||||
|
||||
/** Roll an attack: dialog with skill selector, then roll 2d6+skill+DM vs difficulty; on success roll damage */
|
||||
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 skills = actor.system.skills ?? [];
|
||||
|
||||
const result = await RollPromptHelper.roll({
|
||||
isCreature: true,
|
||||
showSkillSelector: true,
|
||||
creatureSkills: skills,
|
||||
selectedSkillIndex: attack.skill ?? -1,
|
||||
difficulty: "Average",
|
||||
title: game.i18n.localize("MGT2.Creature.RollAttack") + " — " + attack.name,
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
const skillIndex = parseInt(result.creatureSkillIndex ?? "-1", 10);
|
||||
const chosenSkill = (skillIndex >= 0 && skillIndex < skills.length) ? skills[skillIndex] : null;
|
||||
const skillLevel = parseInt(chosenSkill?.level ?? 0, 10) || 0;
|
||||
const customDM = parseInt(result.customDM ?? "0", 10) || 0;
|
||||
const dm = skillLevel + customDM;
|
||||
const diceModifier = result.diceModifier ?? "";
|
||||
|
||||
// Build formula exactly like character-sheet: parts joined without spaces
|
||||
const parts = [];
|
||||
if (diceModifier) {
|
||||
parts.push("3d6", diceModifier);
|
||||
} else {
|
||||
parts.push("2d6");
|
||||
}
|
||||
if (dm !== 0) parts.push(MGT2Helper.getFormulaDM(dm));
|
||||
const fullFormula = parts.join("");
|
||||
|
||||
const roll = await new Roll(fullFormula).evaluate();
|
||||
const rollLabel = chosenSkill
|
||||
? `${attack.name} — ${chosenSkill.name} (${skillLevel >= 0 ? "+" : ""}${skillLevel})`
|
||||
: attack.name;
|
||||
|
||||
const tooltipParts = [`Dés: ${roll.dice.reduce((s, d) => s + d.total, 0)}`];
|
||||
if (chosenSkill) tooltipParts.push(`${chosenSkill.name} ${skillLevel >= 0 ? "+" : ""}${skillLevel}`);
|
||||
if (customDM !== 0) tooltipParts.push(`MD perso ${customDM >= 0 ? "+" : ""}${customDM}`);
|
||||
|
||||
const { success } = await TravellerCreatureSheet.#postCreatureRoll({
|
||||
actor, roll, rollLabel,
|
||||
dm,
|
||||
difficulty: result.difficulty,
|
||||
rollMode: result.rollMode,
|
||||
extraTooltip: tooltipParts.join(" | "),
|
||||
});
|
||||
|
||||
// Roll damage only on success
|
||||
if (success && attack.damage) {
|
||||
const dmgFormula = normalizeDice(attack.damage);
|
||||
const dmgRoll = await new Roll(dmgFormula).evaluate();
|
||||
await dmgRoll.toMessage({
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
flavor: `<strong>${actor.name}</strong> — ${game.i18n.localize("MGT2.Chat.Weapon.Damage")}: ${attack.name} (${attack.damage})`,
|
||||
rollMode: result.rollMode ?? game.settings.get("core", "rollMode"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────── CRUD Handlers
|
||||
@@ -223,7 +282,7 @@ export default class TravellerCreatureSheet extends MGT2ActorSheet {
|
||||
_getDefaultRow(prop) {
|
||||
switch (prop) {
|
||||
case "skills": return { name: "", level: 0, note: "" };
|
||||
case "attacks": return { name: "", damage: "1D", description: "" };
|
||||
case "attacks": return { name: "", damage: "1D", skill: -1, description: "" };
|
||||
case "traits": return { name: "", value: "", description: "" };
|
||||
default: return {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user