[PR] [Feature] 590 - Daggerheart Menu (#1007)

* Added menu with refresh tools

* Replaced menu icon
This commit is contained in:
WBHarry 2025-09-07 00:30:29 +02:00 committed by GitHub
parent eefb28c312
commit f1b6d3851d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 730 additions and 350 deletions

View file

@ -34,8 +34,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
this.extraSchemas.forEach(s => {
let clsField = this.getActionField(s);
if (clsField)
schemaFields[s] = new clsField();
if (clsField) schemaFields[s] = new clsField();
});
return schemaFields;
@ -43,7 +42,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/**
* Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property.
*
*
* Each step can be called individually as long as needed config is provided.
* Ex: <action>.workflow.get("damage").execute(config)
* @returns {Map}
@ -53,8 +52,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
this.constructor.extraSchemas.forEach(s => {
let clsField = this.constructor.getActionField(s);
if (clsField?.execute) {
workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) } );
if( s === "damage" ) workflow.set("applyDamage", { order: 75, execute: clsField.applyDamage.bind(this) } );
workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) });
if (s === 'damage')
workflow.set('applyDamage', { order: 75, execute: clsField.applyDamage.bind(this) });
}
});
return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order));
@ -64,9 +64,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
* Getter returning the workflow property or creating it the first time the property is called
*/
get workflow() {
if ( this.hasOwnProperty("_workflow") ) return this._workflow;
if (this.hasOwnProperty('_workflow')) return this._workflow;
const workflow = Object.freeze(this.defineWorkflow());
Object.defineProperty(this, "_workflow", {value: workflow, writable: false});
Object.defineProperty(this, '_workflow', { value: workflow, writable: false });
return workflow;
}
@ -117,7 +117,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/**
* Prepare base data based on Action Type & Parent Type
* @param {object} parent
* @param {object} parent
* @returns {object}
*/
static getSourceConfig(parent) {
@ -167,9 +167,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
* @param {object} config Config object usually created from prepareConfig method
*/
async executeWorkflow(config) {
for(const [key, part] of this.workflow) {
for (const [key, part] of this.workflow) {
if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return;
if(await part.execute(config) === false) return;
if ((await part.execute(config)) === false) return;
if (Hooks.call(`${CONFIG.DH.id}.post${key.capitalize()}Action`, this, config) === false) return;
}
}
@ -185,7 +185,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (this.chatDisplay) await this.toChat();
let config = this.prepareConfig(event);
if(!config) return;
if (!config) return;
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
@ -194,7 +194,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
config = await D20RollDialog.configure(null, config);
if (!config) return;
}
// Execute the Action Worflow in order based of schema fields
await this.executeWorkflow(config);
@ -206,7 +206,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/**
* Create the basic config common to every action type
* @param {Event} event Event from the button used to trigger the Action
* @returns {object}
* @returns {object}
*/
prepareBaseConfig(event) {
const config = {
@ -236,13 +236,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/**
* Create the config for that action used for its workflow
* @param {Event} event Event from the button used to trigger the Action
* @returns {object}
* @returns {object}
*/
prepareConfig(event) {
const config = this.prepareBaseConfig(event);
for(const clsField of Object.values(this.schema.fields)) {
if (clsField?.prepareConfig)
if(clsField.prepareConfig.call(this, config) === false) return false;
for (const clsField of Object.values(this.schema.fields)) {
if (clsField?.prepareConfig) if (clsField.prepareConfig.call(this, config) === false) return false;
}
return config;
}
@ -260,11 +259,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
* Consume Action configured resources & uses.
* That method is only used when we want those resources to be consumed outside of the use method workflow.
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
* @param {boolean} successCost
* @param {boolean} successCost
*/
async consume(config, successCost = false) {
await this.workflow.get("cost")?.execute(config, successCost);
await this.workflow.get("uses")?.execute(config, successCost);
await this.workflow.get('cost')?.execute(config, successCost);
await this.workflow.get('uses')?.execute(config, successCost);
if (config.roll && !config.roll.success && successCost) {
setTimeout(() => {
@ -291,13 +290,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
get hasDamage() {
return this.damage?.parts?.length && this.type !== 'healing'
return this.damage?.parts?.length && this.type !== 'healing';
}
get hasHealing() {
return this.damage?.parts?.length && this.type === 'healing'
return this.damage?.parts?.length && this.type === 'healing';
}
get hasSave() {
return !!this.save?.trait;
}

View file

@ -1,9 +1,9 @@
import DHAbilityUse from "./abilityUse.mjs";
import DHActorRoll from "./actorRoll.mjs";
import DHAbilityUse from './abilityUse.mjs';
import DHActorRoll from './actorRoll.mjs';
export const config = {
abilityUse: DHAbilityUse,
adversaryRoll: DHActorRoll,
damageRoll: DHActorRoll,
dualityRoll: DHActorRoll
abilityUse: DHAbilityUse,
adversaryRoll: DHActorRoll,
damageRoll: DHActorRoll,
dualityRoll: DHActorRoll
};

View file

@ -58,10 +58,8 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
const actionActor = this.actionActor,
actionItem = this.actionItem;
if (!this.source.action) return null;
if(actionItem)
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
else if(actionActor?.system.attack?._id === this.source.action)
return actionActor.system.attack
if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action);
else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack;
return null;
}

View file

@ -1,4 +1,4 @@
import BeastformDialog from "../../../applications/dialogs/beastformDialog.mjs";
import BeastformDialog from '../../../applications/dialogs/beastformDialog.mjs';
const fields = foundry.data.fields;
@ -49,14 +49,14 @@ export default class BeastformField extends fields.SchemaField {
return await BeastformField.transform.call(this, selected, evolved, hybrid);
}
/**
* Update Action Workflow config object.
* Must be called within Action context.
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
*/
prepareConfig(config) {
if(this.actor.effects.find(x => x.type === 'beastform')) {
if (this.actor.effects.find(x => x.type === 'beastform')) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied'));
return false;
}
@ -73,10 +73,10 @@ export default class BeastformField extends fields.SchemaField {
/**
* TODO by Harry
* @param {*} selectedForm
* @param {*} evolvedData
* @param {*} hybridData
* @returns
* @param {*} selectedForm
* @param {*} evolvedData
* @param {*} hybridData
* @returns
*/
static async transform(selectedForm, evolvedData, hybridData) {
const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject();

View file

@ -30,8 +30,13 @@ export default class DamageField extends fields.SchemaField {
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async execute(config, messageId = null, force = false) {
if(!this.hasDamage && !this.hasHealing) return;
if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return;
if (!this.hasDamage && !this.hasHealing) return;
if (
this.hasRoll &&
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
!force
)
return;
let formulas = this.damage.parts.map(p => ({
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
@ -51,9 +56,10 @@ export default class DamageField extends fields.SchemaField {
};
delete damageConfig.evaluate;
if(DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false;
if (DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id)
damageConfig.dialog.configure = false;
if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod;
damageConfig.source.message = config.message?._id ?? messageId;
damageConfig.directDamage = !!damageConfig.source?.message;
@ -61,7 +67,7 @@ export default class DamageField extends fields.SchemaField {
// await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
if(!damageResult) return false;
if (!damageResult) return false;
config.damage = damageResult.damage;
config.message ??= damageConfig.message;
}
@ -70,19 +76,15 @@ export default class DamageField extends fields.SchemaField {
* Apply Damage/Healing Action Worflow part.
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
* @param {*[]} targets Arrays of targets to bypass pre-selected ones.
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
* @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.filter(target => target.hit);
if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
if (!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
for (let target of targets) {
const actor = fromUuidSync(target.actorId);
if(!actor) continue;
if (
!config.hasHealing &&
config.onSave &&
target.saved?.success === true
) {
if (!actor) continue;
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1;
Object.entries(config.damage).forEach(([k, v]) => {
v.total = 0;
@ -97,17 +99,17 @@ export default class DamageField extends fields.SchemaField {
else actor.takeDamage(config.damage, config.isDirect);
}
}
/**
* Return value or valueAlt from damage part
* Must be called within Action context or similar.
* @param {object} part Damage Part
* @param {object} part Damage Part
* @param {object} data Action getRollData
* @returns Formula value object
*/
static getFormulaValue(part, data) {
let formulaValue = part.value;
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
const isAdversary = this.actor.type === 'adversary';
@ -123,8 +125,8 @@ export default class DamageField extends fields.SchemaField {
* Prepare formulas for Damage Roll
* Must be called within Action context or similar.
* @param {object[]} formulas Array of formatted formulas object
* @param {object} data Action getRollData
* @returns
* @param {object} data Action getRollData
* @returns
*/
static formatFormulas(formulas, data) {
const formattedFormulas = [];
@ -145,16 +147,25 @@ export default class DamageField extends fields.SchemaField {
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
*/
static getAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
);
}
/**
* Return the automation setting for applyDamage method for current user role
* @returns {boolean} If applyDamage should be triggered automatically
*/
static getApplyAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
);
}
}

View file

@ -1,4 +1,4 @@
import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs";
import { emitAsGM, GMUpdateEvent } from '../../../systemRegistration/socket.mjs';
const fields = foundry.data.fields;
@ -25,21 +25,16 @@ export default class EffectsField extends fields.ArrayField {
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async execute(config, targets = null, force = false) {
if(!config.hasEffect) return;
if (!config.hasEffect) return;
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
if(!message) {
if (!message) {
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
roll._evaluated = true;
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
}
if(EffectsField.getAutomation() || force) {
if (EffectsField.getAutomation() || force) {
targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
await emitAsGM(
GMUpdateEvent.UpdateEffect,
EffectsField.applyEffects.bind(this),
targets,
this.uuid
);
await emitAsGM(GMUpdateEvent.UpdateEffect, EffectsField.applyEffects.bind(this), targets, this.uuid);
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
}
}
@ -53,8 +48,7 @@ export default class EffectsField extends fields.ArrayField {
if (!this.effects?.length || !targets?.length) return;
let effects = this.effects;
targets.forEach(async token => {
if (this.hasSave && token.saved.success === true)
effects = this.effects.filter(e => e.onSave === true);
if (this.hasSave && token.saved.success === true) effects = this.effects.filter(e => e.onSave === true);
if (!effects.length) return;
effects.forEach(async e => {
const actor = canvas.tokens.get(token.id)?.actor,
@ -96,6 +90,11 @@ export default class EffectsField extends fields.ArrayField {
* @returns {boolean} If execute should be triggered automatically
*/
static getAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.effect.players)
);
}
}

View file

@ -8,7 +8,7 @@ export default class MacroField extends fields.DocumentUUIDField {
/** @inheritDoc */
constructor(context = {}) {
super({ type: "Macro" }, context);
super({ type: 'Macro' }, context);
}
/**

View file

@ -1,14 +1,13 @@
const fields = foundry.data.fields;
export default class RangeField extends fields.StringField {
/** @inheritDoc */
constructor(context = {}) {
const options = {
choices: CONFIG.DH.GENERAL.range,
required: false,
blank: true,
label: "DAGGERHEART.GENERAL.range"
label: 'DAGGERHEART.GENERAL.range'
};
super(options, context);
}

View file

@ -5,7 +5,12 @@ export class DHActionRollData extends foundry.abstract.DataModel {
static defineSchema() {
return {
type: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.GENERAL.rollTypes }),
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities, label: "DAGGERHEART.GENERAL.Trait.single" }),
trait: new fields.StringField({
nullable: true,
initial: null,
choices: CONFIG.DH.ACTOR.abilities,
label: 'DAGGERHEART.GENERAL.Trait.single'
}),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
advState: new fields.StringField({
@ -86,14 +91,14 @@ export class DHActionRollData extends foundry.abstract.DataModel {
}
get rollTrait() {
if(this.parent?.actor?.type !== "character") return null;
if (this.parent?.actor?.type !== 'character') return null;
switch (this.type) {
case CONFIG.DH.GENERAL.rollTypes.spellcast.id:
return this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility';
case CONFIG.DH.GENERAL.rollTypes.attack.id:
case CONFIG.DH.GENERAL.rollTypes.trait.id:
return this.useDefault || !this.trait
? this.parent.item.system.attack?.roll?.trait ?? 'agility'
? (this.parent.item.system.attack?.roll?.trait ?? 'agility')
: this.trait;
default:
return null;
@ -118,21 +123,21 @@ export default class RollField extends fields.EmbeddedDataField {
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
*/
static async execute(config) {
if(!config.hasRoll) return;
if (!config.hasRoll) return;
config = await this.actor.diceRoll(config);
if(!config) return false;
if (!config) return false;
}
/**
* Update Action Workflow config object.
* Must be called within Action context.
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
*/
prepareConfig(config) {
if(!config.hasRoll) return;
if (!config.hasRoll) return;
config.dialog.configure = RollField.getAutomation() ? !config.dialog.configure : config.dialog.configure;
const roll = {
baseModifiers: this.roll.getModifier(),
label: 'Attack',
@ -152,6 +157,11 @@ export default class RollField extends fields.EmbeddedDataField {
* @returns {boolean} If execute should be triggered automatically
*/
static getAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.roll.players)
);
}
}

View file

@ -1,4 +1,4 @@
import { abilities } from "../../../config/actorConfig.mjs";
import { abilities } from '../../../config/actorConfig.mjs';
const fields = foundry.data.fields;
@ -33,15 +33,15 @@ export default class SaveField extends fields.SchemaField {
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async execute(config, targets = null, force = false) {
if(!config.hasSave) return;
if (!config.hasSave) return;
let message = config.message ?? ui.chat.collection.get(config.parent?._id);
if(!message) {
if (!message) {
const roll = new CONFIG.Dice.daggerheart.DHRoll('');
roll._evaluated = true;
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
}
if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
if (SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
await SaveField.rollAllSave.call(this, targets, config.event, message);
} else return false;
@ -52,35 +52,35 @@ export default class SaveField extends fields.SchemaField {
* Must be called within Action context.
* @param {object[]} targets Array of formatted targets.
* @param {Event} event Triggering event
* @param {ChatMessage} message The ChatMessage the triggered button comes from.
* @param {ChatMessage} message The ChatMessage the triggered button comes from.
*/
static async rollAllSave(targets, event, message) {
if(!targets) return;
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
});
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());
})
});
}
/**
@ -93,10 +93,10 @@ export default class SaveField extends fields.SchemaField {
static async rollSave(actor, event) {
if (!actor) return;
const title = actor.isNPC
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(abilities[this.save.trait]?.label)
}),
? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll')
: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(abilities[this.save.trait]?.label)
}),
rollConfig = {
event,
title,
@ -109,7 +109,8 @@ export default class SaveField extends fields.SchemaField {
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);
}
@ -117,31 +118,32 @@ export default class SaveField extends fields.SchemaField {
* Update a Roll ChatMessage for a token according to his Reaction Roll result.
* @param {object} result Result from the Reaction Roll
* @param {object} message ChatMessage to update
* @param {string} targetId Token ID
* @param {string} targetId Token ID
*/
static async updateSaveMessage(result, message, targetId) {
if (!result) return;
const updateMsg = async function(message, targetId, result) {
const updateMsg = async function (message, targetId, result) {
// setTimeout(async () => {
const chatMessage = ui.chat.collection.get(message._id),
changes = {
flags: {
[game.system.id]: {
reactionRolls: {
[targetId]:
{
result: result.roll.total,
success: result.roll.success
}
const chatMessage = ui.chat.collection.get(message._id),
changes = {
flags: {
[game.system.id]: {
reactionRolls: {
[targetId]: {
result: result.roll.total,
success: result.roll.success
}
}
}
};
await chatMessage.update(changes);
}
};
await chatMessage.update(changes);
// }, 100);
};
if (game.modules.get('dice-so-nice')?.active)
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await 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);
}
@ -150,25 +152,29 @@ export default class SaveField extends fields.SchemaField {
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
*/
static getAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.save.players)
);
}
/**
* Send a query to an Actor owner to roll a Reaction Roll then send back the result.
* @param {object} param0
* @param {string} param0.actionId Action ID
* @param {string} param0.actorId Actor ID
* @param {Event} param0.event Triggering event
* @param {ChatMessage} param0.message Chat Message to update
* @returns
* @param {object} param0
* @param {string} param0.actionId Action ID
* @param {string} param0.actorId Actor ID
* @param {Event} param0.event Triggering event
* @param {ChatMessage} param0.message Chat Message to update
* @returns
*/
static rollSaveQuery({ actionId, actorId, event, message }) {
return new Promise(async (resolve, reject) => {
const actor = await fromUuid(actorId),
action = await fromUuid(actionId);
if (!actor || !actor?.isOwner) reject();
SaveField.rollSave.call(action, actor, event, message)
.then(result => resolve(result));
SaveField.rollSave.call(action, actor, event, message).then(result => resolve(result));
});
}
}

View file

@ -1,7 +1,6 @@
const fields = foundry.data.fields;
export default class TargetField extends fields.SchemaField {
/** @inheritDoc */
constructor(options = {}, context = {}) {
const targetFields = {
@ -21,7 +20,7 @@ export default class TargetField extends fields.SchemaField {
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
*/
prepareConfig(config) {
if (!this.target?.type) return config.targets = [];
if (!this.target?.type) return (config.targets = []);
config.hasTarget = true;
let targets;
// If the Action is configured as self-targeted, set targets as the owner.
@ -62,15 +61,11 @@ export default class TargetField extends fields.SchemaField {
* @returns {boolean} If both actors respect the provided type.
*/
static isTargetFriendly(actor, target, type) {
const actorDisposition = actor.token
? actor.token.disposition
: actor.prototypeToken.disposition,
const actorDisposition = actor.token ? actor.token.disposition : actor.prototypeToken.disposition,
targetDisposition = target.document.disposition;
return (
(type === CONFIG.DH.GENERAL.targetTypes.friendly.id &&
actorDisposition === targetDisposition) ||
(type === CONFIG.DH.GENERAL.targetTypes.hostile.id &&
actorDisposition + targetDisposition === 0)
(type === CONFIG.DH.GENERAL.targetTypes.friendly.id && actorDisposition === targetDisposition) ||
(type === CONFIG.DH.GENERAL.targetTypes.hostile.id && actorDisposition + targetDisposition === 0)
);
}

View file

@ -7,7 +7,7 @@ export default class UsesField extends fields.SchemaField {
* Action Workflow order
*/
static order = 160;
/** @inheritDoc */
constructor(options = {}, context = {}) {
const usesFields = {
@ -62,7 +62,7 @@ export default class UsesField extends fields.SchemaField {
/**
* Prepare Uses object for Action Workflow
* Must be called within Action context.
* @param {object} uses
* @param {object} uses
* @returns {object}
*/
static calcUses(uses) {
@ -77,7 +77,7 @@ export default class UsesField extends fields.SchemaField {
/**
* Check if the Action still get atleast one unspent uses.
* Must be called within Action context.
* @param {*} uses
* @param {*} uses
* @returns {boolean}
*/
static hasUses(uses) {

View file

@ -1,26 +1,27 @@
export default class DhAppearance extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ["DAGGERHEART.SETTINGS.Appearance"];
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
static defineSchema() {
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
// helper to create dice style schema
const diceStyle = ({ fg, bg, outline, edge }) => new SchemaField({
foreground: new ColorField({ required: true, initial: fg }),
background: new ColorField({ required: true, initial: bg }),
outline: new ColorField({ required: true, initial: outline }),
edge: new ColorField({ required: true, initial: edge }),
texture: new StringField({ initial: 'astralsea', required: true, blank: false }),
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
material: new StringField({ initial: 'metal', required: true, blank: false }),
system: new StringField({ initial: 'standard', required: true, blank: false })
});
const diceStyle = ({ fg, bg, outline, edge }) =>
new SchemaField({
foreground: new ColorField({ required: true, initial: fg }),
background: new ColorField({ required: true, initial: bg }),
outline: new ColorField({ required: true, initial: outline }),
edge: new ColorField({ required: true, initial: edge }),
texture: new StringField({ initial: 'astralsea', required: true, blank: false }),
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
material: new StringField({ initial: 'metal', required: true, blank: false }),
system: new StringField({ initial: 'standard', required: true, blank: false })
});
return {
displayFear: new StringField({
required: true,
choices: CONFIG.DH.GENERAL.fearDisplay,
initial: CONFIG.DH.GENERAL.fearDisplay.token.value,
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
}),
diceSoNice: new SchemaField({
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
@ -39,7 +40,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
target: new BooleanField()
}),
hideAttribution: new BooleanField(),
showGenericStatusEffects: new BooleanField({ initial: true }),
showGenericStatusEffects: new BooleanField({ initial: true })
};
}
}

View file

@ -97,13 +97,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
damage: new fields.SchemaField({
gm: new fields.StringField({
required: true,
initial: "never",
initial: 'never',
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
label: 'DAGGERHEART.GENERAL.gm'
}),
players: new fields.StringField({
required: true,
initial: "never",
initial: 'never',
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
label: 'DAGGERHEART.GENERAL.player.plurial'
})
@ -111,13 +111,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
save: new fields.SchemaField({
gm: new fields.StringField({
required: true,
initial: "never",
initial: 'never',
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
label: 'DAGGERHEART.GENERAL.gm'
}),
players: new fields.StringField({
required: true,
initial: "never",
initial: 'never',
choices: CONFIG.DH.SETTINGS.actionAutomationChoices,
label: 'DAGGERHEART.GENERAL.player.plurial'
})