diff --git a/daggerheart.mjs b/daggerheart.mjs index a6676f19..153c2f84 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -18,6 +18,7 @@ import { } from './module/systemRegistration/_module.mjs'; import { placeables } from './module/canvas/_module.mjs'; import { registerRollDiceHooks } from './module/dice/dhRoll.mjs'; +import { registerDHActorHooks } from './module/documents/actor.mjs'; Hooks.once('init', () => { CONFIG.DH = SYSTEM; @@ -154,6 +155,7 @@ Hooks.on('ready', () => { socketRegistration.registerSocketHooks(); registerCountdownApplicationHooks(); registerRollDiceHooks(); + registerDHActorHooks(); }); Hooks.once('dicesoniceready', () => {}); diff --git a/lang/en.json b/lang/en.json index ea4afdb6..83c4fa74 100755 --- a/lang/en.json +++ b/lang/en.json @@ -214,6 +214,10 @@ "encounter": "Encounter" } }, + "DeleteConfirmation": { + "title": "Delete {type} - {name}", + "text": "Are you sure you want to delete {name}?" + }, "DamageReduction": { "armorMarks": "Armor Marks", "armorWithStress": "Spend 1 stress to use an extra mark", @@ -1126,6 +1130,7 @@ "examples": { "label": "Examples" }, "advantageOn": { "label": "Gain Advantage On" }, "tokenImg": { "label": "Token Image" }, + "tokenRingImg": { "label": "Subject Texture" }, "tokenSize": { "placeholder": "Using character dimensions", "height": { "label": "Height" }, diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index 7d89eb31..0612089d 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -1,6 +1,6 @@ import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs'; -const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; +const { DialogV2, ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) { constructor(resolve, reject, actor, damage, damageType) { @@ -223,9 +223,17 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap async close(fromSave) { if (!fromSave) { - this.reject(); + this.resolve(); } await super.close({}); } + + static async armorStackQuery({ actorId, damage, type }) { + return new Promise(async (resolve, reject) => { + const actor = await fromUuid(actorId); + if (!actor || !actor?.isOwner) reject(); + new DamageReductionDialog(resolve, reject, actor, damage, type).render({ force: true }); + }); + } } diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index d95e6129..57deea25 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -70,6 +70,18 @@ export default class DHAdversarySettings extends DHBaseActorSettings { * @type {ApplicationClickAction} */ static async #removeExperience(_, target) { + const experience = this.actor.system.experiences[target.dataset.experience]; + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Experience.single`), + name: experience.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: experience.name }) + }); + if (!confirmed) return; + await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); } diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index d0ca897a..7422f5fc 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -82,10 +82,24 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { static async #deleteAdversary(event, target) { const adversaryKey = target.dataset.adversary; const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; - console.log(target.dataset.potentialAdversar); - const newAdversaries = foundry.utils - .getProperty(this.actor, path) - .filter(x => x && (x?.uuid ?? x) !== adversaryKey); + const property = foundry.utils.getProperty(this.actor, path); + const adversary = property.find(x => (x?.uuid ?? x) === adversaryKey); + + if (adversary) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize('TYPES.Actor.adversary'), + name: adversary.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: adversary.name }) + }); + + if (!confirmed) return; + } + + const newAdversaries = property.filter(x => x && (x?.uuid ?? x) !== adversaryKey); await this.actor.update({ [path]: newAdversaries }); } diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 5dae9741..67f57781 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -92,9 +92,7 @@ export default class AdversarySheet extends DHBaseActorSheet { const cls = getDocumentClass('ChatMessage'); const systemData = { name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), - description: `${experience.name} ${ - experience.modifier < 0 ? experience.modifier : `+${experience.modifier}` - }` + description: `${experience.name} ${experience.total.signedString()}` }; const msg = new cls({ type: 'abilityUse', diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index ca8149a3..e70774f5 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -188,7 +188,17 @@ export default class CharacterSheet extends DHBaseActorSheet { * @param {HTMLElement} el * @returns {foundry.documents.Item?} */ - const getItem = el => this.actor.items.get(el.closest('[data-item-id]')?.dataset.itemId); + const getItem = element => { + const listElement = (element.target ?? element).closest('[data-item-id]'); + const itemId = listElement.dataset.itemId; + + switch (listElement.dataset.type) { + case 'effect': + return this.document.effects.get(itemId); + default: + return this.document.items.get(itemId); + } + }; return [ { @@ -249,7 +259,23 @@ export default class CharacterSheet extends DHBaseActorSheet { { name: 'CONTROLS.CommonDelete', icon: '', - callback: el => getItem(el).delete() + callback: async el => { + const item = getItem(el); + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`TYPES.${item.documentName}.${item.type}`), + name: item.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { + name: item.name + }) + }); + if (!confirmed) return; + + item.delete(); + } } ]; } @@ -495,7 +521,7 @@ export default class CharacterSheet extends DHBaseActorSheet { const config = { event: event, title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`, - headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilitychecktitle', { + headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: abilityLabel }), roll: { diff --git a/module/applications/sheets/api/actor-setting.mjs b/module/applications/sheets/api/actor-setting.mjs index 50e2b0a9..79aa1c37 100644 --- a/module/applications/sheets/api/actor-setting.mjs +++ b/module/applications/sheets/api/actor-setting.mjs @@ -42,9 +42,12 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee /**@inheritdoc */ async _prepareContext(options) { const context = await super._prepareContext(options); - context.systemFields.attack.fields = this.actor.system.attack.schema.fields; context.isNPC = this.actor.isNPC; + if (context.systemFields.attack) { + context.systemFields.attack.fields = this.actor.system.attack.schema.fields; + } + return context; } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index d0ef63d6..0e5f211d 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -218,7 +218,6 @@ export default function DHApplicationMixin(Base) { */ static async #createDoc(event, button) { const { documentClass, type } = button.dataset; - console.log(documentClass, type); const parent = this.document; const cls = getDocumentClass(documentClass); @@ -250,7 +249,23 @@ export default function DHApplicationMixin(Base) { */ static async #deleteDoc(_event, button) { const { type, docId } = button.dataset; - await this.document.getEmbeddedDocument(type, docId, { strict: true }).delete(); + const document = this.document.getEmbeddedDocument(type, docId, { strict: true }); + const typeName = game.i18n.localize( + document.type === 'base' ? `DOCUMENT.${type}` : `TYPES.${type}.${document.type}` + ); + + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: typeName, + name: document.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: document.name }) + }); + if (!confirmed) return; + + await document.delete(); } } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index f9d52025..d624acc6 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -139,6 +139,19 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static async #removeAction(event, button) { event.stopPropagation(); const actionIndex = button.closest('[data-index]').dataset.index; + const action = this.document.system.actions[actionIndex]; + + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), + name: action.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name }) + }); + if (!confirmed) return; + await this.document.update({ 'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex)) }); @@ -180,6 +193,20 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static async #removeFeature(event, button) { event.stopPropagation(); const target = button.closest('.feature-item'); + const feature = this.document.system.features.find(x => x && x.id === target.id); + + if (feature) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`TYPES.Item.feature`), + name: feature.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name }) + }); + if (!confirmed) return; + } await this.document.update({ 'system.features': this.document.system.features diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs index e3b72d01..194f3ab1 100644 --- a/module/applications/sheets/items/beastform.mjs +++ b/module/applications/sheets/items/beastform.mjs @@ -30,8 +30,17 @@ export default class BeastformSheet extends DHBaseItemSheet { }; /**@inheritdoc */ - async _preparePartContext(partId, context) { - await super._preparePartContext(partId, context); + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + + context.document = context.document.toObject(); + context.document.effects = this.document.effects.map(effect => { + const data = effect.toObject(); + data.id = effect.id; + if (effect.type === 'beastform') data.mandatory = true; + + return data; + }); return context; } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index bbd151cd..71532733 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -215,7 +215,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) damage = Math.ceil(damage * (CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1)); - await target.actor.takeDamage(damage, message.system.roll.type); + target.actor.takeDamage(damage, message.system.roll.type); } }; @@ -227,7 +227,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); for (var target of targets) { - await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]); + target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]); } }; diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 3aa25bef..6445f65d 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -10,6 +10,11 @@ export default class BeastformEffect extends foundry.abstract.TypeDataModel { base64: false, nullable: true }), + tokenRingImg: new fields.FilePathField({ + initial: 'icons/svg/mystery-man.svg', + categories: ['IMAGE'], + base64: false + }), tokenSize: new fields.SchemaField({ height: new fields.NumberField({ integer: true, nullable: true }), width: new fields.NumberField({ integer: true, nullable: true }) @@ -28,6 +33,11 @@ export default class BeastformEffect extends foundry.abstract.TypeDataModel { width: this.characterTokenData.tokenSize.width, texture: { src: this.characterTokenData.tokenImg + }, + ring: { + subject: { + texture: this.characterTokenData.tokenRingImg + } } }; diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 3cbf4eaa..9bb5d5f8 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -15,7 +15,7 @@ export default class DhpAdversary extends BaseDataActor { return foundry.utils.mergeObject(super.metadata, { label: 'TYPES.Actor.adversary', type: 'adversary', - settingSheet: DHAdversarySettings, + settingSheet: DHAdversarySettings }); } @@ -74,7 +74,7 @@ export default class DhpAdversary extends BaseDataActor { experiences: new fields.TypedObjectField( new fields.SchemaField({ name: new fields.StringField(), - modifier: new fields.NumberField({ required: true, integer: true, initial: 1 }) + total: new fields.NumberField({ required: true, integer: true, initial: 1 }) }) ), bonuses: new fields.SchemaField({ diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs index 2eb871ec..b7ea5cb9 100644 --- a/module/data/item/beastform.mjs +++ b/module/data/item/beastform.mjs @@ -3,7 +3,7 @@ import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayFie import BaseDataItem from './base.mjs'; export default class DHBeastform extends BaseDataItem { - static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Beastform']; + static LOCALIZATION_PREFIXES = ['DAGGERHEART.ITEMS.Beastform']; /** @inheritDoc */ static get metadata() { @@ -29,12 +29,17 @@ export default class DHBeastform extends BaseDataItem { categories: ['IMAGE'], base64: false }), + tokenRingImg: new fields.FilePathField({ + initial: 'icons/svg/mystery-man.svg', + categories: ['IMAGE'], + base64: false + }), tokenSize: new fields.SchemaField({ height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }), width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }) }), examples: new fields.StringField(), - advantageOn: new fields.ArrayField(new fields.StringField()), + advantageOn: new fields.StringField(), features: new ForeignDocumentUUIDArrayField({ type: 'Item' }) }; } @@ -56,40 +61,54 @@ export default class DHBeastform extends BaseDataItem { 'Item', this.features.map(x => x.toObject()) ); - const effects = await this.parent.parent.createEmbeddedDocuments( + + const extraEffects = await this.parent.parent.createEmbeddedDocuments( 'ActiveEffect', - this.parent.effects.map(x => x.toObject()) + this.parent.effects.filter(x => x.type !== 'beastform').map(x => x.toObject()) ); - await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [ - { - type: 'beastform', - name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'), - img: 'icons/creatures/abilities/paw-print-pair-purple.webp', - system: { - isBeastform: true, - characterTokenData: { - tokenImg: this.parent.parent.prototypeToken.texture.src, - tokenSize: { - height: this.parent.parent.prototypeToken.height, - width: this.parent.parent.prototypeToken.width - } - }, - advantageOn: this.advantageOn, - featureIds: features.map(x => x.id), - effectIds: effects.map(x => x.id) - } + const beastformEffect = this.parent.effects.find(x => x.type === 'beastform'); + await beastformEffect.updateSource({ + system: { + characterTokenData: { + tokenImg: this.parent.parent.prototypeToken.texture.src, + tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture, + tokenSize: { + height: this.parent.parent.prototypeToken.height, + width: this.parent.parent.prototypeToken.width + } + }, + advantageOn: this.advantageOn, + featureIds: features.map(x => x.id), + effectIds: extraEffects.map(x => x.id) } - ]); + }); + + await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]); await updateActorTokens(this.parent.parent, { height: this.tokenSize.height, width: this.tokenSize.width, texture: { src: this.tokenImg + }, + ring: { + subject: { + texture: this.tokenRingImg + } } }); return false; } + + _onCreate() { + this.parent.createEmbeddedDocuments('ActiveEffect', [ + { + type: 'beastform', + name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'), + img: 'icons/creatures/abilities/paw-print-pair-purple.webp' + } + ]); + } } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index a6102f42..3c91f605 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,11 +1,20 @@ import DamageSelectionDialog from '../applications/dialogs/damageSelectionDialog.mjs'; -import { GMUpdateEvent, socketEvent } from '../systemRegistration/socket.mjs'; +import { emitAsGM, emitAsOwner, GMUpdateEvent, socketEvent } from '../systemRegistration/socket.mjs'; import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; import { damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs'; -export default class DhpActor extends foundry.documents.Actor { +export default class DhpActor extends Actor { + /** + * Return the first Actor active owner. + */ + get owner() { + const user = + this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active); + if (!user) return game.user.isGM ? game.user : null; + return user; + } /** * Whether this actor is an NPC. @@ -361,16 +370,16 @@ export default class DhpActor extends foundry.documents.Actor { const modifier = roll.modifier !== null ? Number.parseInt(roll.modifier) : null; return modifier !== null ? [ - { - value: modifier, - label: roll.label - ? modifier >= 0 - ? `${roll.label} +${modifier}` - : `${roll.label} ${modifier}` - : null, - title: roll.label - } - ] + { + value: modifier, + label: roll.label + ? modifier >= 0 + ? `${roll.label} +${modifier}` + : `${roll.label} ${modifier}` + : null, + title: roll.label + } + ] : []; } @@ -457,6 +466,8 @@ export default class DhpActor extends foundry.documents.Actor { } async takeDamage(baseDamage, type) { + if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, baseDamage, type) === false) return null; + if (this.type === 'companion') { await this.modifyResource([{ value: 1, type: 'stress' }]); return; @@ -464,41 +475,33 @@ export default class DhpActor extends foundry.documents.Actor { const flatReduction = this.system.bonuses.damageReduction[type]; const damage = Math.max(baseDamage - (flatReduction ?? 0), 0); + const hpDamage = this.convertDamageToThreshold(damage); - const hpDamage = - damage >= this.system.damageThresholds.severe - ? 3 - : damage >= this.system.damageThresholds.major - ? 2 - : damage >= this.system.damageThresholds.minor - ? 1 - : 0; + if (Hooks.call(`${CONFIG.DH.id}.postDamageTreshold`, this, hpDamage, damage, type) === false) return null; - if (hpDamage && this.type === 'character' && this.#canReduceDamage(hpDamage, type)) { - new Promise((resolve, reject) => { - new DamageReductionDialog(resolve, reject, this, hpDamage, type).render(true); - }) - .then(async ({ modifiedDamage, armorSpent, stressSpent }) => { - const resources = [ - { value: modifiedDamage, type: 'hitPoints' }, - ...(armorSpent ? [{ value: armorSpent, type: 'armorStack' }] : []), - ...(stressSpent ? [{ value: stressSpent, type: 'stress' }] : []) - ]; - await this.modifyResource(resources); - }) - .catch(() => { - const cls = getDocumentClass('ChatMessage'); - const msg = new cls({ - user: game.user.id, - content: game.i18n.format('DAGGERHEART.UI.Notifications.damageIgnore', { - character: this.name - }) - }); - cls.create(msg.toObject()); - }); - } else { - await this.modifyResource([{ value: hpDamage, type: 'hitPoints' }]); + if (!hpDamage) return; + + const updates = [{ value: hpDamage, type: 'hitPoints' }]; + + if (this.type === 'character' && this.system.armor && this.#canReduceDamage(hpDamage, type)) { + const armorStackResult = await this.owner.query('armorStack', { + actorId: this.uuid, + damage: hpDamage, + type: type + }); + if (armorStackResult) { + const { modifiedDamage, armorSpent, stressSpent } = armorStackResult; + updates.find(u => u.type === 'hitPoints').value = modifiedDamage; + updates.push( + ...(armorSpent ? [{ value: armorSpent, type: 'armorStack' }] : []), + ...(stressSpent ? [{ value: stressSpent, type: 'stress' }] : []) + ); + } } + + await this.modifyResource(updates); + + if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damage, type) === false) return null; } async takeHealing(resources) { @@ -508,6 +511,8 @@ export default class DhpActor extends foundry.documents.Actor { async modifyResource(resources) { if (!resources.length) return; + + if (resources.find(r => r.type === 'stress')) this.convertStressDamageToHP(resources); let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } }; resources.forEach(r => { switch (r.type) { @@ -535,7 +540,13 @@ export default class DhpActor extends foundry.documents.Actor { }); Object.values(updates).forEach(async u => { if (Object.keys(u.resources).length > 0) { - if (game.user.isGM) { + await emitAsGM( + GMUpdateEvent.UpdateDocument, + u.target.update.bind(u.target), + u.resources, + u.target.uuid + ); + /* if (game.user.isGM) { await u.target.update(u.resources); } else { await game.socket.emit(`system.${CONFIG.DH.id}`, { @@ -546,8 +557,35 @@ export default class DhpActor extends foundry.documents.Actor { update: u.resources } }); - } + } */ } }); } + + convertDamageToThreshold(damage) { + return damage >= this.system.damageThresholds.severe + ? 3 + : damage >= this.system.damageThresholds.major + ? 2 + : damage >= this.system.damageThresholds.minor + ? 1 + : 0; + } + + convertStressDamageToHP(resources) { + const stressDamage = resources.find(r => r.type === 'stress'), + newValue = this.system.resources.stress.value + stressDamage.value; + if (newValue <= this.system.resources.stress.maxTotal) return; + const hpDamage = resources.find(r => r.type === 'hitPoints'); + if (hpDamage) hpDamage.value++; + else + resources.push({ + type: 'hitPoints', + value: 1 + }); + } } + +export const registerDHActorHooks = () => { + CONFIG.queries.armorStack = DamageReductionDialog.armorStackQuery; +}; diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index d7f79df9..0037d99d 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -45,7 +45,13 @@ export const registerSocketHooks = () => { await game.settings.set( CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, - Math.max(0, Math.min(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, data.update)) + Math.max( + 0, + Math.min( + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + data.update + ) + ) ); /* Hooks.callAll(socketEvent.DhpFearUpdate); await game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.DhpFearUpdate }); */ @@ -63,21 +69,28 @@ export const registerSocketHooks = () => { }); }; -export const emitAsGM = async (eventName, callback, args) => { - if(!game.user.isGM) { - return new Promise(async (resolve, reject) => { - try { - const response = await game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.GMUpdate, - data: { - action: eventName, - update: args - } - }); - resolve(response); - } catch (error) { - reject(error); +export const emitAsGM = async (eventName, callback, update, uuid = null) => { + if (!game.user.isGM) { + return await game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.GMUpdate, + data: { + action: eventName, + uuid, + update } - }) - } else return callback(args); -} + }); + } else return callback(update); +}; + +export const emitAsOwner = (eventName, userId, args) => { + if (userId === game.user.id) return; + if (!eventName || !userId) return false; + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: eventName, + data: { + userId, + ...args + } + }); + return false; +}; diff --git a/templates/sheets-settings/adversary-settings/experiences.hbs b/templates/sheets-settings/adversary-settings/experiences.hbs index fb758a40..c15bf6b9 100644 --- a/templates/sheets-settings/adversary-settings/experiences.hbs +++ b/templates/sheets-settings/adversary-settings/experiences.hbs @@ -13,7 +13,7 @@ {{#each document.system.experiences as |experience key|}}
  • - +
  • {{/each}} diff --git a/templates/sheets/actors/adversary/sidebar.hbs b/templates/sheets/actors/adversary/sidebar.hbs index 8a410348..b26c1b81 100644 --- a/templates/sheets/actors/adversary/sidebar.hbs +++ b/templates/sheets/actors/adversary/sidebar.hbs @@ -94,7 +94,7 @@ {{#each source.system.experiences as |experience id|}}
    - +{{experience.modifier}} + +{{experience.total}}
    {{experience.name}}
    diff --git a/templates/sheets/global/tabs/tab-effects.hbs b/templates/sheets/global/tabs/tab-effects.hbs index 744ef8e1..a75f1b0b 100644 --- a/templates/sheets/global/tabs/tab-effects.hbs +++ b/templates/sheets/global/tabs/tab-effects.hbs @@ -17,7 +17,7 @@ {{effect.name}}
    - +
    {{/each}} diff --git a/templates/sheets/items/beastform/settings.hbs b/templates/sheets/items/beastform/settings.hbs index 78af825f..dec7d134 100644 --- a/templates/sheets/items/beastform/settings.hbs +++ b/templates/sheets/items/beastform/settings.hbs @@ -8,11 +8,7 @@ {{formGroup systemFields.examples value=source.system.examples localize=true}}
    -
    - {{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}} - - {{!-- {{formGroup systemFields.examples value=source.system.examples localize=true}} --}} -
    + {{formGroup systemFields.advantageOn value=source.system.advantageOn localize=true}}
    {{localize "DAGGERHEART.ITEMS.Beastform.tokenTitle"}} @@ -21,6 +17,10 @@ {{formGroup systemFields.tokenImg value=source.system.tokenImg localize=true}} +
    + {{formGroup systemFields.tokenRingImg value=source.system.tokenRingImg localize=true}} +
    + {{formGroup systemFields.tokenSize.fields.height value=source.system.tokenSize.height localize=true placeholder=(localize "DAGGERHEART.ITEMS.Beastform.FIELDS.tokenSize.placeholder") }} {{formGroup systemFields.tokenSize.fields.width value=source.system.tokenSize.width localize=true placeholder=(localize "DAGGERHEART.ITEMS.Beastform.FIELDS.tokenSize.placeholder")}}
    diff --git a/templates/ui/chat/adversary-roll.hbs b/templates/ui/chat/adversary-roll.hbs index 1f97fefe..a8918062 100644 --- a/templates/ui/chat/adversary-roll.hbs +++ b/templates/ui/chat/adversary-roll.hbs @@ -49,7 +49,7 @@
    {{#if damage.roll}}
    - +
    {{else}}
    diff --git a/templates/ui/chat/duality-roll.hbs b/templates/ui/chat/duality-roll.hbs index 4242c515..66d32e95 100644 --- a/templates/ui/chat/duality-roll.hbs +++ b/templates/ui/chat/duality-roll.hbs @@ -134,7 +134,7 @@
    {{#if hasDamage}} {{#if damage.roll}} - + {{else}} {{/if}}