Feature/635 allow weapon direct damage (#657)

* #635 & #637

* #653
This commit is contained in:
Dapoulp 2025-08-06 20:28:53 +02:00 committed by GitHub
parent cf571aa2a5
commit bcedc74bf3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 57 additions and 156 deletions

View file

@ -2,7 +2,6 @@ export { default as BeastformDialog } from './beastformDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs';
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';
export { default as DamageSelectionDialog } from './damageSelectionDialog.mjs';
export { default as DeathMove } from './deathMove.mjs';
export { default as Downtime } from './downtime.mjs';
export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs';

View file

@ -1,128 +0,0 @@
// TO DELETE ?
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(rollString, bonusDamage, resolve, hope = 0) {
super({});
this.data = {
rollString,
bonusDamage: bonusDamage.reduce((acc, x) => {
if (x.appliesOn === CONFIG.DH.EFFECTS.applyLocations.damageRoll.id) {
acc.push({
...x,
hopeUses: 0
});
}
return acc;
}, []),
hope
};
this.resolve = resolve;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
decreaseHopeUse: this.decreaseHopeUse,
increaseHopeUse: this.increaseHopeUse,
rollDamage: this.rollDamage
},
form: {
handler: this.updateSelection,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/damageSelection.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return `Damage Options`;
}
async _prepareContext(_options) {
return {
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hope: this.data.hope + 1,
hopeUsed: this.getHopeUsed()
};
}
static updateSelection(event, _, formData) {
const { bonusDamage, ...rest } = foundry.utils.expandObject(formData.object);
for (var index in bonusDamage) {
this.data.bonusDamage[index].initiallySelected = bonusDamage[index].initiallySelected;
if (bonusDamage[index].hopeUses) {
const value = Number.parseInt(bonusDamage[index].hopeUses);
if (!Number.isNaN(value)) this.data.bonusDamage[index].hopeUses = value;
}
}
this.data = foundry.utils.mergeObject(this.data, rest);
this.render(true);
}
getRollString() {
return this.data.rollString.concat(
this.data.bonusDamage.reduce((acc, x) => {
if (x.initiallySelected) {
const nr = 1 + x.hopeUses;
const baseDamage = x.value;
return acc.concat(` + ${nr}${baseDamage}`);
}
return acc;
}, '')
);
}
getHopeUsed() {
return this.data.bonusDamage.reduce((acc, x) => acc + x.hopeUses, 0);
}
static decreaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses - 1 >= 0) {
this.data.bonusDamage[index].hopeUses -= 1;
this.render(true);
}
}
static increaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses <= this.data.hope + 1) {
this.data.bonusDamage[index].hopeUses += 1;
this.render(true);
}
}
static rollDamage(event) {
event.preventDefault();
this.resolve({
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hopeUsed: this.getHopeUsed()
});
this.close();
}
}

View file

@ -7,7 +7,11 @@ export default class DhpEnvironment extends DHBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ['environment'],
position: {
width: 500
width: 500,
height: 725
},
window: {
resizable: true
},
actions: {},
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]

View file

@ -323,6 +323,20 @@ export default function DHApplicationMixin(Base) {
];
if (usable)
options.unshift({
name: 'DAGGERHEART.GENERAL.damage',
icon: 'fa-solid fa-explosion',
condition: target => {
const doc = getDocFromElementSync(target);
return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length;
},
callback: async (target, event) => {
const doc = await getDocFromElement(target),
action = doc?.system?.attack ?? doc;
return action && action.use(event, { byPassRoll: true })
}
});
options.unshift({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
icon: 'fa-solid fa-burst',
@ -334,7 +348,7 @@ export default function DHApplicationMixin(Base) {
});
if (toChat)
options.unshift({
options.push({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message',
callback: async target => (await getDocFromElement(target)).toChat(this.document.id)

View file

@ -194,8 +194,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation();
const item = await foundry.utils.fromUuid(message.system.origin);
const action = item.system.actions.get(event.currentTarget.id);
await item.use(action);
const action = item.system.attack?.id === event.currentTarget.id ? item.system.attack : item.system.actions.get(event.currentTarget.id);
if(event.currentTarget.dataset.directDamage)
action.use(event, { byPassRoll: true })
else
action.use(event);
}
async actionUseButton(event, message) {

View file

@ -34,8 +34,8 @@ export default class DHAttackAction extends DHDamageAction {
};
}
async use(event, ...args) {
const result = await super.use(event, args);
async use(event, options) {
const result = await super.use(event, options);
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.characterAttack.id);

View file

@ -111,12 +111,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
return actorData;
}
async use(event, ...args) {
async use(event, options = {}) {
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
if (this.chatDisplay) await this.toChat();
let config = this.prepareConfig(event);
let { byPassRoll } = options,
config = this.prepareConfig(event, byPassRoll);
for (let i = 0; i < this.constructor.extraSchemas.length; i++) {
let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]);
if (clsField?.prepareConfig) {
@ -133,14 +134,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (!config) return;
}
if (this.hasRoll) {
if (config.hasRoll) {
const rollConfig = this.prepareRoll(config);
config.roll = rollConfig;
config = await this.actor.diceRoll(config);
if (!config) return;
}
if (this.doFollowUp()) {
if (this.doFollowUp(config)) {
if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config);
else if (this.trigger) await this.trigger(event, config);
else if (this.hasSave || this.hasEffect) {
@ -160,7 +161,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
/* */
prepareConfig(event) {
prepareConfig(event, byPass = false) {
const hasRoll = this.getUseHasRoll(byPass);
return {
event,
title: `${this.item.name}: ${this.name}`,
@ -170,10 +172,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
actor: this.actor.uuid
},
dialog: {
configure: this.hasRoll
configure: hasRoll
},
type: this.type,
hasRoll: this.hasRoll,
hasRoll: hasRoll,
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
hasHealing: this.damage?.parts?.length && this.type === 'healing',
hasEffect: !!this.effects?.length,
@ -182,12 +184,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
selectedRollMode: game.settings.get('core', 'rollMode'),
isFastForward: event.shiftKey,
data: this.getRollData(),
evaluate: this.hasRoll
evaluate: hasRoll
};
}
requireConfigurationDialog(config) {
return !config.event.shiftKey && !this.hasRoll && (config.costs?.length || config.uses);
return !config.event.shiftKey && !config.hasRoll && (config.costs?.length || config.uses);
}
prepareRoll() {
@ -205,7 +207,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
doFollowUp(config) {
return !this.hasRoll;
return !config.hasRoll;
}
async consume(config, successCost = false) {
@ -257,6 +259,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/* */
/* ROLL */
getUseHasRoll(byPass = false) {
return this.hasRoll && !byPass;
}
get hasRoll() {
return !!this.roll?.type;
}

View file

@ -4,7 +4,7 @@ import DHBaseAction from './baseAction.mjs';
export default class DhBeastformAction extends DHBaseAction {
static extraSchemas = [...super.extraSchemas, 'beastform'];
async use(event, ...args) {
async use(event, options) {
const beastformConfig = this.prepareBeastformConfig();
const abort = await this.handleActiveTransformations();
@ -20,7 +20,7 @@ export default class DhBeastformAction extends DHBaseAction {
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, this.item);
if (!selected) return;
const result = await super.use(event, args);
const result = await super.use(event, options);
if (!result) return;
await this.transform(selected, evolved, hybrid);

View file

@ -6,7 +6,7 @@ export default class DHDamageAction extends DHBaseAction {
getFormulaValue(part, data) {
let formulaValue = part.value;
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
if (data.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
const isAdversary = this.actor.type === 'adversary';
if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {

View file

@ -10,8 +10,6 @@ export default class DHMacroAction extends DHBaseAction {
}
async trigger(event, ...args) {
// const config = await super.use(event, args);
// if (['error', 'warning'].includes(config.type)) return;
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
macro = await fromUuid(fixUUID);
try {

View file

@ -11,7 +11,6 @@ export default class DHSummonAction extends DHBaseAction {
async trigger(event, ...args) {
if (!this.canSummon || !canvas.scene) return;
// const config = await super.use(event, args);
}
get canSummon() {

View file

@ -132,7 +132,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
if(this.targetSelection === true) {
this.targetShort = this.targets.reduce((a,c) => {
if(c.hit) a.hit += 1;
else c.miss += 1;
else a.miss += 1;
return a;
}, {hit: 0, miss: 0})
}

View file

@ -145,7 +145,7 @@ export default class D20Roll extends DHRoll {
config.targetSelection = true;
config.targets.forEach(target => {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
target.hit = this.isCritical || roll.total >= difficulty;
target.hit = roll.isCritical || roll.total >= difficulty;
});
data.success = config.targets.some(target => target.hit)
} else if (config.roll.difficulty) {

View file

@ -163,7 +163,7 @@ export default class DHItem extends foundry.documents.Item {
img: this.img,
tags: this._getTags()
},
actions: item.system.actions,
actions: item.system.actionsList,
description: this.system.description
};