Implement the most basic version of a dice pool configuration
This commit is contained in:
parent
c62d3cae2f
commit
a95412ad2e
19 changed files with 465 additions and 30 deletions
158
module/Apps/DicePool.mjs
Normal file
158
module/Apps/DicePool.mjs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import { filePath } from "../consts.mjs";
|
||||
import { GenericAppMixin } from "./GenericApp.mjs";
|
||||
import { localizer } from "../utils/Localizer.mjs";
|
||||
import { Logger } from "../utils/Logger.mjs";
|
||||
|
||||
const { Roll } = foundry.dice;
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export class DicePool extends GenericAppMixin(HandlebarsApplicationMixin(ApplicationV2)) {
|
||||
// #region Options
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: [
|
||||
`ripcrypt--DicePool`,
|
||||
],
|
||||
window: {
|
||||
title: `Dice Pool`,
|
||||
frame: true,
|
||||
positioned: true,
|
||||
resizable: false,
|
||||
minimizable: true,
|
||||
},
|
||||
position: {
|
||||
width: `auto`,
|
||||
height: `auto`,
|
||||
},
|
||||
actions: {
|
||||
diceCountDelta: this.#diceCountDelta,
|
||||
targetDelta: this.#targetDelta,
|
||||
roll: this.#roll,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
numberOfDice: {
|
||||
template: filePath(`templates/Apps/DicePool/numberOfDice.hbs`),
|
||||
},
|
||||
target: {
|
||||
template: filePath(`templates/Apps/DicePool/target.hbs`),
|
||||
},
|
||||
buttons: {
|
||||
template: filePath(`templates/Apps/DicePool/buttons.hbs`),
|
||||
},
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Instance Data
|
||||
_diceCount;
|
||||
_target;
|
||||
|
||||
constructor({
|
||||
diceCount = 1,
|
||||
target,
|
||||
flavor = ``,
|
||||
...opts
|
||||
} = {}) {
|
||||
super(opts);
|
||||
|
||||
this._flavor = flavor;
|
||||
this._diceCount = diceCount;
|
||||
this._target = target ?? game.settings.get(`ripcrypt`, `dc`) ?? 1;
|
||||
};
|
||||
|
||||
get title() {
|
||||
if (!this._flavor) {
|
||||
return super.title;
|
||||
}
|
||||
return `${super.title}: ${this._flavor}`;
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle
|
||||
async _preparePartContext(partId, ctx, _opts) {
|
||||
ctx = {};
|
||||
|
||||
switch (partId) {
|
||||
case `numberOfDice`: {
|
||||
this._prepareNumberOfDice(ctx);
|
||||
break;
|
||||
};
|
||||
case `target`: {
|
||||
this._prepareTarget(ctx);
|
||||
break;
|
||||
};
|
||||
case `buttons`: {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
Logger.debug(`${partId} Context:`, ctx);
|
||||
return ctx;
|
||||
};
|
||||
|
||||
async _prepareNumberOfDice(ctx) {
|
||||
ctx.numberOfDice = this._diceCount;
|
||||
ctx.decrementDisabled = this._diceCount <= 0;
|
||||
};
|
||||
|
||||
async _prepareTarget(ctx) {
|
||||
ctx.target = this._target;
|
||||
ctx.incrementDisabled = this._target >= 8;
|
||||
ctx.decrementDisabled = this._target <= 1;
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Actions
|
||||
static async #diceCountDelta(_event, element) {
|
||||
const delta = parseInt(element.dataset.delta);
|
||||
if (Number.isNaN(delta)) {
|
||||
ui.notifications.error(
|
||||
localizer(`RipCrypt.notifs.error.invalid-delta`, { name: `@RipCrypt.Apps.numberOfDice` }),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let newCount = this._diceCount + delta;
|
||||
|
||||
if (newCount < 0) {
|
||||
ui.notifications.warn(
|
||||
localizer(`RipCrypt.notifs.warn.cannot-go-negative`, { name: `@RipCrypt.Apps.numberOfDice` }),
|
||||
);
|
||||
};
|
||||
|
||||
this._diceCount = Math.max(newCount, 0);
|
||||
this.render({ parts: [`numberOfDice`] });
|
||||
};
|
||||
|
||||
static async #targetDelta(_event, element) {
|
||||
const delta = parseInt(element.dataset.delta);
|
||||
if (Number.isNaN(delta)) {
|
||||
ui.notifications.error(
|
||||
localizer(`RipCrypt.notifs.error.invalid-delta`, { name: `@RipCrypt.Apps.rollTarget` }),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
this._target += delta;
|
||||
this.render({ parts: [`target`] });
|
||||
};
|
||||
|
||||
static async #roll() {
|
||||
const formula = `${this._diceCount}d8rc${this._target}`;
|
||||
Logger.debug(`Attempting to roll formula: ${formula}`);
|
||||
|
||||
let flavor = this._flavor;
|
||||
if (this._flavor) {
|
||||
flavor += ` ` + localizer(`RipCrypt.Apps.difficulty`, { dc: this._target });
|
||||
}
|
||||
|
||||
const roll = new Roll(formula);
|
||||
await roll.evaluate();
|
||||
await roll.toMessage({
|
||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
||||
flavor,
|
||||
});
|
||||
this.close();
|
||||
};
|
||||
// #endregion
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue