[Feature] Reload Check (#2051)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run

This commit is contained in:
WBHarry 2026-07-21 02:08:52 +02:00 committed by GitHub
parent 8d68166e4c
commit 26bcc2dddc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 223 additions and 14 deletions

View file

@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.mjs';
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs';
import { getArmorSources, getDocFromElement, getDocFromElementSync, sortBy } from '../../../helpers/utils.mjs';
import { getArmorSources, getDocFromElement, getDocFromElementSync, itemAbleRollParse, sortBy } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -29,6 +29,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
toggleResourceDice: CharacterSheet.#toggleResourceDice,
handleResourceDice: CharacterSheet.#handleResourceDice,
advanceResourceDie: CharacterSheet.#advanceResourceDie,
toggleItemReload: CharacterSheet.#onToggleItemReload,
cancelBeastform: CharacterSheet.#cancelBeastform,
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
useDowntime: this.useDowntime,
@ -954,11 +955,21 @@ export default class CharacterSheet extends DHBaseActorSheet {
});
}
/** */
static #advanceResourceDie(_, target) {
this.updateResourceDie(target, true);
}
static async #onToggleItemReload(_, target) {
const item = await getDocFromElement(target);
if (!item || !item.system.resource?.max)
return;
await item.update({
'system.resource.value': item.system.needsReload ?
itemAbleRollParse(item.system.resource.max, this.document, item) : 0
})
}
lowerResourceDie(event) {
event.preventDefault();
event.stopPropagation();

View file

@ -152,6 +152,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.risk-it-all-button').forEach(element =>
element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data))
);
for (const element of html.querySelectorAll('.roll-reload-check')) {
element.addEventListener('click', event => this.onRollReloadCheck(event, message));
}
};
setupHooks() {
@ -275,4 +278,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
const actor = game.actors.get(event.target.dataset.actorId);
new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true });
}
async onRollReloadCheck(_event, messageData) {
const message = game.messages.get(messageData._id);
const needsReload = await message.system.action.handleReload?.({ awaitRoll: true });
await message.update({ 'system.needsReload': needsReload });
}
}

View file

@ -62,3 +62,18 @@ export const actionAutomationChoices = {
label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.always'
}
};
export const reloadChoices = {
off: {
id: 'off',
label: 'DAGGERHEART.CONFIG.ReloadChoices.off.label'
},
button: {
id: 'button',
label: 'DAGGERHEART.CONFIG.ReloadChoices.button.label'
},
auto: {
id: 'auto',
label: 'DAGGERHEART.CONFIG.ReloadChoices.auto.label'
}
};

View file

@ -52,6 +52,10 @@ export default class DHAttackAction extends DHDamageAction {
}
async use(event, options) {
if (this.item?.system.needsReload) {
return ui.notifications.error(_loc('DAGGERHEART.UI.Notifications.reloadRequired', { weapon: this.item.name }));
}
const result = await super.use(event, options);
if (result?.message?.system.action?.roll?.type === 'attack') {
@ -62,6 +66,23 @@ export default class DHAttackAction extends DHDamageAction {
return result;
}
async handleReload(options = { awaitRoll: false }) {
const roll = await new Roll('1d6').evaluate();
if (game.modules.get('dice-so-nice')?.active) {
if (options.awaitRoll)
await game.dice3d.showForRoll(roll, game.user, true);
else
game.dice3d.showForRoll(roll, game.user, true);
}
const needsToReload = roll.total === 1;
if (needsToReload) {
this.item.update({ 'system.resource.value': 0 });
}
return needsToReload;
}
/**
* Generate a localized label array for this item subtype.
* @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects.

View file

@ -42,6 +42,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
hasEffect: new fields.BooleanField({ initial: false }),
hasSave: new fields.BooleanField({ initial: false }),
hasTarget: new fields.BooleanField({ initial: false }),
needsReload: new fields.BooleanField({ initial: false }),
isDirect: new fields.BooleanField({ initial: false }),
onSave: new fields.StringField(),
source: new fields.SchemaField({

View file

@ -116,6 +116,14 @@ export default class DHWeapon extends AttachableItem {
return this.weaponFeatures;
}
get hasReload() {
return Boolean(this.weaponFeatures.find(x => x.value === 'reloading'));
}
get needsReload() {
return this.hasReload && this.resource.value === 0;
}
/**@inheritdoc */
async getDescriptionData() {
const baseDescription = this.description;

View file

@ -196,6 +196,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
})
})
}),
reload: new fields.StringField({
required: true,
choices: CONFIG.DH.SETTINGS.reloadChoices,
initial: CONFIG.DH.SETTINGS.reloadChoices.button.id,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.label',
hint: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.hint'
}),
autoExpireActiveEffects: new fields.BooleanField({
required: true,
initial: true,

View file

@ -117,7 +117,11 @@ export default class DHRoll extends BaseRoll {
static async toMessage(roll, config) {
const item = config.data.parent?.items?.get?.(config.source.item) ?? null;
const action = item ? item.system.actions.get(config.source.action) : null;
const actions = item ? [
...item.system.actions,
...(item.system.attack?.id === config.source.action ? [item.system.attack] : [])
] : [];
const action = actions.find(x => x.id === config.source.action);
let actionDescription = null;
if (action?.chatDisplay) {
actionDescription = action
@ -129,6 +133,14 @@ export default class DHRoll extends BaseRoll {
config.actionChatMessageHandled = true;
}
const reloadSetting =
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).reload;
const useReload =
item?.system.hasReload &&
action?.type === 'attack' &&
reloadSetting === CONFIG.DH.SETTINGS.reloadChoices.auto.id;
const needsReload = useReload ? await action?.handleReload?.() : false;
const cls = getDocumentClass('ChatMessage'),
msgData = {
type: this.messageType,
@ -136,7 +148,7 @@ export default class DHRoll extends BaseRoll {
title: roll.title,
speaker: cls.getSpeaker({ actor: roll.data?.parent }),
sound: config.mute ? null : CONFIG.sounds.dice,
system: { ...config, actionDescription },
system: { ...config, actionDescription, needsReload },
rolls: [roll]
};
@ -158,14 +170,17 @@ export default class DHRoll extends BaseRoll {
if (!this._evaluated) return;
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
return foundry.applications.handlebars.renderTemplate(template, {
roll: this,
...chatData,
action: chatData.action,
parent: chatData.parent,
targetMode: chatData.targetMode,
areas: chatData.action?.areas,
metagamingSettings
metagamingSettings,
automationSettings
});
}