mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Embedding Duality Rolls (#52)
* Added DualityRoll direct rolls in chat * Added button render to renderJournalEntryPageProseMirrorSheet and renderHandlebarsApplication * Hope and Fear dice totals are now properly added together * Added Colorful/Normal DualityRoll color settings
This commit is contained in:
parent
cf51153432
commit
d1a0a9ab24
19 changed files with 1192 additions and 1264 deletions
|
|
@ -1,3 +1,6 @@
|
|||
import { DualityRollColor } from '../config/settingsConfig.mjs';
|
||||
import DhpDualityRoll from '../data/dualityRoll.mjs';
|
||||
|
||||
export default class DhpChatMesssage extends ChatMessage {
|
||||
async renderHTML() {
|
||||
if (
|
||||
|
|
@ -9,6 +12,20 @@ export default class DhpChatMesssage extends ChatMessage {
|
|||
this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
|
||||
}
|
||||
|
||||
return super.renderHTML();
|
||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||
const html = await super.renderHTML();
|
||||
if (
|
||||
this.type === 'dualityRoll' &&
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor) ===
|
||||
DualityRollColor.colorful.value
|
||||
) {
|
||||
html.classList.add('duality');
|
||||
const dualityResult = this.system.dualityResult;
|
||||
if (dualityResult === DhpDualityRoll.dualityResult.hope) html.classList.add('hope');
|
||||
else if (dualityResult === DhpDualityRoll.dualityResult.fear) html.classList.add('fear');
|
||||
else html.classList.add('critical');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@ export default class DamageSelectionDialog extends HandlebarsApplicationMixin(Ap
|
|||
}
|
||||
}
|
||||
|
||||
static rollDamage() {
|
||||
static rollDamage(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.resolve({
|
||||
rollString: this.getRollString(),
|
||||
bonusDamage: this.data.bonusDamage,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { DualityRollColor } from '../config/settingsConfig.mjs';
|
||||
|
||||
class DhpAutomationSettings extends FormApplication {
|
||||
constructor(object = {}, options = {}) {
|
||||
super(object, options);
|
||||
|
|
@ -213,6 +215,16 @@ export const registerDHPSettings = () => {
|
|||
}
|
||||
});
|
||||
|
||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
|
||||
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
|
||||
scope: 'world',
|
||||
config: true,
|
||||
type: Number,
|
||||
choices: Object.values(DualityRollColor),
|
||||
default: DualityRollColor.colorful.value
|
||||
});
|
||||
|
||||
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
|
||||
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
|
||||
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -24,5 +24,17 @@ export const gameSettings = {
|
|||
General: {
|
||||
AbilityArray: 'AbilityArray',
|
||||
RangeMeasurement: 'RangeMeasurement'
|
||||
},
|
||||
DualityRollColor: 'DualityRollColor'
|
||||
};
|
||||
|
||||
export const DualityRollColor = {
|
||||
colorful: {
|
||||
value: 0,
|
||||
label: 'DAGGERHEART.Settings.DualityRollColor.Options.Colorful'
|
||||
},
|
||||
normal: {
|
||||
value: 1,
|
||||
label: 'DAGGERHEART.Settings.DualityRollColor.Options.Normal'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { DualityRollColor } from '../config/settingsConfig.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
const diceField = () =>
|
||||
new fields.SchemaField({
|
||||
|
|
@ -6,6 +8,12 @@ const diceField = () =>
|
|||
});
|
||||
|
||||
export default class DhpDualityRoll extends foundry.abstract.TypeDataModel {
|
||||
static dualityResult = {
|
||||
hope: 1,
|
||||
fear: 2,
|
||||
critical: 3
|
||||
};
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
title: new fields.StringField(),
|
||||
|
|
@ -57,17 +65,32 @@ export default class DhpDualityRoll extends foundry.abstract.TypeDataModel {
|
|||
}
|
||||
|
||||
get total() {
|
||||
const modifiers = this.modifiers.reduce((acc, x) => acc + x.value, 0);
|
||||
const advantage = this.advantage.value
|
||||
? this.advantage.value
|
||||
: this.disadvantage.value
|
||||
? -this.disadvantage.value
|
||||
: 0;
|
||||
return this.highestRoll + advantage + modifiers;
|
||||
return this.diceTotal + advantage + this.modifierTotal.value;
|
||||
}
|
||||
|
||||
get highestRoll() {
|
||||
return Math.max(this.hope.value, this.fear.value);
|
||||
get diceTotal() {
|
||||
return this.hope.value + this.fear.value;
|
||||
}
|
||||
|
||||
get modifierTotal() {
|
||||
const total = this.modifiers.reduce((acc, x) => acc + x.value, 0);
|
||||
return {
|
||||
value: total,
|
||||
label: total > 0 ? `+${total}` : total < 0 ? `-${total}` : ''
|
||||
};
|
||||
}
|
||||
|
||||
get dualityResult() {
|
||||
return this.hope.value > this.fear.value
|
||||
? this.constructor.dualityResult.hope
|
||||
: this.fear.value > this.hope.value
|
||||
? this.constructor.dualityResult.fear
|
||||
: this.constructor.dualityResult.critical;
|
||||
}
|
||||
|
||||
get totalLabel() {
|
||||
|
|
@ -81,6 +104,13 @@ export default class DhpDualityRoll extends foundry.abstract.TypeDataModel {
|
|||
return game.i18n.localize(label);
|
||||
}
|
||||
|
||||
get colorful() {
|
||||
return (
|
||||
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor) ===
|
||||
DualityRollColor.colorful.value
|
||||
);
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
const total = this.total;
|
||||
|
||||
|
|
@ -92,89 +122,3 @@ export default class DhpDualityRoll extends foundry.abstract.TypeDataModel {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
//V1.3
|
||||
// const fields = foundry.data.fields;
|
||||
// const diceField = () => new fields.SchemaField({
|
||||
// dice: new fields.StringField({}),
|
||||
// value: new fields.NumberField({ integer: true}),
|
||||
// });
|
||||
|
||||
// export default class DhpDualityRoll extends foundry.abstract.TypeDataModel {
|
||||
// static defineSchema() {
|
||||
|
||||
// return {
|
||||
// roll: new fields.StringField({}),
|
||||
// modifiers: new fields.ArrayField(new fields.SchemaField({
|
||||
// value: new fields.NumberField({ integer: true }),
|
||||
// label: new fields.StringField({}),
|
||||
// title: new fields.StringField({}),
|
||||
// })),
|
||||
// hope: diceField(),
|
||||
// fear: diceField(),
|
||||
// advantage: diceField(),
|
||||
// disadvantage: diceField(),
|
||||
// advantageSelected: new fields.NumberField({ initial: 0 }),
|
||||
// targets: new fields.ArrayField(new fields.SchemaField({
|
||||
// id: new fields.StringField({}),
|
||||
// name: new fields.StringField({}),
|
||||
// img: new fields.StringField({}),
|
||||
// difficulty: new fields.NumberField({ integer: true, nullable: true }),
|
||||
// evasion: new fields.NumberField({ integer: true }),
|
||||
// hit: new fields.BooleanField({ initial: false }),
|
||||
// })),
|
||||
// damage: new fields.SchemaField({
|
||||
// value: new fields.StringField({}),
|
||||
// type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }),
|
||||
// bonusDamage: new fields.ArrayField(new fields.SchemaField({
|
||||
// value: new fields.StringField({}),
|
||||
// type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }),
|
||||
// initiallySelected: new fields.BooleanField(),
|
||||
// appliesOn: new fields.StringField({ choices: Object.keys(SYSTEM.EFFECTS.applyLocations) }, { nullable: true, initial: null }),
|
||||
// description: new fields.StringField({}),
|
||||
// hopeIncrease: new fields.StringField({ nullable: true })
|
||||
// }), { nullable: true, initial: null })
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// get total() {
|
||||
// const modifiers = this.modifiers.reduce((acc, x) => acc+x.value, 0);
|
||||
// const regular = {
|
||||
// normal: this.disadvantage.value ? Math.min(this.disadvantage.value, this.hope.value) + this.fear.value + modifiers : this.hope.value + this.fear.value + modifiers,
|
||||
// alternate: this.advantage.value ? this.advantage.value + this.fear.value + modifiers : null,
|
||||
// };
|
||||
|
||||
// const advantageSolve = this.advantageSelected === 0 ? null : {
|
||||
// normal: this.advantageSelected === 1 ? this.hope.value + this.fear.value + modifiers : this.advantage.value + this.fear.value + modifiers,
|
||||
// alternate: null,
|
||||
// };
|
||||
|
||||
// return advantageSolve ?? regular;
|
||||
// }
|
||||
|
||||
// get totalLabel() {
|
||||
// if(this.advantage.value && this.advantageSelected === 0) return game.i18n.localize("DAGGERHEART.Chat.DualityRoll.AdvantageChooseTitle");
|
||||
|
||||
// const hope = !this.advantage.value || this.advantageSelected === 1 ? this.hope.value : this.advantage.value;
|
||||
// const label = hope > this.fear.value ? "DAGGERHEART.General.Hope" : this.fear.value > hope ? "DAGGERHEART.General.Fear" : "DAGGERHEART.General.CriticalSuccess";
|
||||
|
||||
// return game.i18n.localize(label);
|
||||
// }
|
||||
|
||||
// get dualityDiceStates() {
|
||||
// return {
|
||||
// hope: this.hope.value > this.fear.value ? 'hope' : this.fear.value > this.hope.value ? 'fear' : 'critical',
|
||||
// alternate: this.advantage.value > this.fear.value ? 'hope' : this.fear.value > this.advantage.value ? 'fear' : 'critical',
|
||||
// }
|
||||
// }
|
||||
|
||||
// prepareDerivedData(){
|
||||
// const total = this.total;
|
||||
// if(total.alternate) return false;
|
||||
|
||||
// this.targets.forEach(target => {
|
||||
// target.hit = target.difficulty ? total.normal >= target.difficulty : total.normal >= target.evasion;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -119,7 +119,10 @@ export default class DhpActor extends Actor {
|
|||
const modifiers = [
|
||||
{
|
||||
value: modifier.value ? Number.parseInt(modifier.value) : 0,
|
||||
label: modifier.value >= 0 ? `+${modifier.value}` : `-${modifier.value}`,
|
||||
label:
|
||||
modifier.value >= 0
|
||||
? `${modifier.title} +${modifier.value}`
|
||||
: `${modifier.title} -${modifier.value}`,
|
||||
title: modifier.title
|
||||
}
|
||||
];
|
||||
|
|
|
|||
36
module/enrichers/DualityRollEnricher.mjs
Normal file
36
module/enrichers/DualityRollEnricher.mjs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { abilities } from '../config/actorConfig.mjs';
|
||||
import { rollCommandToJSON } from '../helpers/utils.mjs';
|
||||
|
||||
export function dualityRollEnricher(match, _options) {
|
||||
const roll = rollCommandToJSON(match[1]);
|
||||
if (!roll) return match[0];
|
||||
|
||||
return getDualityMessage(roll);
|
||||
}
|
||||
|
||||
export function getDualityMessage(roll) {
|
||||
const attributeLabel =
|
||||
roll.attribute && abilities[roll.attribute]
|
||||
? game.i18n.format('DAGGERHEART.General.Check', {
|
||||
check: game.i18n.localize(abilities[roll.attribute].label)
|
||||
})
|
||||
: null;
|
||||
const label = attributeLabel ?? game.i18n.localize('DAGGERHEART.General.Duality');
|
||||
|
||||
const dualityElement = document.createElement('span');
|
||||
dualityElement.innerHTML = `
|
||||
<button class="duality-roll-button"
|
||||
data-label="${label}"
|
||||
data-hope="${roll.hope ?? 'd12'}"
|
||||
data-fear="${roll.fear ?? 'd12'}"
|
||||
${roll.attribute && abilities[roll.attribute] ? `data-attribute="${roll.attribute}"` : ''}
|
||||
${roll.advantage ? 'data-advantage="true"' : ''}
|
||||
${roll.disadvantage ? 'data-disadvantage="true"' : ''}
|
||||
>
|
||||
<i class="fa-solid fa-circle-half-stroke"></i>
|
||||
${label}
|
||||
</button>
|
||||
`;
|
||||
|
||||
return dualityElement;
|
||||
}
|
||||
|
|
@ -22,17 +22,6 @@ const getCompendiumOptions = async compendium => {
|
|||
};
|
||||
|
||||
export const getWidthOfText = (txt, fontsize, allCaps, bold) => {
|
||||
// if(getWidthOfText.e === undefined){
|
||||
// getWidthOfText.e = document.createElement('span');
|
||||
// getWidthOfText.e.style.display = "none";
|
||||
// document.body.appendChild(getWidthOfText.e);
|
||||
// }
|
||||
// if(getWidthOfText.e.style.fontSize !== fontsize)
|
||||
// getWidthOfText.e.style.fontSize = fontsize;
|
||||
// if(getWidthOfText.e.style.fontFamily !== 'Signika, sans-serif')
|
||||
// getWidthOfText.e.style.fontFamily = 'Signika, sans-serif';
|
||||
// getWidthOfText.e.innerText = txt;
|
||||
// return getWidthOfText.e.offsetWidth;
|
||||
const text = allCaps ? txt.toUpperCase() : txt;
|
||||
if (getWidthOfText.c === undefined) {
|
||||
getWidthOfText.c = document.createElement('canvas');
|
||||
|
|
@ -82,3 +71,50 @@ export const generateId = (title, length) => {
|
|||
.join('');
|
||||
return Number.isNumeric(length) ? id.slice(0, length).padEnd(length, '0') : id;
|
||||
};
|
||||
|
||||
export function rollCommandToJSON(text) {
|
||||
if (!text) return {};
|
||||
|
||||
// Match key="quoted string" OR key=unquotedValue
|
||||
const PAIR_RE = /(\w+)=("(?:[^"\\]|\\.)*"|\S+)/g;
|
||||
const result = {};
|
||||
for (const [, key, raw] of text.matchAll(PAIR_RE)) {
|
||||
let value;
|
||||
if (raw.startsWith('"') && raw.endsWith('"')) {
|
||||
// Strip the surrounding quotes, un-escape any \" sequences
|
||||
value = raw.slice(1, -1).replace(/\\"/g, '"');
|
||||
} else if (/^(true|false)$/i.test(raw)) {
|
||||
// Boolean
|
||||
value = raw.toLowerCase() === 'true';
|
||||
} else if (!Number.isNaN(Number(raw))) {
|
||||
// Numeric
|
||||
value = Number(raw);
|
||||
} else {
|
||||
// Fallback to string
|
||||
value = raw;
|
||||
}
|
||||
result[key] = value;
|
||||
}
|
||||
return Object.keys(result).length > 0 ? result : null;
|
||||
}
|
||||
|
||||
export const getCommandTarget = () => {
|
||||
let target = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0].actor : null;
|
||||
if (!game.user.isGM) {
|
||||
target = game.user.character;
|
||||
if (!target) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoAssignedPlayerCharacter'));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!target) {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoSelectedToken'));
|
||||
return null;
|
||||
}
|
||||
if (target.type !== 'pc') {
|
||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.OnlyUseableByPC'));
|
||||
return null;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
|
||||
addChatListeners = async (app, html, data) => {
|
||||
html.querySelectorAll('.roll-damage-button').forEach(element =>
|
||||
html.querySelectorAll('.duality-action').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.target-container').forEach(element => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue