Fixed Feature spellcasting modifier

This commit is contained in:
WBHarry 2025-07-14 00:14:32 +02:00
parent 637918d686
commit 8fc26495c1
12 changed files with 57 additions and 34 deletions

View file

@ -1,14 +1,12 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class ResourceDiceDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class ResourceDiceDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(name, recovery, actor, resource, options = {}) { constructor(item, actor, options = {}) {
super(options); super(options);
this.name = name; this.item = item;
this.recovery = recovery;
this.actor = actor; this.actor = actor;
this.resource = resource; this.diceStates = foundry.utils.deepClone(item.system.resource.diceStates);
this.diceStates = foundry.utils.deepClone(resource.diceStates);
} }
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
@ -37,16 +35,14 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
}; };
get title() { get title() {
return game.i18n.format('DAGGERHEART.APPLICATIONS.ResourceDice.title', { name: this.name }); return game.i18n.format('DAGGERHEART.APPLICATIONS.ResourceDice.title', { name: this.item.name });
} }
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.name = this.name; context.item = this.item;
context.recovery = game.i18n.localize(CONFIG.DH.GENERAL.refreshTypes[this.recovery].label);
context.resource = this.resource;
context.diceStates = this.diceStates;
context.actor = this.actor; context.actor = this.actor;
context.diceStates = this.diceStates;
return context; return context;
} }
@ -90,9 +86,9 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
this.close(); this.close();
} }
static async create(name, recovery, actor, resource, options = {}) { static async create(item, actor, options = {}) {
return new Promise(resolve => { return new Promise(resolve => {
const app = new this(name, recovery, actor, resource, options); const app = new this(item, actor, options);
app.addEventListener('close', () => resolve(app.rollValues), { once: true }); app.addEventListener('close', () => resolve(app.rollValues), { once: true });
app.render({ force: true }); app.render({ force: true });
}); });

View file

@ -697,12 +697,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
const item = this.getItem(event); const item = this.getItem(event);
if (!item) return; if (!item) return;
const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create( const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document);
item.name,
item.system.resource.recovery,
this.document,
item.system.resource
);
if (!rollValues) return; if (!rollValues) return;
await item.update({ await item.update({

View file

@ -68,7 +68,6 @@ export default class FeatureSheet extends DHBaseItemSheet {
} }
), ),
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType'); title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType');
console.log(this.document);
return foundry.applications.api.DialogV2.prompt({ return foundry.applications.api.DialogV2.prompt({
window: { title }, window: { title },

View file

@ -371,8 +371,6 @@ export const abilityCosts = {
label: 'Fear', label: 'Fear',
group: 'TYPES.Actor.adversary' group: 'TYPES.Actor.adversary'
} }
// ...featureTokenTypes,
// ...featureDiceTypes
}; };
export const countdownTypes = { export const countdownTypes = {

View file

@ -1,7 +1,6 @@
import { DHActionDiceData, DHActionRollData, DHDamageField } from './actionDice.mjs'; import { DHActionDiceData, DHActionRollData, DHDamageField } from './actionDice.mjs';
import DhpActor from '../../documents/actor.mjs'; import DhpActor from '../../documents/actor.mjs';
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs'; import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
import { getResources } from '../../helpers/utils.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;

View file

@ -124,7 +124,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
...feature, ...feature,
system: { system: {
...feature.system, ...feature.system,
type: this.parent.type, originItemType: this.parent.type,
originId: data._id, originId: data._id,
identifier: feature.identifier identifier: feature.identifier
} }

View file

@ -17,10 +17,32 @@ export default class DHFeature extends BaseDataItem {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
type: new fields.StringField({ choices: CONFIG.DH.ITEM.featureTypes, nullable: true, initial: null }), originItemType: new fields.StringField({
choices: CONFIG.DH.ITEM.featureTypes,
nullable: true,
initial: null
}),
originId: new fields.StringField({ nullable: true, initial: null }), originId: new fields.StringField({ nullable: true, initial: null }),
identifier: new fields.StringField(), identifier: new fields.StringField(),
actions: new fields.ArrayField(new ActionField()) actions: new fields.ArrayField(new ActionField())
}; };
} }
get spellcastingModifier() {
let traitValue = 0;
if (this.actor && this.originId && ['class', 'subclass'].includes(this.originItemType)) {
if (this.originItemType === 'subclass') {
traitValue =
this.actor.system.traits[this.actor.items.get(this.originId).system.spellcastingTrait]?.value ?? 0;
} else {
const subclass =
this.actor.system.multiclass.value?.id === this.originId
? this.actor.system.multiclass.subclass
: this.actor.system.class.subclass;
traitValue = this.actor.system.traits[subclass.system.spellcastingTrait]?.value ?? 0;
}
}
return traitValue;
}
} }

