diff --git a/module/burningwheel.ts b/module/burningwheel.ts index dd3b80cc..f4a54e98 100644 --- a/module/burningwheel.ts +++ b/module/burningwheel.ts @@ -5,6 +5,7 @@ import { RegisterItemSheets } from "./items/item.js"; +import { hideChatButtonsIfNotOwner, onChatLogRender } from "./chat.js"; import { slugify } from "./helpers.js"; import { preloadHandlebarsTemplates } from "./templates.js"; @@ -77,3 +78,7 @@ function registerHelpers() { return slugify(value.toLowerCase()); }); } + + +Hooks.on("renderChatLog", (_app, html: JQuery, _data) => onChatLogRender(html)); +Hooks.on("renderChatMessage", (app, html, data) => hideChatButtonsIfNotOwner(app, html, data)); \ No newline at end of file diff --git a/module/chat.ts b/module/chat.ts new file mode 100644 index 00000000..4128ee86 --- /dev/null +++ b/module/chat.ts @@ -0,0 +1,23 @@ +/** + * Chat message helpers + */ +import { handleFateReroll } from "./rolls.js"; + +/** + * Binds buttons in chat log to perform actions + * @param html rendered html of the chat long + */ +export function onChatLogRender(html: JQuery) { + html.on('click', 'button.chat-fate-button', (e) => handleFateReroll(e.target)); +} + +export function hideChatButtonsIfNotOwner(_message: unknown, html: JQuery, data: any) { + const message = html.find("div.chat-message"); + if (message.length > 0) { + const actor = game.actors.get(data.message.speaker.actor); + if (actor && actor.owner) { + return; // we are the owner of the message and shouldn't hide the buttons + } + message.find('div.chat-fate-reroll').each((i, b) => { b.style.display = "none"; }); + } +} diff --git a/module/rolls.ts b/module/rolls.ts index 90264654..3cae5119 100644 --- a/module/rolls.ts +++ b/module/rolls.ts @@ -32,6 +32,14 @@ export async function handleRollable( } } +export async function handleFateReroll(target: HTMLButtonElement): Promise { + return null; +} + + +/* ================================================= */ +/* Private Roll Handlers */ +/* ================================================= */ async function handleShrugRoll(target: HTMLButtonElement, sheet: BWActorSheet): Promise { return handlePtgsRoll(target, sheet, true); } @@ -157,7 +165,7 @@ async function handleAttrRoll(target: HTMLButtonElement, sheet: BWActorSheet): P woundDice: attrName === "Steel" ? actor.data.data.ptgs.woundDice : undefined, obPenalty: actor.data.data.ptgs.obPenalty, tax, - stat + stat, }; const html = await renderTemplate(templates.attrDialog, data); @@ -194,6 +202,7 @@ async function attrRollCallback( const isSuccessful = parseInt(roll.result, 10) >= (baseData.diff + baseData.obPenalty); + const fateReroll = buildFateRerollData(sheet.actor, roll, accessor); const data: RollChatMessageData = { name: `${name} Test`, successes: roll.result, @@ -204,7 +213,8 @@ async function attrRollCallback( rolls: roll.dice[0].rolls, difficultyGroup: dg, penaltySources: baseData.penaltySources, - dieSources + dieSources, + fateReroll }; sheet.actor.addAttributeTest(stat, name, accessor, dg, isSuccessful); @@ -275,6 +285,8 @@ async function circlesRollCallback( const roll = rollDice(exp + baseData.bDice + baseData.aDice + bonusData.sum, stat.open, stat.shade); if (!roll) { return; } + const fateReroll = buildFateRerollData(sheet.actor, roll, "data.circles"); + baseData.obstacleTotal += penaltyData.sum; const data: RollChatMessageData = { name: `Circles Test`, @@ -286,7 +298,8 @@ async function circlesRollCallback( rolls: roll.dice[0].rolls, difficultyGroup: dg, dieSources, - penaltySources: { ...baseData.penaltySources, ...penaltyData.bonuses } + penaltySources: { ...baseData.penaltySources, ...penaltyData.bonuses }, + fateReroll }; const messageHtml = await renderTemplate(templates.circlesMessage, data); @@ -351,6 +364,7 @@ async function learningRollCallback( ); if (!roll) { return; } const isSuccessful = parseInt(roll.result, 10) >= baseData.obstacleTotal; + const fateReroll = buildFateRerollData(sheet.actor, roll, undefined, skill._id); const data: RollChatMessageData = { name: `Beginner's Luck ${skill.data.name} Test`, @@ -363,6 +377,7 @@ async function learningRollCallback( difficultyGroup: dg, penaltySources: baseData.penaltySources, dieSources, + fateReroll }; const messageHtml = await renderTemplate(templates.learnMessage, data); advanceLearning(skill, sheet.actor, dg, isSuccessful); @@ -428,6 +443,8 @@ async function statRollCallback( if (!roll) { return; } const isSuccessful = parseInt(roll.result, 10) >= baseData.obstacleTotal; + const fateReroll = buildFateRerollData(sheet.actor, roll, accessor); + const data: RollChatMessageData = { name: `${name} Test`, successes: roll.result, @@ -439,6 +456,7 @@ async function statRollCallback( difficultyGroup: dg, penaltySources: baseData.penaltySources, dieSources, + fateReroll }; sheet.actor.addStatTest(stat, name, accessor, dg, isSuccessful); @@ -494,6 +512,7 @@ async function skillRollCallback( skill.data.data.open, skill.data.data.shade); if (!roll) { return; } + const fateReroll = buildFateRerollData(sheet.actor, roll, undefined, skill._id); const data: RollChatMessageData = { name: `${skill.name} Test`, @@ -506,6 +525,7 @@ async function skillRollCallback( difficultyGroup: dg, penaltySources: baseData.penaltySources, dieSources, + fateReroll }; await helpers.addTestToSkill(skill, dg); @@ -528,6 +548,10 @@ async function skillRollCallback( }); } + +/* ================================================= */ +/* Helper functions */ +/* ================================================= */ function buildDiceSourceObject( exp: number, aDice: number, @@ -546,7 +570,29 @@ function buildDiceSourceObject( return dieSources; } -/* ======== Helper functions ======================= */ +function buildFateRerollData(actor: BWActor, roll: Roll, accessor?: string, itemId?: string): + FateRerollData | undefined { + if (!parseInt(actor.data.data.fate, 10)) { + return; + } + const coreData: FateRerollData = { + dice: roll.dice[0].rolls.map(r => r.roll).join(","), + type: "stat", + actorId: actor._id, + }; + if (accessor) { + return { + accessor, + ...coreData + }; + } else { + return { + itemId, + ...coreData + }; + } +} + function extractBaseData(html: JQuery, sheet: BWActorSheet ) { const actorData = sheet.actor.data; const woundDice = extractNumber(html, "woundDice") || 0; @@ -808,4 +854,13 @@ export interface RollChatMessageData { dieSources?: { [i: string]: string }; penaltySources?: { [i: string]: string }; + fateReroll?: FateRerollData; +} + +export interface FateRerollData { + dice: string; + actorId: string; + type: "stat" | "skill"; + itemId?: string; + accessor?: string; } \ No newline at end of file diff --git a/styles/chat/roll.scss b/styles/chat/roll.scss index 426d0100..a478068e 100644 --- a/styles/chat/roll.scss +++ b/styles/chat/roll.scss @@ -1,14 +1,14 @@ .chat-message { display: flex; + hr { + width: 100%; + } + .message-title { font-size: 1.5em; font-weight: bold; - &.open-roll { - color: gold; - } - &.shade-grey { background-color: rgba(black, 0.35); } @@ -22,6 +22,10 @@ background-color: rgba(white, .75); color: black; } + + &.open-roll { + color: gold; + } } div { diff --git a/templates/chat/roll-message.html b/templates/chat/roll-message.html index 99f21e1d..394b4b61 100644 --- a/templates/chat/roll-message.html +++ b/templates/chat/roll-message.html @@ -53,4 +53,18 @@ Failure! {{/if}} - \ No newline at end of file + {{#if fateReroll}} +
+
+ +
+ {{/if}} +