Some fixes

This commit is contained in:
Dapoolp 2025-08-26 13:30:38 +02:00
parent 92a0883806
commit fb9240e130
18 changed files with 94 additions and 82 deletions

View file

@ -2228,11 +2228,11 @@
},
"damageApply": {
"label": "Apply Damage/Healing",
"hint": "Automatically apply damages & healings. Targets must be selected before the action is made. Bypass users permissions."
"hint": "Automatically apply damages & healings. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
},
"effect": {
"label": "Apply Effects",
"hint": "Automatically apply effects. Targets must be selected before the action is made. Bypass users permissions."
"hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
}
}
},

View file

@ -146,9 +146,9 @@ export default class AdversarySheet extends DHBaseActorSheet {
title: `Reaction Roll: ${this.actor.name}`,
headerTitle: 'Adversary Reaction Roll',
roll: {
type: 'reaction'
type: 'trait'
},
type: 'trait',
actionType: 'reaction',
hasRoll: true,
data: this.actor.getRollData()
};

View file

@ -208,7 +208,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
prepareBaseConfig(event) {
const config = {
event,
title: `${this.item.name}: ${game.i18n.localize(this.name)}`,
title: `${this.item instanceof CONFIG.Actor.documentClass ? "" : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
source: {
item: this.item._id,
action: this._id,

View file

@ -95,7 +95,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
}
registerTargetHook() {
if (!this.parent.isAuthor) return;
if (!this.parent.isAuthor || !this.hasTarget) return;
if (this.targetMode && this.parent.targetHook !== null) {
Hooks.off('targetToken', this.parent.targetHook);
return (this.parent.targetHook = null);

View file

@ -111,7 +111,7 @@ export default class CostField extends fields.ArrayField {
c.key === 'fear'
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
: resources[c.key].isReversed
? resources[c.key].max
? resources[c.key].max - resources[c.key].value
: resources[c.key].value;
if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step);
return c;

View file

@ -62,6 +62,7 @@ export default class DamageField extends fields.SchemaField {
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
if(!damageResult) return false;
config.damage = damageResult.damage;
config.message ??= damageConfig.message;
}
/**
@ -71,7 +72,7 @@ export default class DamageField extends fields.SchemaField {
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async applyDamage(config, targets = null, force = false) {
targets ??= config.targets;
targets ??= config.targets.filter(target => target.hit);
if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
for (let target of targets) {
const actor = fromUuidSync(target.actorId);

View file

@ -1,3 +1,5 @@
import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs";
const fields = foundry.data.fields;
export default class EffectsField extends fields.ArrayField {
@ -31,8 +33,14 @@ export default class EffectsField extends fields.ArrayField {
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
}
if(EffectsField.getAutomation() || force) {
targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
await emitAsGM(
GMUpdateEvent.UpdateEffect,
EffectsField.applyEffects.bind(this),
targets,
this.uuid
);
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
}
}

View file

@ -71,29 +71,6 @@ export class DHActionRollData extends foundry.abstract.DataModel {
const modifiers = [];
if (!this.parent?.actor) return modifiers;
switch (this.parent.actor.type) {
case 'character':
// const spellcastingTrait =
// this.type === 'spellcast'
// ? (this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility')
// : null;
// const trait =
// this.useDefault || !this.trait
// ? (spellcastingTrait ?? this.parent.item.system.attack?.roll?.trait ?? 'agility')
// : this.trait;
// if (
// this.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
// this.type === CONFIG.DH.GENERAL.rollTypes.trait.id
// )
// modifiers.push({
// label: `DAGGERHEART.CONFIG.Traits.${trait}.name`,
// value: this.parent.actor.system.traits[trait].value
// });
// else if (this.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id)
// modifiers.push({
// label: `DAGGERHEART.CONFIG.RollTypes.spellcast.name`,
// value: this.parent.actor.system.spellcastModifier
// });
break;
case 'companion':
case 'adversary':
if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)

View file

@ -35,6 +35,7 @@ export default class SaveField extends fields.SchemaField {
static async execute(config, targets = null, force = false) {
if(!config.hasSave) return;
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
if(!message) {
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
roll._evaluated = true;
@ -42,8 +43,8 @@ export default class SaveField extends fields.SchemaField {
}
if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
SaveField.rollAllSave.call(this, targets, config.event, message);
}
await SaveField.rollAllSave.call(this, targets, config.event, message);
} else return false;
}
/**
@ -54,22 +55,32 @@ export default class SaveField extends fields.SchemaField {
* @param {ChatMessage} message The ChatMessage the triggered button comes from.
*/
static async rollAllSave(targets, event, message) {
if(!targets || !game.user.isGM) return;
targets.forEach(target => {
const actor = fromUuidSync(target.actorId);
if(actor) {
const rollSave = game.user === actor.owner ?
SaveField.rollSave.call(this, actor, event, message)
: actor.owner
.query('reactionRoll', {
actionId: this.uuid,
actorId: actor.uuid,
event,
message
});
rollSave.then(result => SaveField.updateSaveMessage.call(this, result, message, target.id));
}
});
if(!targets) return;
return new Promise(resolve => {
const aPromise = [];
targets.forEach(target => {
aPromise.push(
new Promise(async subResolve => {
const actor = fromUuidSync(target.actorId);
if(actor) {
const rollSave = game.user === actor.owner ?
SaveField.rollSave.call(this, actor, event)
: actor.owner
.query('reactionRoll', {
actionId: this.uuid,
actorId: actor.uuid,
event,
message
});
const result = await rollSave;
await SaveField.updateSaveMessage.call(this, result, message, target.id);
subResolve();
} else subResolve();
})
)
});
Promise.all(aPromise).then(result => resolve());
})
}
/**
@ -92,13 +103,13 @@ export default class SaveField extends fields.SchemaField {
roll: {
trait: this.save.trait,
difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty,
type: 'reaction'
type: 'trait'
},
type: 'trait',
actionType: 'reaction',
hasRoll: true,
data: actor.getRollData()
};
if(SaveField.getAutomation() == CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false };
if(SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false };
return actor.diceRoll(rollConfig);
}
@ -108,10 +119,10 @@ export default class SaveField extends fields.SchemaField {
* @param {object} message ChatMessage to update
* @param {string} targetId Token ID
*/
static updateSaveMessage(result, message, targetId) {
static async updateSaveMessage(result, message, targetId) {
if (!result) return;
const updateMsg = function(message, targetId, result) {
setTimeout(async () => {
const updateMsg = async function(message, targetId, result) {
// setTimeout(async () => {
const chatMessage = ui.chat.collection.get(message._id),
changes = {
flags: {
@ -127,11 +138,11 @@ export default class SaveField extends fields.SchemaField {
}
};
await chatMessage.update(changes);
}, 100);
// }, 100);
};
if (game.modules.get('dice-so-nice')?.active)
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(() => updateMsg(message, targetId, result));
else updateMsg(message, targetId, result);
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result));
else await updateMsg(message, targetId, result);
}
/**

View file

@ -28,6 +28,7 @@ export default class DHRoll extends Roll {
static async buildConfigure(config = {}, message = {}) {
config.hooks = [...this.getHooks(), ''];
config.dialog ??= {};
for (const hook of config.hooks) {
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
}

View file

@ -154,7 +154,7 @@ export default class DualityRoll extends D20Roll {
applyBaseBonus() {
const modifiers = super.applyBaseBonus();
if (this.options.roll.trait && this.data.traits[this.options.roll.trait])
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait])
modifiers.unshift({
label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
value: this.data.traits[this.options.roll.trait].value

View file

@ -14,7 +14,7 @@ export default class DhpActor extends Actor {
get owner() {
const user =
this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active);
if (!user) return game.user.isGM ? game.user : null;
if (!user) return game.users.activeGM;
return user;
}

View file

@ -147,12 +147,16 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
async onRollDamage(event) {
event.stopPropagation();
this.system.action?.workflow.get("damage")?.execute(this.system, this._id, true);
const config = foundry.utils.deepClone(this.system);
config.event = event;
this.system.action?.workflow.get("damage")?.execute(config, this._id, true);
}
async onApplyDamage(event) {
event.stopPropagation();
const targets = this.filterPermTargets(this.system.hitTargets);
const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
if (this.system.onSave) {
const pendingingSaves = targets.filter(t => t.saved.success === null);
@ -169,7 +173,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
this.consumeOnSuccess();
this.system.action?.workflow.get("applyDamage")?.execute(this.system, targets, true);
this.system.action?.workflow.get("applyDamage")?.execute(config, targets, true);
}
async onRollSave(event) {
@ -198,17 +202,21 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
async onRollAllSave(event) {
event.stopPropagation();
if (!game.user.isGM) return;
const targets = this.system.hitTargets;
this.system.action?.workflow.get("save")?.execute(this.system, targets, true);
const targets = this.system.hitTargets,
config = foundry.utils.deepClone(this.system);
config.event = event;
this.system.action?.workflow.get("save")?.execute(config, targets, true);
}
async onApplyEffect(event) {
event.stopPropagation();
const targets = this.filterPermTargets(this.system.hitTargets);
const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
if (targets.length === 0)
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
this.consumeOnSuccess();
this.system.action?.workflow.get("effects")?.execute(this.system, targets, true);
this.system.action?.workflow.get("effects")?.execute(config, targets, true);
}
filterPermTargets(targets) {

View file

@ -22,6 +22,7 @@ export const socketEvent = {
export const GMUpdateEvent = {
UpdateDocument: 'DhGMUpdateDocument',
UpdateEffect: 'DhGMUpdateEffect',
UpdateSetting: 'DhGMUpdateSetting',
UpdateFear: 'DhGMUpdateFear',
UpdateSaveMessage: 'DhGMUpdateSaveMessage'
@ -37,9 +38,12 @@ export const registerSocketHooks = () => {
const document = data.uuid ? await fromUuid(data.uuid) : null;
switch (data.action) {
case GMUpdateEvent.UpdateDocument:
if (document && data.update) {
if (document && data.update)
await document.update(data.update);
}
break;
case GMUpdateEvent.UpdateEffect:
if (document && data.update)
await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.update);
break;
case GMUpdateEvent.UpdateSetting:
await game.settings.set(CONFIG.DH.id, data.uuid, data.update);

View file

@ -56,7 +56,7 @@
<div class="nest-inputs">
<input type="hidden" name="{{../path}}damage.parts.{{realIndex}}.valueAlt.multiplier" value="flat">
{{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." realIndex ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}
{{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child"}}
{{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child" localize=true}}
{{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." realIndex ".valueAlt.bonus") localize=true classes="inline-child"}}
</div>
</fieldset>
@ -70,7 +70,7 @@
{{#*inline "formula"}}
{{#unless dmg.base}}
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox"}}
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox" localize=true}}
{{/unless}}
{{#if source.custom.enabled}}
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." realIndex "." target ".custom.formula") localize=true}}
@ -79,8 +79,8 @@
{{#unless @root.isNPC}}
{{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}}
{{/unless}}
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") }}{{/if}}
{{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice")}}
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") localize=true }}{{/if}}
{{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice") localize=true}}
{{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}}
</div>
{{/if}}

View file

@ -126,10 +126,12 @@
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
</select>
</div>
<span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span>
<select name="trait">
{{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" localize=true}}
</select>
{{#if abilities}}
<span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span>
<select name="trait">
{{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" localize=true}}
</select>
{{/if}}
{{/unless}}
{{#if @root.rallyDie.length}}
<span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span>

View file

@ -13,10 +13,10 @@
{{#each damage as | roll index | }}
<fieldset>
<legend>
{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}} <div class="roll-formula">{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}</div>{{#if (and (eq index "hitPoints")../isDirect)}} <div class="roll-formula">{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}</div>{{/if}}
{{#if ../hasHealing}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')}}{{else}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}{{/if}} <div class="roll-formula">{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}</div>{{#if (and (eq index "hitPoints") ../isDirect)}} <div class="roll-formula">{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}</div>{{/if}}
</legend>
{{#each roll.parts}}
{{#if damageTypes.length}}
{{#if (and (not @root.hasHealing) damageTypes.length)}}
<label class="roll-part-header"><span>
{{#each damageTypes}}
{{localize (concat 'DAGGERHEART.CONFIG.ArmorFeature.' this '.name')}}

View file

@ -40,7 +40,7 @@
</div>
{{/if}}
</div>
{{#if (and ../hasSave (or hit (not @root.hasRoll)))}}
{{#if (and ../hasSave (or hit (not @root.hasRoll) (not @root.targetMode)))}}
<div class="target-save{{#if saved.result includeZero=true}} is-rolled{{/if}}" data-perm-id="{{actorId}}">
<i class="fa-solid {{#if saved.result includeZero=true}}{{#if saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}} fa-lg"></i>
</div>