From 2a7377f094c4814a767280399707c6a71bd5bad2 Mon Sep 17 00:00:00 2001 From: Stas Tserkovny Date: Mon, 20 Jul 2020 17:31:33 -0600 Subject: [PATCH] Add fate reroll button to chat messages Add fate reroll button to chat messages, allowing for using fate to open the skill (rerolling any 6's) Hide fate reroll button for all but the owner of the character that performed the roll. --- module/burningwheel.ts | 5 +++ module/chat.ts | 23 ++++++++++++ module/rolls.ts | 63 ++++++++++++++++++++++++++++++-- styles/chat/roll.scss | 12 ++++-- templates/chat/roll-message.html | 16 +++++++- 5 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 module/chat.ts 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}} +