View file

@ -1,3 +1,5 @@
import { itemAbleRollParse } from '../helpers/utils.mjs';
export default class DhActiveEffect extends ActiveEffect { export default class DhActiveEffect extends ActiveEffect {
get isSuppressed() { get isSuppressed() {
// If this is a copied effect from an attachment, never suppress it // If this is a copied effect from an attachment, never suppress it
@ -53,11 +55,7 @@ export default class DhActiveEffect extends ActiveEffect {
} }
static applyField(model, change, field) { static applyField(model, change, field) {
const isItemTarget = change.value.toLowerCase().startsWith('item.'); change.value = itemAbleRollParse(change.value, model, change.effect.parent);
change.value = isItemTarget ? change.value.slice(5) : change.value;
change.value = Roll.safeEval(
Roll.replaceFormulaData(change.value, isItemTarget ? change.effect.parent : model)
);
super.applyField(model, change, field); super.applyField(model, change, field);
} }

View file

@ -1,3 +1,5 @@
import { itemAbleRollParse } from './utils.mjs';
export default class RegisterHandlebarsHelpers { export default class RegisterHandlebarsHelpers {
static registerHelpers() { static registerHelpers() {
Handlebars.registerHelper({ Handlebars.registerHelper({
@ -42,7 +44,9 @@ export default class RegisterHandlebarsHelpers {
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`)); return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
} }
static rollParsed(value, actor) { static rollParsed(value, actor, item, numerical) {
return Roll.replaceFormulaData(value, actor); const isNumerical = typeof numerical === 'boolean' ? numerical : false;
const result = itemAbleRollParse(value, actor, item);
return isNumerical && !result ? 0 : result;
} }
} }

View file

@ -299,3 +299,15 @@ export const updateActorTokens = async (actor, update) => {
} }
} }
}; };
export const itemAbleRollParse = (value, actor, item) => {
if (!value) return value;
const isItemTarget = value.toLowerCase().startsWith('item.');
const slicedValue = isItemTarget ? value.slice(5) : value;
try {
return Roll.safeEval(Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor));
} catch (_) {
return '';
}
};

View file

@ -1,10 +1,10 @@
<div> <div>
<div class="resource-items"> <div class="resource-items">
{{#times (rollParsed resource.max actor)}} {{#times (rollParsed item.resource.max actor item numerical=true)}}
{{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}} {{#with (ifThen (lookup ../diceStates this) (lookup ../diceStates this) this) as | state |}}
<div class="resource-item" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}"> <div class="resource-item" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
<input type="number" data-dtype="Number" name={{concat "diceStates." (ifThen ../../this ../this state) ".value" }} value="{{state.value}}" /> <input type="number" data-dtype="Number" name={{concat "diceStates." (ifThen ../../this ../this state) ".value" }} value="{{state.value}}" />
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../resource.dieFaces ../../resource.dieFaces ../resource.dieFaces) ".svg"}}" /> <img src="{{concat "systems/daggerheart/assets/icons/dice/hope/d" (ifThen ../../item.resource.dieFaces ../../item.resource.dieFaces ../item.resource.dieFaces) ".svg"}}" />
</div> </div>
{{/with}} {{/with}}
{{/times}} {{/times}}

View file

@ -5,7 +5,7 @@
</div> </div>
{{else}} {{else}}
<div class="item-resources"> <div class="item-resources">
{{#times (rollParsed item.system.resource.max item.parent)}} {{#times (rollParsed item.system.resource.max item.parent item numerical=true)}}
{{#with (ifThen (lookup ../item.system.resource.diceStates this) (lookup ../item.system.resource.diceStates this) this) as | state |}} {{#with (ifThen (lookup ../item.system.resource.diceStates this) (lookup ../item.system.resource.diceStates this) this) as | state |}}
<a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}"> <a class="item-resource" data-action="toggleResourceDice" data-dice="{{#if ../../this}}{{../this}}{{else}}{{state}}{{/if}}">
<div class="item-dice-resource"> <div class="item-dice-resource">