mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-14 04:31:07 +01:00
Merged with development
This commit is contained in:
commit
c32e812803
120 changed files with 2380 additions and 469 deletions
|
|
@ -10,6 +10,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.config = config;
|
||||
this.config.experiences = [];
|
||||
this.reactionOverride = config.actionType === 'reaction';
|
||||
this.selectedEffects = this.config.bonusEffects;
|
||||
|
||||
if (config.source?.action) {
|
||||
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
|
||||
|
|
@ -35,6 +36,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
selectExperience: this.selectExperience,
|
||||
toggleReaction: this.toggleReaction,
|
||||
toggleTagTeamRoll: this.toggleTagTeamRoll,
|
||||
toggleSelectedEffect: this.toggleSelectedEffect,
|
||||
submitRoll: this.submitRoll
|
||||
},
|
||||
form: {
|
||||
|
|
@ -76,6 +78,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
icon
|
||||
}));
|
||||
|
||||
context.hasSelectedEffects = Boolean(this.selectedEffects && Object.keys(this.selectedEffects).length);
|
||||
context.selectedEffects = this.selectedEffects;
|
||||
|
||||
this.config.costs ??= [];
|
||||
if (this.config.costs?.length) {
|
||||
const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(
|
||||
|
|
@ -208,6 +213,11 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.render();
|
||||
}
|
||||
|
||||
static toggleSelectedEffect(_event, button) {
|
||||
this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async submitRoll() {
|
||||
await this.close({ submitted: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
|
||||
this.roll = roll;
|
||||
this.config = config;
|
||||
this.selectedEffects = this.config.bonusEffects;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
@ -20,6 +21,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleSelectedEffect: this.toggleSelectedEffect,
|
||||
submitRoll: this.submitRoll
|
||||
},
|
||||
form: {
|
||||
|
|
@ -57,6 +59,9 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
icon
|
||||
}));
|
||||
context.modifiers = this.config.modifiers;
|
||||
context.hasSelectedEffects = Boolean(Object.keys(this.selectedEffects).length);
|
||||
context.selectedEffects = this.selectedEffects;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +74,11 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
this.render();
|
||||
}
|
||||
|
||||
static toggleSelectedEffect(_event, button) {
|
||||
this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async submitRoll() {
|
||||
await this.close({ submitted: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,27 +93,29 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
|
||||
getRefreshables() {
|
||||
const actionItems = this.actor.items.filter(x => this.actor.system.isItemAvailable(x)).reduce((acc, x) => {
|
||||
if (x.system.actions) {
|
||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||
if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) {
|
||||
acc.push({
|
||||
title: x.name,
|
||||
name: action.name,
|
||||
uuid: action.uuid
|
||||
});
|
||||
const actionItems = this.actor.items
|
||||
.filter(x => this.actor.system.isItemAvailable(x))
|
||||
.reduce((acc, x) => {
|
||||
if (x.system.actions) {
|
||||
const recoverable = x.system.actions.reduce((acc, action) => {
|
||||
if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) {
|
||||
acc.push({
|
||||
title: x.name,
|
||||
name: action.name,
|
||||
uuid: action.uuid
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (recoverable) {
|
||||
acc.push(...recoverable);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (recoverable) {
|
||||
acc.push(...recoverable);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
return acc;
|
||||
}, []);
|
||||
const resourceItems = this.actor.items.reduce((acc, x) => {
|
||||
if (
|
||||
x.system.resource &&
|
||||
|
|
@ -189,7 +191,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
}));
|
||||
});
|
||||
});
|
||||
const characters = game.actors.filter(x => x.type === 'character')
|
||||
const characters = game.actors
|
||||
.filter(x => x.type === 'character')
|
||||
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
|
||||
.filter(x => x.uuid !== this.actor.uuid);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig {
|
||||
// static DEFAULT_OPTIONS = {
|
||||
// ...super.DEFAULT_OPTIONS,
|
||||
// form: {
|
||||
// handler: this.updateData,
|
||||
// closeOnSubmit: true
|
||||
// }
|
||||
// };
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
Hooks.on(socketEvent.Refresh, ({ refreshType }) => {
|
||||
if (refreshType === RefreshType.Scene) {
|
||||
this.daggerheartFlag = new game.system.api.data.scenes.DHScene(this.document.flags.daggerheart);
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
actions: {
|
||||
...super.DEFAULT_OPTIONS.actions,
|
||||
removeSceneEnvironment: DhSceneConfigSettings.#removeSceneEnvironment
|
||||
}
|
||||
};
|
||||
|
||||
static buildParts() {
|
||||
const { footer, tabs, ...parts } = super.PARTS;
|
||||
const tmpParts = {
|
||||
// tabs,
|
||||
tabs: { template: 'systems/daggerheart/templates/scene/tabs.hbs' },
|
||||
...parts,
|
||||
dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' },
|
||||
|
|
@ -28,27 +40,45 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
|||
|
||||
static TABS = DhSceneConfigSettings.buildTabs();
|
||||
|
||||
async _preRender(context, options) {
|
||||
await super._preFirstRender(context, options);
|
||||
this.daggerheartFlag = new game.system.api.data.scenes.DHScene(this.document.flags.daggerheart);
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
switch (partId) {
|
||||
case 'dh':
|
||||
htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => {
|
||||
const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, {
|
||||
rangeMeasurement: { setting: event.target.value }
|
||||
});
|
||||
this.document.flags.daggerheart = flagData;
|
||||
this.daggerheartFlag.updateSource({ rangeMeasurement: { setting: event.target.value } });
|
||||
this.render();
|
||||
});
|
||||
|
||||
const dragArea = htmlElement.querySelector('.scene-environments');
|
||||
if (dragArea) dragArea.ondrop = this._onDrop.bind(this);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
|
||||
const item = await foundry.utils.fromUuid(data.uuid);
|
||||
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
|
||||
await this.daggerheartFlag.updateSource({
|
||||
sceneEnvironments: [...this.daggerheartFlag.sceneEnvironments, data.uuid]
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
context = await super._preparePartContext(partId, context, options);
|
||||
switch (partId) {
|
||||
case 'dh':
|
||||
context.data = new game.system.api.data.scenes.DHScene(canvas.scene.flags.daggerheart);
|
||||
context.data = this.daggerheartFlag;
|
||||
context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules);
|
||||
break;
|
||||
}
|
||||
|
|
@ -56,8 +86,24 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
|
|||
return context;
|
||||
}
|
||||
|
||||
// static async updateData(event, _, formData) {
|
||||
// const data = foundry.utils.expandObject(formData.object);
|
||||
// this.close(data);
|
||||
// }
|
||||
static async #removeSceneEnvironment(_event, button) {
|
||||
await this.daggerheartFlag.updateSource({
|
||||
sceneEnvironments: this.daggerheartFlag.sceneEnvironments.filter(
|
||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
||||
)
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _processSubmitData(event, form, submitData, options) {
|
||||
submitData.flags.daggerheart = this.daggerheartFlag.toObject();
|
||||
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
|
||||
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
|
||||
submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null;
|
||||
}
|
||||
}
|
||||
|
||||
super._processSubmitData(event, form, submitData, options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
|
||||
this.action = action;
|
||||
this.openSection = null;
|
||||
this.openTrigger = this.action.triggers.length > 0 ? 0 : null;
|
||||
}
|
||||
|
||||
get title() {
|
||||
|
|
@ -15,7 +16,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'dh-style', 'dialog', 'max-800'],
|
||||
classes: ['daggerheart', 'dh-style', 'action-config', 'dialog', 'max-800'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-wrench',
|
||||
resizable: false
|
||||
|
|
@ -29,13 +30,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
removeElement: this.removeElement,
|
||||
editEffect: this.editEffect,
|
||||
addDamage: this.addDamage,
|
||||
removeDamage: this.removeDamage
|
||||
removeDamage: this.removeDamage,
|
||||
editDoc: this.editDoc,
|
||||
addTrigger: this.addTrigger,
|
||||
removeTrigger: this.removeTrigger,
|
||||
expandTrigger: this.expandTrigger
|
||||
},
|
||||
form: {
|
||||
handler: this.updateForm,
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false
|
||||
}
|
||||
},
|
||||
dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
@ -55,6 +61,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
effect: {
|
||||
id: 'effect',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
||||
},
|
||||
trigger: {
|
||||
id: 'trigger',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/trigger.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -82,10 +92,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
id: 'effect',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.GENERAL.Tabs.effects'
|
||||
},
|
||||
trigger: {
|
||||
active: false,
|
||||
cssClass: '',
|
||||
group: 'primary',
|
||||
id: 'trigger',
|
||||
icon: null,
|
||||
label: 'DAGGERHEART.GENERAL.Tabs.triggers'
|
||||
}
|
||||
};
|
||||
|
||||
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects'];
|
||||
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects', 'summon'];
|
||||
|
||||
_getTabs(tabs) {
|
||||
for (const v of Object.values(tabs)) {
|
||||
|
|
@ -96,9 +114,24 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
return tabs;
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => {
|
||||
element.addEventListener('change', this.updateSummonCount.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options, 'action');
|
||||
context.source = this.action.toObject(true);
|
||||
|
||||
context.summons = [];
|
||||
for (const summon of context.source.summon ?? []) {
|
||||
const actor = await foundry.utils.fromUuid(summon.actorUUID);
|
||||
context.summons.push({ actor, count: summon.count });
|
||||
}
|
||||
|
||||
context.openSection = this.openSection;
|
||||
context.tabs = this._getTabs(this.constructor.TABS);
|
||||
context.config = CONFIG.DH;
|
||||
|
|
@ -111,6 +144,16 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
|
||||
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
|
||||
context.hasRoll = this.action.hasRoll;
|
||||
context.triggers = context.source.triggers.map((trigger, index) => {
|
||||
const { hint, returns, usesActor } = CONFIG.DH.TRIGGER.triggers[trigger.trigger];
|
||||
return {
|
||||
...trigger,
|
||||
hint,
|
||||
returns,
|
||||
usesActor,
|
||||
revealed: this.openTrigger === index
|
||||
};
|
||||
});
|
||||
|
||||
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
|
||||
context.tierOptions = [
|
||||
|
|
@ -181,8 +224,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
}
|
||||
|
||||
static async updateForm(event, _, formData) {
|
||||
const submitData = this._prepareSubmitData(event, formData),
|
||||
data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
||||
const submitData = this._prepareSubmitData(event, formData);
|
||||
|
||||
const data = foundry.utils.mergeObject(this.action.toObject(), submitData);
|
||||
this.action = await this.action.update(data);
|
||||
|
||||
this.sheetUpdate?.(this.action);
|
||||
|
|
@ -201,12 +245,26 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
static removeElement(event, button) {
|
||||
event.stopPropagation();
|
||||
const data = this.action.toObject(),
|
||||
key = event.target.closest('[data-key]').dataset.key,
|
||||
index = button.dataset.index;
|
||||
key = event.target.closest('[data-key]').dataset.key;
|
||||
|
||||
// Prefer explicit index, otherwise find by uuid
|
||||
let index = button?.dataset.index;
|
||||
if (index === undefined || index === null || index === '') {
|
||||
const uuid = button?.dataset.uuid ?? button?.dataset.itemUuid;
|
||||
index = data[key].findIndex(e => (e?.actorUUID ?? e?.uuid) === uuid);
|
||||
if (index === -1) return;
|
||||
} else index = Number(index);
|
||||
|
||||
data[key].splice(index, 1);
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static async editDoc(_event, target) {
|
||||
const element = target.closest('[data-item-uuid]');
|
||||
const doc = (await foundry.utils.fromUuid(element.dataset.itemUuid)) ?? null;
|
||||
if (doc) return doc.sheet.render({ force: true });
|
||||
}
|
||||
|
||||
static addDamage(_event) {
|
||||
if (!this.action.damage.parts) return;
|
||||
const data = this.action.toObject(),
|
||||
|
|
@ -224,6 +282,69 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static addTrigger() {
|
||||
const data = this.action.toObject();
|
||||
data.triggers.push({
|
||||
trigger: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
||||
triggeringActor: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id
|
||||
});
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static async removeTrigger(_event, button) {
|
||||
const trigger = CONFIG.DH.TRIGGER.triggers[this.action.triggers[button.dataset.index].trigger];
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.deleteTriggerTitle')
|
||||
},
|
||||
content: game.i18n.format('DAGGERHEART.ACTIONS.Config.deleteTriggerContent', {
|
||||
trigger: game.i18n.localize(trigger.label)
|
||||
})
|
||||
});
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
const data = this.action.toObject();
|
||||
data.triggers = data.triggers.filter((_, index) => index !== Number.parseInt(button.dataset.index));
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
static async expandTrigger(_event, button) {
|
||||
const index = Number.parseInt(button.dataset.index);
|
||||
const toggle = (element, codeMirror) => {
|
||||
codeMirror.classList.toggle('revealed');
|
||||
const button = element.querySelector('a > i');
|
||||
button.classList.toggle('fa-angle-up');
|
||||
button.classList.toggle('fa-angle-down');
|
||||
};
|
||||
|
||||
const fieldset = button.closest('fieldset');
|
||||
const codeMirror = fieldset.querySelector('.code-mirror-wrapper');
|
||||
toggle(fieldset, codeMirror);
|
||||
|
||||
if (this.openTrigger !== null && this.openTrigger !== index) {
|
||||
const previouslyExpanded = fieldset
|
||||
.closest(`section`)
|
||||
.querySelector(`fieldset[data-index="${this.openTrigger}"]`);
|
||||
const codeMirror = previouslyExpanded.querySelector('.code-mirror-wrapper');
|
||||
toggle(previouslyExpanded, codeMirror);
|
||||
this.openTrigger = index;
|
||||
} else if (this.openTrigger === index) {
|
||||
this.openTrigger = null;
|
||||
} else {
|
||||
this.openTrigger = index;
|
||||
}
|
||||
}
|
||||
|
||||
updateSummonCount(event) {
|
||||
event.stopPropagation();
|
||||
const wrapper = event.target.closest('.summon-count-wrapper');
|
||||
const index = wrapper.dataset.index;
|
||||
const data = this.action.toObject();
|
||||
data.summon[index].count = event.target.value;
|
||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||
}
|
||||
|
||||
/** Specific implementation in extending classes **/
|
||||
static async addEffect(_event) {}
|
||||
static removeEffect(_event, _button) {}
|
||||
|
|
@ -233,4 +354,29 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
|||
this.tabGroups.primary = 'base';
|
||||
await super.close(options);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.getDragEventData(event);
|
||||
const item = await foundry.utils.fromUuid(data.uuid);
|
||||
if (!(item instanceof game.system.api.documents.DhpActor)) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.invalidDrop'));
|
||||
return;
|
||||
}
|
||||
|
||||
const actionData = this.action.toObject();
|
||||
let countvalue = 1;
|
||||
for (const entry of actionData.summon) {
|
||||
if (entry.actorUUID === data.uuid) {
|
||||
entry.count += 1;
|
||||
countvalue = entry.count;
|
||||
await this.constructor.updateForm.bind(this)(null, null, {
|
||||
object: foundry.utils.flattenObject(actionData)
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
actionData.summon.push({ actorUUID: data.uuid, count: countvalue });
|
||||
await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
|
||||
dropSelector: null
|
||||
}
|
||||
],
|
||||
]
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
|
|
@ -185,7 +185,6 @@ export default class AdversarySheet extends DHBaseActorSheet {
|
|||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
advanceResourceDie: CharacterSheet.#advanceResourceDie,
|
||||
cancelBeastform: CharacterSheet.#cancelBeastform,
|
||||
useDowntime: this.useDowntime,
|
||||
viewParty: CharacterSheet.#viewParty,
|
||||
viewParty: CharacterSheet.#viewParty
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
|
|
@ -338,15 +338,20 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
const type = 'effect';
|
||||
const cls = game.system.api.models.actions.actionsTypes[type];
|
||||
const action = new cls({
|
||||
...cls.getSourceConfig(doc.system),
|
||||
type: type,
|
||||
chatDisplay: false,
|
||||
cost: [{
|
||||
key: 'stress',
|
||||
value: doc.system.recallCost
|
||||
}]
|
||||
}, { parent: doc.system });
|
||||
const action = new cls(
|
||||
{
|
||||
...cls.getSourceConfig(doc.system),
|
||||
type: type,
|
||||
chatDisplay: false,
|
||||
cost: [
|
||||
{
|
||||
key: 'stress',
|
||||
value: doc.system.recallCost
|
||||
}
|
||||
]
|
||||
},
|
||||
{ parent: doc.system }
|
||||
);
|
||||
const config = await action.use(event);
|
||||
if (config) {
|
||||
return doc.update({ 'system.inVault': false });
|
||||
|
|
@ -707,8 +712,10 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
|
||||
ability: abilityLabel
|
||||
}),
|
||||
effects: Array.from(await this.document.allApplicableEffects()),
|
||||
roll: {
|
||||
trait: button.dataset.attribute
|
||||
trait: button.dataset.attribute,
|
||||
type: 'trait'
|
||||
},
|
||||
hasRoll: true,
|
||||
actionType: 'action',
|
||||
|
|
@ -718,6 +725,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
})
|
||||
};
|
||||
const result = await this.document.diceRoll(config);
|
||||
if (!result) return;
|
||||
|
||||
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
|
||||
const costResources = result.costs
|
||||
|
|
@ -822,7 +830,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
static async #toggleVault(_event, button) {
|
||||
const doc = await getDocFromElement(button);
|
||||
const { available } = this.document.system.loadoutSlot;
|
||||
if (doc.system.inVault && !available) {
|
||||
if (doc.system.inVault && !available && !doc.system.loadoutIgnore) {
|
||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached'));
|
||||
}
|
||||
|
||||
|
|
@ -900,32 +908,32 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
return;
|
||||
}
|
||||
|
||||
const buttons = parties.map((p) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.classList.add("plain");
|
||||
const img = document.createElement("img");
|
||||
const buttons = parties.map(p => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.classList.add('plain');
|
||||
const img = document.createElement('img');
|
||||
img.src = p.img;
|
||||
button.append(img);
|
||||
const name = document.createElement("span");
|
||||
const name = document.createElement('span');
|
||||
name.textContent = p.name;
|
||||
button.append(name);
|
||||
button.addEventListener("click", () => {
|
||||
button.addEventListener('click', () => {
|
||||
p.sheet?.render({ force: true });
|
||||
game.tooltip.dismissLockedTooltips();
|
||||
});
|
||||
return button;
|
||||
});
|
||||
|
||||
const html = document.createElement("div");
|
||||
html.classList.add("party-list");
|
||||
const html = document.createElement('div');
|
||||
html.classList.add('party-list');
|
||||
html.append(...buttons);
|
||||
|
||||
|
||||
game.tooltip.dismissLockedTooltips();
|
||||
game.tooltip.activate(target, {
|
||||
html,
|
||||
locked: true,
|
||||
})
|
||||
locked: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export default function DHApplicationMixin(Base) {
|
|||
const step = event.key === 'ArrowUp' ? 1 : event.key === 'ArrowDown' ? -1 : 0;
|
||||
if (step !== 0) {
|
||||
handleUpdate(step);
|
||||
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
deltaInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ export default function DHApplicationMixin(Base) {
|
|||
if (deltaInput === document.activeElement) {
|
||||
event.preventDefault();
|
||||
handleUpdate(Math.sign(-1 * event.deltaY));
|
||||
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
deltaInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
},
|
||||
{ passive: false }
|
||||
|
|
@ -236,7 +236,7 @@ export default function DHApplicationMixin(Base) {
|
|||
// Handle contenteditable
|
||||
for (const input of htmlElement.querySelectorAll('[contenteditable][data-property]')) {
|
||||
const property = input.dataset.property;
|
||||
input.addEventListener("blur", () => {
|
||||
input.addEventListener('blur', () => {
|
||||
const selection = document.getSelection();
|
||||
if (input.contains(selection.anchorNode)) {
|
||||
selection.empty();
|
||||
|
|
@ -244,12 +244,12 @@ export default function DHApplicationMixin(Base) {
|
|||
this.document.update({ [property]: input.textContent });
|
||||
});
|
||||
|
||||
input.addEventListener("keydown", event => {
|
||||
if (event.key === "Enter") input.blur();
|
||||
input.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') input.blur();
|
||||
});
|
||||
|
||||
// Chrome sometimes add <br>, which aren't a problem for the value but are for the placeholder
|
||||
input.addEventListener("input", () => input.querySelectorAll("br").forEach((i) => i.remove()));
|
||||
input.addEventListener('input', () => input.querySelectorAll('br').forEach(i => i.remove()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +585,9 @@ export default function DHApplicationMixin(Base) {
|
|||
if (!doc || !descriptionElement) continue;
|
||||
|
||||
// localize the description (idk if it's still necessary)
|
||||
const description = game.i18n.localize(doc.system?.description ?? doc.description);
|
||||
const description = doc.system?.getEnrichedDescription
|
||||
? await doc.system.getEnrichedDescription()
|
||||
: game.i18n.localize(doc.system?.description ?? doc.description);
|
||||
|
||||
// Enrich the description and attach it;
|
||||
const isAction = doc.documentName === 'Action';
|
||||
|
|
@ -736,7 +738,7 @@ export default function DHApplicationMixin(Base) {
|
|||
};
|
||||
if (inVault) data['system.inVault'] = true;
|
||||
if (disabled) data.disabled = true;
|
||||
if (type === "domainCard" && parent?.system.domains?.length) {
|
||||
if (type === 'domainCard' && parent?.system.domains?.length) {
|
||||
data.system.domain = parent.system.domains[0];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,16 +76,10 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
|
|||
/**@inheritdoc */
|
||||
async _preparePartContext(partId, context, options) {
|
||||
await super._preparePartContext(partId, context, options);
|
||||
const { TextEditor } = foundry.applications.ux;
|
||||
|
||||
switch (partId) {
|
||||
case 'description':
|
||||
const value = foundry.utils.getProperty(this.document, 'system.description') ?? '';
|
||||
context.enrichedDescription = await TextEditor.enrichHTML(value, {
|
||||
relativeTo: this.item,
|
||||
rollData: this.item.getRollData(),
|
||||
secrets: this.item.isOwner
|
||||
});
|
||||
context.enrichedDescription = await this.document.system.getEnrichedDescription();
|
||||
break;
|
||||
case 'effects':
|
||||
await this._prepareEffectsContext(context, options);
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ export { default as DhCombatTracker } from './combatTracker.mjs';
|
|||
export { default as DhEffectsDisplay } from './effectsDisplay.mjs';
|
||||
export { default as DhFearTracker } from './fearTracker.mjs';
|
||||
export { default as DhHotbar } from './hotbar.mjs';
|
||||
export { default as DhSceneNavigation } from './sceneNavigation.mjs';
|
||||
export { ItemBrowser } from './itemBrowser.mjs';
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
async actionUseButton(event, message) {
|
||||
const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset;
|
||||
const targetUuid = event.currentTarget.closest('.action-use-button-parent').querySelector('select')?.value;
|
||||
const parent = await foundry.utils.fromUuid(targetUuid || message.system.actor)
|
||||
const parent = await foundry.utils.fromUuid(targetUuid || message.system.actor);
|
||||
|
||||
const actionType = message.system.moves[moveIndex].actions[actionIndex];
|
||||
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
|
||||
|
|
|
|||
89
module/applications/ui/sceneNavigation.mjs
Normal file
89
module/applications/ui/sceneNavigation.mjs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhSceneNavigation extends foundry.applications.ui.SceneNavigation {
|
||||
/** @inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: ['faded-ui', 'flexcol', 'scene-navigation'],
|
||||
actions: {
|
||||
openSceneEnvironment: DhSceneNavigation.#openSceneEnvironment
|
||||
}
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
static PARTS = {
|
||||
scenes: {
|
||||
root: true,
|
||||
template: 'systems/daggerheart/templates/ui/sceneNavigation/scene-navigation.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
/** @inheritdoc */
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
|
||||
const extendScenes = scenes =>
|
||||
scenes.map(x => {
|
||||
const scene = game.scenes.get(x.id);
|
||||
if (!scene.flags.daggerheart) return x;
|
||||
|
||||
const daggerheartInfo = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart);
|
||||
const environments = daggerheartInfo.sceneEnvironments.filter(
|
||||
x => x && x.testUserPermission(game.user, 'LIMITED')
|
||||
);
|
||||
const hasEnvironments = environments.length > 0;
|
||||
return {
|
||||
...x,
|
||||
hasEnvironments,
|
||||
environmentImage: hasEnvironments ? environments[0].img : null,
|
||||
environments: environments
|
||||
};
|
||||
});
|
||||
context.scenes.active = extendScenes(context.scenes.active);
|
||||
context.scenes.inactive = extendScenes(context.scenes.inactive);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #openSceneEnvironment(event, button) {
|
||||
const scene = game.scenes.get(button.dataset.sceneId);
|
||||
const sceneEnvironments = new game.system.api.data.scenes.DHScene(
|
||||
scene.flags.daggerheart
|
||||
).sceneEnvironments.filter(x => x.testUserPermission(game.user, 'LIMITED'));
|
||||
|
||||
if (sceneEnvironments.length === 1 || event.shiftKey) {
|
||||
sceneEnvironments[0].sheet.render(true);
|
||||
} else {
|
||||
new foundry.applications.ux.ContextMenu.implementation(
|
||||
button,
|
||||
'.scene-environment',
|
||||
sceneEnvironments.map(environment => ({
|
||||
name: environment.name,
|
||||
callback: () => {
|
||||
if (scene.flags.daggerheart.sceneEnvironments[0] !== environment.uuid) {
|
||||
const newEnvironments = scene.flags.daggerheart.sceneEnvironments;
|
||||
const newFirst = newEnvironments.splice(
|
||||
newEnvironments.findIndex(x => x === environment.uuid)
|
||||
)[0];
|
||||
newEnvironments.unshift(newFirst);
|
||||
emitAsGM(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
scene.update.bind(scene),
|
||||
{ 'flags.daggerheart.sceneEnvironments': newEnvironments },
|
||||
scene.uuid
|
||||
);
|
||||
}
|
||||
|
||||
environment.sheet.render({ force: true });
|
||||
}
|
||||
})),
|
||||
{
|
||||
jQuery: false,
|
||||
fixed: true
|
||||
}
|
||||
);
|
||||
|
||||
CONFIG.ux.ContextMenu.triggerContextMenu(event, '.scene-environment');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,11 +96,11 @@ export default class DHContextMenu extends foundry.applications.ux.ContextMenu {
|
|||
* Trigger a context menu event in response to a normal click on a additional options button.
|
||||
* @param {PointerEvent} event
|
||||
*/
|
||||
static triggerContextMenu(event) {
|
||||
static triggerContextMenu(event, altSelector) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const { clientX, clientY } = event;
|
||||
const selector = '[data-item-uuid]';
|
||||
const selector = altSelector ?? '[data-item-uuid]';
|
||||
const target = event.target.closest(selector) ?? event.currentTarget.closest(selector);
|
||||
target?.dispatchEvent(
|
||||
new PointerEvent('contextmenu', {
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * as placeables from './placeables/_module.mjs';
|
||||
export { default as DhTokenLayer } from './tokens.mjs';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||
/** @inheritdoc */
|
||||
async _draw(options) {
|
||||
await super._draw(options);
|
||||
|
||||
if (this.document.flags.daggerheart?.createPlacement)
|
||||
this.previewHelp ||= this.addChild(this.#drawPreviewHelp());
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
async _drawEffects() {
|
||||
this.effects.renderable = false;
|
||||
|
|
@ -34,7 +42,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
this.renderFlags.set({ refreshEffects: true });
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns the distance from this token to another token object.
|
||||
* This value is corrected to handle alternate token sizes and other grid types
|
||||
* according to the diagonal rules.
|
||||
|
|
@ -47,11 +55,11 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
const destinationPoint = target.center;
|
||||
|
||||
// Compute for gridless. This version returns circular edge to edge + grid distance,
|
||||
// so that tokens that are touching return 5.
|
||||
// so that tokens that are touching return 5.
|
||||
if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) {
|
||||
const boundsCorrection = canvas.grid.distance / canvas.grid.size;
|
||||
const originRadius = this.bounds.width * boundsCorrection / 2;
|
||||
const targetRadius = target.bounds.width * boundsCorrection / 2;
|
||||
const originRadius = (this.bounds.width * boundsCorrection) / 2;
|
||||
const targetRadius = (target.bounds.width * boundsCorrection) / 2;
|
||||
const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance;
|
||||
return distance - originRadius - targetRadius + canvas.grid.distance;
|
||||
}
|
||||
|
|
@ -61,11 +69,11 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint);
|
||||
const adjustedOriginPoint = canvas.grid.getTopLeftPoint({
|
||||
x: originEdge.x + Math.sign(originPoint.x - originEdge.x),
|
||||
y: originEdge.y + Math.sign(originPoint.y - originEdge.y)
|
||||
y: originEdge.y + Math.sign(originPoint.y - originEdge.y)
|
||||
});
|
||||
const adjustDestinationPoint = canvas.grid.getTopLeftPoint({
|
||||
x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x),
|
||||
y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y)
|
||||
y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y)
|
||||
});
|
||||
return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance;
|
||||
}
|
||||
|
|
@ -94,7 +102,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
|
||||
/** Tests if the token is at least adjacent with another, with some leeway for diagonals */
|
||||
isAdjacentWith(token) {
|
||||
return this.distanceTo(token) <= (canvas.grid.distance * 1.5);
|
||||
return this.distanceTo(token) <= canvas.grid.distance * 1.5;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
@ -132,4 +140,25 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
|||
bar.position.set(0, posY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a helptext for previews as a text object
|
||||
* @returns {PreciseText} The Text object for the preview helper
|
||||
*/
|
||||
#drawPreviewHelp() {
|
||||
const { uiScale } = canvas.dimensions;
|
||||
|
||||
const textStyle = CONFIG.canvasTextStyle.clone();
|
||||
textStyle.fontSize = 18;
|
||||
textStyle.wordWrapWidth = this.w * 2.5;
|
||||
textStyle.fontStyle = 'italic';
|
||||
|
||||
const helpText = new PreciseText(
|
||||
`(${game.i18n.localize('DAGGERHEART.UI.Tooltip.previewTokenHelp')})`,
|
||||
textStyle
|
||||
);
|
||||
helpText.anchor.set(helpText.width / 900, 1);
|
||||
helpText.scale.set(uiScale, uiScale);
|
||||
return helpText;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
module/canvas/tokens.mjs
Normal file
16
module/canvas/tokens.mjs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {
|
||||
async _createPreview(createData, options) {
|
||||
if (options.actor) {
|
||||
const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes;
|
||||
if (options.actor?.system.metadata.usesSize) {
|
||||
const tokenSize = tokenSizes[options.actor.system.size];
|
||||
if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) {
|
||||
createData.width = tokenSize;
|
||||
createData.height = tokenSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super._createPreview(createData, options);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,3 +10,4 @@ export * as itemConfig from './itemConfig.mjs';
|
|||
export * as settingsConfig from './settingsConfig.mjs';
|
||||
export * as systemConfig from './system.mjs';
|
||||
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
|
||||
export * as triggerConfig from './triggerConfig.mjs';
|
||||
|
|
|
|||
|
|
@ -496,6 +496,8 @@ export const diceTypes = {
|
|||
d20: 'd20'
|
||||
};
|
||||
|
||||
export const dieFaces = [4, 6, 8, 10, 12, 20];
|
||||
|
||||
export const multiplierTypes = {
|
||||
prof: 'Proficiency',
|
||||
cast: 'Spellcast',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
const hooksConfig = {
|
||||
export const hooksConfig = {
|
||||
effectDisplayToggle: 'DHEffectDisplayToggle'
|
||||
};
|
||||
|
||||
export default hooksConfig;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import * as SETTINGS from './settingsConfig.mjs';
|
|||
import * as EFFECTS from './effectConfig.mjs';
|
||||
import * as ACTIONS from './actionConfig.mjs';
|
||||
import * as FLAGS from './flagsConfig.mjs';
|
||||
import HOOKS from './hooksConfig.mjs';
|
||||
import * as HOOKS from './hooksConfig.mjs';
|
||||
import * as TRIGGER from './triggerConfig.mjs';
|
||||
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
|
||||
|
||||
export const SYSTEM_ID = 'daggerheart';
|
||||
|
|
@ -24,5 +25,6 @@ export const SYSTEM = {
|
|||
ACTIONS,
|
||||
FLAGS,
|
||||
HOOKS,
|
||||
TRIGGER,
|
||||
ITEMBROWSER
|
||||
};
|
||||
|
|
|
|||
42
module/config/triggerConfig.mjs
Normal file
42
module/config/triggerConfig.mjs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* hints and returns are intentionally not translated. They are programatical terms and best understood in english */
|
||||
export const triggers = {
|
||||
dualityRoll: {
|
||||
id: 'dualityRoll',
|
||||
usesActor: true,
|
||||
args: ['roll', 'actor'],
|
||||
label: 'DAGGERHEART.CONFIG.Triggers.dualityRoll.label',
|
||||
hint: 'this: Action, roll: DhRoll, actor: DhActor',
|
||||
returns: '{ updates: [{ key, value, total }] }'
|
||||
},
|
||||
fearRoll: {
|
||||
id: 'fearRoll',
|
||||
usesActor: true,
|
||||
args: ['roll', 'actor'],
|
||||
label: 'DAGGERHEART.CONFIG.Triggers.fearRoll.label',
|
||||
hint: 'this: Action, roll: DhRoll, actor: DhActor',
|
||||
returns: '{ updates: [{ key, value, total }] }'
|
||||
},
|
||||
postDamageReduction: {
|
||||
id: 'postDamageReduction',
|
||||
usesActor: true,
|
||||
args: ['damageUpdates', 'actor'],
|
||||
label: 'DAGGERHEART.CONFIG.Triggers.postDamageReduction.label',
|
||||
hint: 'damageUpdates: ResourceUpdates, actor: DhActor',
|
||||
returns: '{ updates: [{ originActor: this.actor, updates: [{ key, value, total }] }] }'
|
||||
}
|
||||
};
|
||||
|
||||
export const triggerActorTargetType = {
|
||||
any: {
|
||||
id: 'any',
|
||||
label: 'DAGGERHEART.CONFIG.TargetTypes.any'
|
||||
},
|
||||
self: {
|
||||
id: 'self',
|
||||
label: 'DAGGERHEART.CONFIG.TargetTypes.self'
|
||||
},
|
||||
other: {
|
||||
id: 'other',
|
||||
label: 'DAGGERHEART.CONFIG.TargetTypes.other'
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ import DhpActor from '../../documents/actor.mjs';
|
|||
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
|
||||
import { ActionMixin } from '../fields/actionField.mjs';
|
||||
import { originItemField } from '../chat-message/actorRoll.mjs';
|
||||
import TriggerField from '../fields/triggerField.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
|
|
@ -34,7 +35,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
nullable: false,
|
||||
required: true
|
||||
}),
|
||||
targetUuid: new fields.StringField({ initial: undefined })
|
||||
targetUuid: new fields.StringField({ initial: undefined }),
|
||||
triggers: new fields.ArrayField(new TriggerField())
|
||||
};
|
||||
|
||||
this.extraSchemas.forEach(s => {
|
||||
|
|
@ -164,7 +166,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
*/
|
||||
getRollData(data = {}) {
|
||||
const actorData = this.actor ? this.actor.getRollData(false) : {};
|
||||
|
||||
actorData.result = data.roll?.total ?? 1;
|
||||
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||
|
|
@ -197,6 +198,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
let config = this.prepareConfig(event);
|
||||
if (!config) return;
|
||||
|
||||
await this.addEffects(config);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return;
|
||||
|
||||
// Display configuration window if necessary
|
||||
|
|
@ -263,6 +266,16 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
return config;
|
||||
}
|
||||
|
||||
/** */
|
||||
async addEffects(config) {
|
||||
let effects = [];
|
||||
if (this.actor) {
|
||||
effects = Array.from(await this.actor.allApplicableEffects());
|
||||
}
|
||||
|
||||
config.effects = effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to know if a configuration dialog must be shown or not when there is no roll.
|
||||
* @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
|
||||
|
|
@ -343,6 +356,10 @@ export class ResourceUpdateMap extends Map {
|
|||
}
|
||||
|
||||
addResources(resources) {
|
||||
if (!resources?.length) return;
|
||||
const invalidResources = resources.some(resource => !resource.key);
|
||||
if (invalidResources) return;
|
||||
|
||||
for (const resource of resources) {
|
||||
if (!resource.key) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,5 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
|
||||
export default class DHSummonAction extends DHBaseAction {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
documentUUID: new fields.DocumentUUIDField({ type: 'Actor' })
|
||||
};
|
||||
}
|
||||
|
||||
async trigger(event, ...args) {
|
||||
if (!this.canSummon || !canvas.scene) return;
|
||||
}
|
||||
|
||||
get canSummon() {
|
||||
return game.user.can('TOKEN_CREATE');
|
||||
}
|
||||
static extraSchemas = [...super.extraSchemas, 'summon'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,6 +275,24 @@ export default class DhCharacter extends BaseDataActor {
|
|||
})
|
||||
})
|
||||
}),
|
||||
dualityRoll: new fields.SchemaField({
|
||||
defaultHopeDice: new fields.NumberField({
|
||||
nullable: false,
|
||||
required: true,
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||
initial: 12,
|
||||
label: 'DAGGERHEART.ACTORS.Character.defaultHopeDice'
|
||||
}),
|
||||
defaultFearDice: new fields.NumberField({
|
||||
nullable: false,
|
||||
required: true,
|
||||
integer: true,
|
||||
choices: CONFIG.DH.GENERAL.dieFaces,
|
||||
initial: 12,
|
||||
label: 'DAGGERHEART.ACTORS.Character.defaultFearDice'
|
||||
})
|
||||
}),
|
||||
runeWard: new fields.BooleanField({ initial: false }),
|
||||
burden: new fields.SchemaField({
|
||||
ignore: new fields.BooleanField()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import BaseDataActor from './base.mjs';
|
||||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs';
|
||||
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhEnvironment extends BaseDataActor {
|
||||
scenes = new Set();
|
||||
|
||||
/**@override */
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment'];
|
||||
|
||||
|
|
@ -53,6 +56,31 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
}
|
||||
|
||||
isItemValid(source) {
|
||||
return source.type === "feature";
|
||||
return source.type === 'feature';
|
||||
}
|
||||
|
||||
_onUpdate(changes, options, userId) {
|
||||
super._onUpdate(changes, options, userId);
|
||||
for (const scene of this.scenes) {
|
||||
scene.render();
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
for (const scene of this.scenes) {
|
||||
if (game.user.isActiveGM) {
|
||||
const newSceneEnvironments = scene.flags.daggerheart.sceneEnvironments.filter(
|
||||
x => x !== this.parent.uuid
|
||||
);
|
||||
scene.update({ 'flags.daggerheart.sceneEnvironments': newSceneEnvironments }).then(() => {
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Scene });
|
||||
game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.Refresh,
|
||||
data: { refreshType: RefreshType.TagTeamRoll }
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@ export { ActionCollection } from './actionField.mjs';
|
|||
export { default as FormulaField } from './formulaField.mjs';
|
||||
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
|
||||
export { default as TriggerField } from './triggerField.mjs';
|
||||
export { default as MappingField } from './mappingField.mjs';
|
||||
export * as ActionFields from './action/_module.mjs';
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@ export { default as BeastformField } from './beastformField.mjs';
|
|||
export { default as DamageField } from './damageField.mjs';
|
||||
export { default as RollField } from './rollField.mjs';
|
||||
export { default as MacroField } from './macroField.mjs';
|
||||
export { default as SummonField } from './summonField.mjs';
|
||||
|
|
|
|||
89
module/data/fields/action/summonField.mjs
Normal file
89
module/data/fields/action/summonField.mjs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import FormulaField from '../formulaField.mjs';
|
||||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class DHSummonField extends fields.ArrayField {
|
||||
/**
|
||||
* Action Workflow order
|
||||
*/
|
||||
static order = 120;
|
||||
|
||||
constructor(options = {}, context = {}) {
|
||||
const summonFields = new fields.SchemaField({
|
||||
actorUUID: new fields.DocumentUUIDField({
|
||||
type: 'Actor',
|
||||
required: true
|
||||
}),
|
||||
count: new FormulaField({
|
||||
required: true,
|
||||
default: '1'
|
||||
})
|
||||
});
|
||||
super(summonFields, options, context);
|
||||
}
|
||||
|
||||
static async execute() {
|
||||
if (!canvas.scene) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.summon.length === 0) {
|
||||
ui.notifications.warn('No actors configured for this Summon action.');
|
||||
return;
|
||||
}
|
||||
|
||||
const rolls = [];
|
||||
const summonData = [];
|
||||
for (const summon of this.summon) {
|
||||
let count = summon.count;
|
||||
const roll = new Roll(summon.count);
|
||||
if (!roll.isDeterministic) {
|
||||
await roll.evaluate();
|
||||
if (game.modules.get('dice-so-nice')?.active) rolls.push(roll);
|
||||
count = roll.total;
|
||||
}
|
||||
|
||||
const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID));
|
||||
/* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */
|
||||
summon.rolledCount = count;
|
||||
summon.actor = actor.toObject();
|
||||
|
||||
summonData.push({ actor, count: count });
|
||||
}
|
||||
|
||||
if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true)));
|
||||
|
||||
this.actor.sheet?.minimize();
|
||||
DHSummonField.handleSummon(summonData, this.actor);
|
||||
}
|
||||
|
||||
/* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */
|
||||
static getWorldActor(baseActor) {
|
||||
const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`];
|
||||
if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) {
|
||||
const worldActorCopy = game.actors.find(x => x.name === baseActor.name);
|
||||
return worldActorCopy ?? baseActor;
|
||||
}
|
||||
|
||||
return baseActor;
|
||||
}
|
||||
|
||||
static async handleSummon(summonData, actionActor, summonIndex = 0) {
|
||||
const summon = summonData[summonIndex];
|
||||
const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, {
|
||||
name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}`
|
||||
});
|
||||
|
||||
if (!result) return actionActor.sheet?.maximize();
|
||||
summon.actor = result.actor;
|
||||
|
||||
summon.count--;
|
||||
if (summon.count <= 0) {
|
||||
summonIndex++;
|
||||
if (summonIndex === summonData.length) return actionActor.sheet?.maximize();
|
||||
}
|
||||
|
||||
DHSummonField.handleSummon(summonData, actionActor, summonIndex);
|
||||
}
|
||||
}
|
||||
|
|
@ -267,7 +267,8 @@ export function ActionMixin(Base) {
|
|||
action: {
|
||||
name: this.name,
|
||||
img: this.baseAction ? this.parent.parent.img : this.img,
|
||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10']
|
||||
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
|
||||
summon: this.summon
|
||||
},
|
||||
itemOrigin: this.item,
|
||||
description: this.description || (this.item instanceof Item ? this.item.system.description : '')
|
||||
|
|
|
|||
24
module/data/fields/triggerField.mjs
Normal file
24
module/data/fields/triggerField.mjs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export default class TriggerField extends foundry.data.fields.SchemaField {
|
||||
constructor(context) {
|
||||
super(
|
||||
{
|
||||
trigger: new foundry.data.fields.StringField({
|
||||
nullable: false,
|
||||
blank: false,
|
||||
initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
||||
choices: CONFIG.DH.TRIGGER.triggers,
|
||||
label: 'DAGGERHEART.CONFIG.Triggers.triggerType'
|
||||
}),
|
||||
triggeringActorType: new foundry.data.fields.StringField({
|
||||
nullable: false,
|
||||
blank: false,
|
||||
initial: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id,
|
||||
choices: CONFIG.DH.TRIGGER.triggerActorTargetType,
|
||||
label: 'DAGGERHEART.CONFIG.Triggers.triggeringActorType'
|
||||
}),
|
||||
command: new foundry.data.fields.JavaScriptField({ async: true })
|
||||
},
|
||||
context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,21 @@ export default class DHArmor extends AttachableItem {
|
|||
);
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async getDescriptionData() {
|
||||
const baseDescription = this.description;
|
||||
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
|
||||
const features = this.armorFeatures.map(x => allFeatures[x.value]);
|
||||
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
|
||||
{ features }
|
||||
);
|
||||
|
||||
return { prefix, value: baseDescription, suffix: null };
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
|
||||
*/
|
||||
|
||||
import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
|
||||
import { ActionsField } from '../fields/actionField.mjs';
|
||||
import FormulaField from '../fields/formulaField.mjs';
|
||||
|
||||
|
|
@ -124,6 +124,33 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
|
||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
||||
* @returns {string}
|
||||
*/
|
||||
async getDescriptionData(_options) {
|
||||
return { prefix: null, value: this.description, suffix: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enriched and augmented description for the item.
|
||||
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
|
||||
* @returns {string}
|
||||
*/
|
||||
async getEnrichedDescription() {
|
||||
if (!this.metadata.hasDescription) return '';
|
||||
|
||||
const { prefix, value, suffix } = await this.getDescriptionData();
|
||||
const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
|
||||
|
||||
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
|
||||
relativeTo: this,
|
||||
rollData: this.getRollData(),
|
||||
secrets: this.isOwner
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
|
||||
* @param {object} [options] - Options which modify the getRollData method.
|
||||
|
|
@ -135,6 +162,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
|||
return data;
|
||||
}
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
|
||||
for (const action of this.actions ?? []) {
|
||||
if (!action.actor) continue;
|
||||
|
||||
const actionsToRegister = [];
|
||||
for (let i = 0; i < action.triggers.length; i++) {
|
||||
const trigger = action.triggers[i];
|
||||
const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger];
|
||||
const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`);
|
||||
actionsToRegister.push(fn.bind(action));
|
||||
if (i === action.triggers.length - 1)
|
||||
game.system.registeredTriggers.registerTriggers(
|
||||
trigger.trigger,
|
||||
action.actor?.uuid,
|
||||
trigger.triggeringActorType,
|
||||
this.parent.uuid,
|
||||
actionsToRegister
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
// Skip if no initial action is required or actions already exist
|
||||
if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,21 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
required: true,
|
||||
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
|
||||
}),
|
||||
inVault: new fields.BooleanField({ initial: false })
|
||||
inVault: new fields.BooleanField({ initial: false }),
|
||||
vaultActive: new fields.BooleanField({
|
||||
required: true,
|
||||
nullable: false,
|
||||
initial: false
|
||||
}),
|
||||
loadoutIgnore: new fields.BooleanField({
|
||||
required: true,
|
||||
nullable: false,
|
||||
initial: false
|
||||
}),
|
||||
domainTouched: new fields.NumberField({
|
||||
nullable: true,
|
||||
initial: null
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +52,19 @@ export default class DHDomainCard extends BaseDataItem {
|
|||
return game.i18n.localize(allDomainData[this.domain].label);
|
||||
}
|
||||
|
||||
get isVaultSupressed() {
|
||||
return this.inVault && !this.vaultActive;
|
||||
}
|
||||
|
||||
get isDomainTouchedSuppressed() {
|
||||
if (!this.parent.system.domainTouched || this.parent.parent?.type !== 'character') return false;
|
||||
|
||||
const matchingDomainCards = this.parent.parent.items.filter(
|
||||
item => !item.system.inVault && item.system.domain === this.parent.system.domain
|
||||
).length;
|
||||
return matchingDomainCards < this.parent.system.domainTouched;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**@override */
|
||||
|
|
|
|||
|
|
@ -110,6 +110,21 @@ export default class DHWeapon extends AttachableItem {
|
|||
);
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async getDescriptionData() {
|
||||
const baseDescription = this.description;
|
||||
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
|
||||
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
|
||||
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
|
||||
|
||||
const prefix = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
|
||||
{ features }
|
||||
);
|
||||
|
||||
return { prefix, value: baseDescription, suffix: null };
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
|
||||
|
||||
/* Foundry does not add any system data for subtyped Scenes. The data model is therefore used by instantiating a new instance of it for sceneConfigSettings.mjs.
|
||||
Needed dataprep and lifetime hooks are handled in documents/scene.
|
||||
*/
|
||||
export default class DHScene extends foundry.abstract.DataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
|
|
@ -13,7 +18,8 @@ export default class DHScene extends foundry.abstract.DataModel {
|
|||
veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }),
|
||||
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
|
||||
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
|
||||
})
|
||||
}),
|
||||
sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor', prune: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
|
|||
label: 'DAGGERHEART.GENERAL.player.plurial'
|
||||
})
|
||||
})
|
||||
}),
|
||||
triggers: new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({
|
||||
nullable: false,
|
||||
initial: true,
|
||||
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.triggers.enabled.label'
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,15 +127,55 @@ export default class D20Roll extends DHRoll {
|
|||
const modifiers = foundry.utils.deepClone(this.options.roll.baseModifiers) ?? [];
|
||||
|
||||
modifiers.push(
|
||||
...this.getBonus(`roll.${this.options.actionType}`, `${this.options.actionType?.capitalize()} Bonus`)
|
||||
);
|
||||
modifiers.push(
|
||||
...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type?.capitalize()} Bonus`)
|
||||
...this.getBonus(
|
||||
`system.bonuses.roll.${this.options.actionType}`,
|
||||
`${this.options.actionType?.capitalize()} Bonus`
|
||||
)
|
||||
);
|
||||
|
||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
||||
modifiers.push(
|
||||
...this.getBonus(
|
||||
`system.bonuses.roll.${this.options.roll.type}`,
|
||||
`${this.options.roll.type?.capitalize()} Bonus`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
||||
) {
|
||||
modifiers.push(
|
||||
...this.getBonus(`system.bonuses.roll.attack`, `${this.options.roll.type?.capitalize()} Bonus`)
|
||||
);
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
getActionChangeKeys() {
|
||||
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
||||
|
||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
||||
changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`);
|
||||
}
|
||||
|
||||
if (
|
||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
||||
) {
|
||||
changeKeys.add(`system.bonuses.roll.attack`);
|
||||
}
|
||||
|
||||
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) {
|
||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id)
|
||||
changeKeys.add('system.bonuses.roll.trait');
|
||||
}
|
||||
|
||||
return changeKeys;
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
const data = super.postEvaluate(roll, config);
|
||||
data.type = config.actionType;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ export default class DamageRoll extends DHRoll {
|
|||
type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'),
|
||||
options = part ?? this.options;
|
||||
|
||||
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||
if (!this.options.hasHealing) {
|
||||
options.damageTypes?.forEach(t => {
|
||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||
|
|
@ -108,6 +107,29 @@ export default class DamageRoll extends DHRoll {
|
|||
return modifiers;
|
||||
}
|
||||
|
||||
getActionChangeKeys() {
|
||||
const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage');
|
||||
const changeKeys = [];
|
||||
|
||||
for (const roll of this.options.roll) {
|
||||
for (const damageType of roll.damageTypes) changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
||||
}
|
||||
|
||||
const item = this.data.parent.items?.get(this.options.source.item);
|
||||
if (item) {
|
||||
switch (item.type) {
|
||||
case 'weapon':
|
||||
if (!this.options.hasHealing)
|
||||
['primaryWeapon', 'secondaryWeapon'].forEach(w =>
|
||||
changeKeys.push(`system.bonuses.damage.${w}`)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changeKeys;
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
this.options.roll.forEach((part, index) => {
|
||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export default class DHRoll extends Roll {
|
|||
baseTerms = [];
|
||||
constructor(formula, data = {}, options = {}) {
|
||||
super(formula, data, options);
|
||||
options.bonusEffects = this.bonusEffectBuilder();
|
||||
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||
}
|
||||
|
||||
|
|
@ -164,12 +165,18 @@ export default class DHRoll extends Roll {
|
|||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(modifier.join(' + '), this.options.data)
|
||||
];
|
||||
} else {
|
||||
} else if (Number.isNumeric(modifier)) {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||
];
|
||||
} else {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
...this.constructor.parse(modifier, this.options.data)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,18 +192,20 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
getBonus(path, label) {
|
||||
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
|
||||
modifiers = [];
|
||||
if (bonus?.bonus)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.bonus
|
||||
});
|
||||
if (bonus?.dice?.length)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.dice
|
||||
});
|
||||
const modifiers = [];
|
||||
for (const effect of Object.values(this.options.bonusEffects)) {
|
||||
if (!effect.selected) continue;
|
||||
for (const change of effect.changes) {
|
||||
if (!change.key.includes(path)) continue;
|
||||
const changeValue = game.system.api.documents.DhActiveEffect.getChangeValue(
|
||||
this.data,
|
||||
change,
|
||||
effect.origEffect
|
||||
);
|
||||
modifiers.push({ label: label, value: changeValue });
|
||||
}
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
|
|
@ -235,4 +244,28 @@ export default class DHRoll extends Roll {
|
|||
static temporaryModifierBuilder(config) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bonusEffectBuilder() {
|
||||
const changeKeys = this.getActionChangeKeys();
|
||||
return (
|
||||
this.options.effects?.reduce((acc, effect) => {
|
||||
if (effect.changes.some(x => changeKeys.some(key => x.key.includes(key)))) {
|
||||
acc[effect.id] = {
|
||||
id: effect.id,
|
||||
name: effect.name,
|
||||
description: effect.description,
|
||||
changes: effect.changes,
|
||||
origEffect: effect,
|
||||
selected: !effect.disabled
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {}) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
getActionChangeKeys() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,9 +132,12 @@ export default class DualityRoll extends D20Roll {
|
|||
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
|
||||
return;
|
||||
}
|
||||
this.terms[0] = new foundry.dice.terms.Die({ faces: 12 });
|
||||
|
||||
const { defaultHopeDice, defaultFearDice } = this.data.rules.dualityRoll;
|
||||
|
||||
this.terms[0] = new foundry.dice.terms.Die({ faces: defaultHopeDice });
|
||||
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
|
||||
this.terms[2] = new foundry.dice.terms.Die({ faces: 12 });
|
||||
this.terms[2] = new foundry.dice.terms.Die({ faces: defaultFearDice });
|
||||
}
|
||||
|
||||
applyAdvantage() {
|
||||
|
|
@ -190,6 +193,34 @@ export default class DualityRoll extends D20Roll {
|
|||
return super.buildConfigure(config, message);
|
||||
}
|
||||
|
||||
getActionChangeKeys() {
|
||||
const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]);
|
||||
|
||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) {
|
||||
changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`);
|
||||
}
|
||||
|
||||
if (
|
||||
this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
|
||||
(this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage)
|
||||
) {
|
||||
changeKeys.add(`system.bonuses.roll.attack`);
|
||||
}
|
||||
|
||||
if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) {
|
||||
if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id)
|
||||
changeKeys.add('system.bonuses.roll.trait');
|
||||
}
|
||||
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
changeKeys.add(`system.bonuses.roll.${w}`);
|
||||
});
|
||||
|
||||
return changeKeys;
|
||||
}
|
||||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
await super.buildEvaluate(roll, config, message);
|
||||
|
||||
|
|
@ -241,6 +272,30 @@ export default class DualityRoll extends D20Roll {
|
|||
await super.buildPost(roll, config, message);
|
||||
|
||||
await DualityRoll.dualityUpdate(config);
|
||||
await DualityRoll.handleTriggers(roll, config);
|
||||
}
|
||||
|
||||
static async handleTriggers(roll, config) {
|
||||
const updates = [];
|
||||
const dualityUpdates = await game.system.registeredTriggers.runTrigger(
|
||||
CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
|
||||
roll.data?.parent,
|
||||
roll,
|
||||
roll.data?.parent
|
||||
);
|
||||
if (dualityUpdates?.length) updates.push(...dualityUpdates);
|
||||
|
||||
if (config.roll.result.duality === -1) {
|
||||
const fearUpdates = await game.system.registeredTriggers.runTrigger(
|
||||
CONFIG.DH.TRIGGER.triggers.fearRoll.id,
|
||||
roll.data?.parent,
|
||||
roll,
|
||||
roll.data?.parent
|
||||
);
|
||||
if (fearUpdates?.length) updates.push(...fearUpdates);
|
||||
}
|
||||
|
||||
config.resourceUpdates.addResources(updates);
|
||||
}
|
||||
|
||||
static async addDualityResourceUpdates(config) {
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ export { default as DhScene } from './scene.mjs';
|
|||
export { default as DhToken } from './token.mjs';
|
||||
export { default as DhTooltipManager } from './tooltipManager.mjs';
|
||||
export { default as DhTemplateManager } from './templateManager.mjs';
|
||||
export { default as DhTokenManager } from './tokenManager.mjs';
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
}
|
||||
|
||||
if (this.parent?.type === 'domainCard') {
|
||||
return this.parent.system.inVault;
|
||||
const isVaultSupressed = this.parent.system.isVaultSupressed;
|
||||
const domainTouchedSupressed = this.parent.system.isDomainTouchedSuppressed;
|
||||
|
||||
return isVaultSupressed || domainTouchedSupressed;
|
||||
}
|
||||
|
||||
return super.isSuppressed;
|
||||
|
|
@ -106,23 +109,29 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
|
||||
/**@inheritdoc*/
|
||||
static applyField(model, change, field) {
|
||||
const isOriginTarget = change.value.toLowerCase().includes('origin.@');
|
||||
change.value = DhActiveEffect.getChangeValue(model, change, change.effect);
|
||||
super.applyField(model, change, field);
|
||||
}
|
||||
|
||||
/** */
|
||||
static getChangeValue(model, change, effect) {
|
||||
let value = change.value;
|
||||
const isOriginTarget = value.toLowerCase().includes('origin.@');
|
||||
let parseModel = model;
|
||||
if (isOriginTarget && change.effect.origin) {
|
||||
change.value = change.value.replaceAll(/origin\.@/gi, '@');
|
||||
if (isOriginTarget && effect.origin) {
|
||||
value = change.value.replaceAll(/origin\.@/gi, '@');
|
||||
try {
|
||||
const effect = foundry.utils.fromUuidSync(change.effect.origin);
|
||||
const originEffect = foundry.utils.fromUuidSync(effect.origin);
|
||||
const doc =
|
||||
effect.parent?.parent instanceof game.system.api.documents.DhpActor
|
||||
? effect.parent
|
||||
: effect.parent.parent;
|
||||
originEffect.parent?.parent instanceof game.system.api.documents.DhpActor
|
||||
? originEffect.parent
|
||||
: originEffect.parent.parent;
|
||||
if (doc) parseModel = doc;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, parseModel, change.effect.parent));
|
||||
change.value = evalValue ?? change.value;
|
||||
super.applyField(model, change, field);
|
||||
const evalValue = this.effectSafeEval(itemAbleRollParse(value, parseModel, effect.parent));
|
||||
return evalValue ?? value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -646,6 +646,19 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
const results = await game.system.registeredTriggers.runTrigger(
|
||||
CONFIG.DH.TRIGGER.triggers.postDamageReduction.id,
|
||||
this,
|
||||
updates,
|
||||
this
|
||||
);
|
||||
|
||||
if (results?.length) {
|
||||
const resourceMap = new ResourceUpdateMap(results[0].originActor);
|
||||
for (var result of results) resourceMap.addResources(result.updates);
|
||||
resourceMap.updateResources();
|
||||
}
|
||||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
|
|
|
|||
|
|
@ -166,7 +166,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
event.stopPropagation();
|
||||
const config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
await this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
|
||||
if (this.system.action) {
|
||||
await this.system.action.addEffects(config);
|
||||
await this.system.action.workflow.get('damage')?.execute(config, this._id, true);
|
||||
}
|
||||
|
||||
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default class DHItem extends foundry.documents.Item {
|
|||
static async createDocuments(sources, operation) {
|
||||
// Ensure that items being created are valid to the actor its being added to
|
||||
const actor = operation.parent;
|
||||
sources = actor?.system?.isItemValid ? sources.filter((s) => actor.system.isItemValid(s)) : sources;
|
||||
sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources;
|
||||
return super.createDocuments(sources, operation);
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +146,16 @@ export default class DHItem extends foundry.documents.Item {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
async use(event) {
|
||||
/* DomainCard check. Can be expanded or made neater */
|
||||
if (this.system.isDomainTouchedSuppressed) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.format('DAGGERHEART.UI.Notifications.domainTouchRequirement', {
|
||||
nr: this.domainTouched,
|
||||
domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[this.domain].label)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const actions = new Set(this.system.actionsList);
|
||||
if (actions?.size) {
|
||||
let action = actions.first();
|
||||
|
|
|
|||
|
|
@ -37,4 +37,30 @@ export default class DhScene extends Scene {
|
|||
this.#sizeSyncBatch.clear();
|
||||
this.updateEmbeddedDocuments('Token', entries, { animation: { movementSpeed: 1.5 } });
|
||||
}, 0);
|
||||
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
|
||||
if (this instanceof game.system.api.documents.DhScene) {
|
||||
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
|
||||
|
||||
// Register this scene to all environements
|
||||
for (const environment of system.sceneEnvironments) {
|
||||
environment.system.scenes?.add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onDelete(options, userId) {
|
||||
super._onDelete(options, userId);
|
||||
|
||||
if (this instanceof game.system.api.documents.DhScene) {
|
||||
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
|
||||
|
||||
// Clear this scene from all environments that aren't deleted
|
||||
for (const environment of system.sceneEnvironments) {
|
||||
environment?.system?.scenes?.delete(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
104
module/documents/tokenManager.mjs
Normal file
104
module/documents/tokenManager.mjs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* A singleton class that handles preview tokens.
|
||||
*/
|
||||
|
||||
export default class DhTokenManager {
|
||||
#activePreview;
|
||||
#actor;
|
||||
#resolve;
|
||||
|
||||
/**
|
||||
* Create a template preview, deactivating any existing ones.
|
||||
* @param {object} data
|
||||
*/
|
||||
async createPreview(actor, tokenData) {
|
||||
this.#actor = actor;
|
||||
const token = await canvas.tokens._createPreview(
|
||||
{
|
||||
...actor.prototypeToken,
|
||||
displayName: 50,
|
||||
...tokenData
|
||||
},
|
||||
{ renderSheet: false, actor }
|
||||
);
|
||||
|
||||
this.#activePreview = {
|
||||
document: token.document,
|
||||
object: token,
|
||||
origin: { x: token.document.x, y: token.document.y }
|
||||
};
|
||||
|
||||
this.#activePreview.events = {
|
||||
contextmenu: this.#cancelTemplate.bind(this),
|
||||
mousedown: this.#confirmTemplate.bind(this),
|
||||
mousemove: this.#onDragMouseMove.bind(this)
|
||||
};
|
||||
|
||||
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
|
||||
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
|
||||
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
|
||||
}
|
||||
|
||||
/* Currently intended for using as a preview of where to create a token. (note the flag) */
|
||||
async createPreviewAsync(actor, tokenData = {}) {
|
||||
return new Promise(resolve => {
|
||||
this.#resolve = resolve;
|
||||
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the movement of the token preview on mousedrag.
|
||||
* @param {mousemove Event} event
|
||||
*/
|
||||
#onDragMouseMove(event) {
|
||||
event.stopPropagation();
|
||||
const { moveTime, object } = this.#activePreview;
|
||||
const update = {};
|
||||
|
||||
const now = Date.now();
|
||||
if (now - (moveTime || 0) <= 16) return;
|
||||
this.#activePreview.moveTime = now;
|
||||
|
||||
let cursor = event.getLocalPosition(canvas.templates);
|
||||
|
||||
Object.assign(update, canvas.grid.getTopLeftPoint(cursor));
|
||||
|
||||
object.document.updateSource(update);
|
||||
object.renderFlags.set({ refresh: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the preview token on right-click.
|
||||
* @param {contextmenu Event} event
|
||||
*/
|
||||
#cancelTemplate(_event, resolved) {
|
||||
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
|
||||
this.#activePreview.object.destroy();
|
||||
|
||||
canvas.stage.off('mousemove', mousemove);
|
||||
canvas.stage.off('mousedown', mousedown);
|
||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
||||
if (this.#resolve && !resolved) this.#resolve(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a real Actor and token at the preview location and cancels the preview.
|
||||
* @param {click Event} event
|
||||
*/
|
||||
async #confirmTemplate(event) {
|
||||
event.stopPropagation();
|
||||
this.#cancelTemplate(event, true);
|
||||
|
||||
const actor = this.#actor.inCompendium
|
||||
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
|
||||
: this.#actor;
|
||||
const tokenData = await actor.getTokenDocument();
|
||||
const result = await canvas.scene.createEmbeddedDocuments('Token', [
|
||||
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
|
||||
]);
|
||||
|
||||
this.#activePreview = undefined;
|
||||
if (this.#resolve && result.length) this.#resolve(result[0]);
|
||||
}
|
||||
}
|
||||
|
|
@ -220,12 +220,15 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
|
|||
for (const [index, itemValue] of pathValue.entries()) {
|
||||
const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base;
|
||||
const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item;
|
||||
const enrichedValue = await TextEditor.enrichHTML(value.system?.description ?? value.description);
|
||||
const enrichedValue =
|
||||
(await value.system?.getEnrichedDescription?.()) ??
|
||||
(await TextEditor.enrichHTML(value.system?.description ?? value.description));
|
||||
if (itemIsAction) value.enrichedDescription = enrichedValue;
|
||||
else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue);
|
||||
}
|
||||
} else {
|
||||
const enrichedValue = await TextEditor.enrichHTML(pathValue);
|
||||
const enrichedValue =
|
||||
(await item.system?.getEnrichedDescription?.()) ?? (await TextEditor.enrichHTML(pathValue));
|
||||
foundry.utils.setProperty(
|
||||
item,
|
||||
`${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const preloadHandlebarsTemplates = async function () {
|
|||
'systems/daggerheart/templates/actionTypes/effect.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/beastform.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/countdown.hbs',
|
||||
'systems/daggerheart/templates/actionTypes/summon.hbs',
|
||||
'systems/daggerheart/templates/settings/components/settings-item-line.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs',
|
||||
'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs',
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ export const GMUpdateEvent = {
|
|||
export const RefreshType = {
|
||||
Countdown: 'DhCoundownRefresh',
|
||||
TagTeamRoll: 'DhTagTeamRollRefresh',
|
||||
EffectsDisplay: 'DhEffectsDisplayRefresh'
|
||||
EffectsDisplay: 'DhEffectsDisplayRefresh',
|
||||
Scene: 'DhSceneRefresh'
|
||||
};
|
||||
|
||||
export const registerSocketHooks = () => {
|
||||
|
|
@ -92,6 +93,10 @@ export const registerSocketHooks = () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
Hooks.on(socketEvent.RefreshDocument, async data => {
|
||||
const document = await foundry.utils.fromUuid(data.uuid);
|
||||
document.sheet.render();
|
||||
});
|
||||
};
|
||||
|
||||
export const registerUserQueries = () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue