mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 15:39:02 +01:00
Fixes https://github.com/Foundryborne/daggerheart/issues/374 Some downtime moves require a roll to see how successful they are. In these cases, the player might want to see how their first move works out before they select their next one. This commit updates the downtime dialog to allow for this behaviour: - The "Take Downtime" button is enabled whenever any moves are selected. - Clicking the button only closes the dialog when all moves have been made. To keep track of this, the `nrChoices` object has been expanded to include a `taken` counter, which is increased whenever a move is taken. After making one move the selection is reset, but the number of moves displayed in the dialog header and the number of permitted selections both take the number of taken moves into account.
193 lines
7.2 KiB
JavaScript
193 lines
7.2 KiB
JavaScript
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
|
|
export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
constructor(actor, shortrest) {
|
|
super({});
|
|
|
|
this.actor = actor;
|
|
this.shortrest = shortrest;
|
|
|
|
this.moveData = foundry.utils.deepClone(
|
|
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves
|
|
);
|
|
this.nrChoices = {
|
|
shortRest: {
|
|
taken: 0,
|
|
max:
|
|
(shortrest ? this.moveData.shortRest.nrChoices : 0) +
|
|
actor.system.bonuses.rest[`${shortrest ? 'short' : 'long'}Rest`].shortMoves
|
|
},
|
|
longRest: {
|
|
taken: 0,
|
|
max:
|
|
(!shortrest ? this.moveData.longRest.nrChoices : 0) +
|
|
actor.system.bonuses.rest[`${shortrest ? 'short' : 'long'}Rest`].longMoves
|
|
}
|
|
};
|
|
this.nrChoices.total = { max: this.nrChoices.shortRest.max + this.nrChoices.longRest.max };
|
|
}
|
|
|
|
get title() {
|
|
return '';
|
|
}
|
|
|
|
static DEFAULT_OPTIONS = {
|
|
tag: 'form',
|
|
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'downtime'],
|
|
position: { width: 'auto', height: 'auto' },
|
|
actions: {
|
|
selectMove: this.selectMove,
|
|
takeDowntime: this.takeDowntime
|
|
},
|
|
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
|
|
};
|
|
|
|
static PARTS = {
|
|
application: {
|
|
id: 'downtime',
|
|
template: 'systems/daggerheart/templates/dialogs/downtime/downtime.hbs'
|
|
}
|
|
};
|
|
|
|
_attachPartListeners(partId, htmlElement, options) {
|
|
super._attachPartListeners(partId, htmlElement, options);
|
|
|
|
htmlElement
|
|
.querySelectorAll('.activity-container')
|
|
.forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this)));
|
|
}
|
|
|
|
async _prepareContext(_options) {
|
|
const context = await super._prepareContext(_options);
|
|
context.title = game.i18n.localize(
|
|
`DAGGERHEART.APPLICATIONS.Downtime.${this.shortrest ? 'shortRest' : 'longRest'}.title`
|
|
);
|
|
context.selectedActivity = this.selectedActivity;
|
|
context.moveData = this.moveData;
|
|
context.nrCurrentChoices = Object.values(this.moveData).reduce((acc, category) => {
|
|
acc += Object.values(category.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
|
return acc;
|
|
}, 0);
|
|
|
|
const shortRestMovesSelected = Object.values(this.moveData.shortRest.moves).reduce(
|
|
(acc, x) => acc + (x.selected ?? 0),
|
|
0
|
|
);
|
|
const longRestMovesSelected = Object.values(this.moveData.longRest.moves).reduce(
|
|
(acc, x) => acc + (x.selected ?? 0),
|
|
0
|
|
);
|
|
context.nrChoices = {
|
|
...this.nrChoices,
|
|
shortRest: {
|
|
...this.nrChoices.shortRest,
|
|
current: this.nrChoices.shortRest.taken + shortRestMovesSelected
|
|
},
|
|
longRest: {
|
|
...this.nrChoices.longRest,
|
|
current: this.nrChoices.longRest.taken + longRestMovesSelected
|
|
}
|
|
};
|
|
context.nrChoices.total = {
|
|
...this.nrChoices.total,
|
|
current: context.nrChoices.shortRest.current + context.nrChoices.longRest.current
|
|
};
|
|
|
|
context.shortRestMoves = this.nrChoices.shortRest.max > 0 ? this.moveData.shortRest : null;
|
|
context.longRestMoves = this.nrChoices.longRest.max > 0 ? this.moveData.longRest : null;
|
|
|
|
context.disabledDowntime = shortRestMovesSelected === 0 && longRestMovesSelected === 0;
|
|
|
|
return context;
|
|
}
|
|
|
|
static selectMove(_, target) {
|
|
const { category, move } = target.dataset;
|
|
|
|
const nrSelected = Object.values(this.moveData[category].moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
|
|
|
|
if (nrSelected + this.nrChoices[category].taken >= this.nrChoices[category].max) {
|
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves'));
|
|
return;
|
|
}
|
|
|
|
this.moveData[category].moves[move].selected = this.moveData[category].moves[move].selected
|
|
? this.moveData[category].moves[move].selected + 1
|
|
: 1;
|
|
|
|
this.render();
|
|
}
|
|
|
|
deselectMove(event) {
|
|
const button = event.target.closest('.activity-container');
|
|
const { move, category } = button.dataset;
|
|
this.moveData[category].moves[move].selected = this.moveData[category].moves[move].selected
|
|
? this.moveData[category].moves[move].selected - 1
|
|
: 0;
|
|
|
|
this.render();
|
|
|
|
// On macOS with a single-button mouse (e.g. a laptop trackpad),
|
|
// right-click is triggered with ctrl+click, which triggers both a
|
|
// `contextmenu` event and a regular click event. We need to stop
|
|
// event propagation to prevent the click event from triggering the
|
|
// `selectMove` function and undoing the change we just made.
|
|
event.stopPropagation();
|
|
|
|
// Having stopped propagation, we're no longer subject to Foundry's
|
|
// default `contextmenu` handler, so we also have to prevent the
|
|
// default behaviour to prevent a context menu from appearing.
|
|
event.preventDefault();
|
|
}
|
|
|
|
static async takeDowntime() {
|
|
const moves = Object.values(this.moveData).flatMap(category => {
|
|
return Object.values(category.moves)
|
|
.filter(x => x.selected)
|
|
.flatMap(move => [...Array(move.selected).keys()].map(_ => move));
|
|
});
|
|
|
|
const cls = getDocumentClass('ChatMessage');
|
|
const msg = new cls({
|
|
user: game.user.id,
|
|
system: {
|
|
moves: moves,
|
|
actor: this.actor.uuid
|
|
},
|
|
content: await foundry.applications.handlebars.renderTemplate(
|
|
'systems/daggerheart/templates/ui/chat/downtime.hbs',
|
|
{
|
|
title: `${this.actor.name} - ${game.i18n.localize(`DAGGERHEART.APPLICATIONS.Downtime.${this.shortRest ? 'shortRest' : 'longRest'}.title`)}`,
|
|
moves: moves
|
|
}
|
|
)
|
|
});
|
|
|
|
cls.create(msg.toObject());
|
|
|
|
// Reset selection and update number of taken moves
|
|
for (const [catName, category] of Object.entries(this.moveData)) {
|
|
for (const move of Object.values(category.moves)) {
|
|
if (move.selected > 0) {
|
|
this.nrChoices[catName].taken += move.selected;
|
|
move.selected = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We can close the window when all moves are taken
|
|
if (
|
|
this.nrChoices.shortRest.taken >= this.nrChoices.shortRest.max &&
|
|
this.nrChoices.longRest.taken >= this.nrChoices.longRest.max
|
|
) {
|
|
this.close();
|
|
} else {
|
|
this.render();
|
|
}
|
|
}
|
|
|
|
static async updateData(event, element, formData) {
|
|
this.customActivity = foundry.utils.mergeObject(this.customActivity, formData.object);
|
|
this.render();
|
|
}
|
|
}
|