diff --git a/module/applications/config/Action.mjs b/module/applications/config/Action.mjs index 75127b60..082a9a3f 100644 --- a/module/applications/config/Action.mjs +++ b/module/applications/config/Action.mjs @@ -62,7 +62,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id)); if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.damage; context.getRealIndex = this.getRealIndex.bind(this); - console.log(context) return context; } diff --git a/module/applications/costSelectionDialog.mjs b/module/applications/costSelectionDialog.mjs new file mode 100644 index 00000000..74855f2c --- /dev/null +++ b/module/applications/costSelectionDialog.mjs @@ -0,0 +1,64 @@ +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +export default class CostSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(cost, resolve) { + super({}); + this.cost = cost; + this.resolve = resolve; + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'views', 'damage-selection'], + position: { + width: 400, + height: 'auto' + }, + actions: { + sendHope: this.sendHope + }, + form: { + handler: this.updateForm, + submitOnChange: true, + closeOnSubmit: false + } + }; + + /** @override */ + static PARTS = { + damageSelection: { + id: 'costSelection', + template: 'systems/daggerheart/templates/views/costSelection.hbs' + } + }; + + /* -------------------------------------------- */ + + /** @inheritDoc */ + get title() { + return `Cost Options`; + } + + async _prepareContext(_options) { + return { + cost: this.cost.map(c => { + c.scale = c.scale ?? 1; + c.step = c.step ?? 1; + c.total = c.value * c.scale * c.step; + c.enabled = c.hasOwnProperty('enabled') ? c.enabled : true; + return c + }) + }; + } + + static async updateForm(event, _, formData) { + this.cost = foundry.utils.mergeObject(this.cost, foundry.utils.expandObject(formData.object)); + this.render(true) + } + + static sendHope(event) { + event.preventDefault(); + this.resolve(this.cost.filter(c => c.enabled)); + this.close(); + } +} \ No newline at end of file diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 663a29f4..8ac9da49 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -50,9 +50,9 @@ export const targetTypes = { id: 'friendly', label: 'Friendly' }, - adversary: { - id: 'adversary', - label: 'Adversary' + hostile: { + id: 'hostile', + label: 'Hostile' }, any: { id: 'any', diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index 274f4041..a5f31248 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -1,3 +1,5 @@ +import DamageSelectionDialog from '../../applications/damageSelectionDialog.mjs'; +import CostSelectionDialog from '../../applications/costSelectionDialog.mjs'; import { abilities } from '../../config/actorConfig.mjs'; import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs'; @@ -15,6 +17,8 @@ const fields = foundry.data.fields; - Area of effect and measurement placement - Auto use costs and action + - Auto disable selected Cost from other cost list + Activity Types List - Attack => Weapon Attack, Spell Attack, etc... - Effects => Like Attack without damage @@ -27,6 +31,8 @@ const fields = foundry.data.fields; */ export class DHBaseAction extends foundry.abstract.DataModel { + static extraSchemas = []; + static defineSchema() { return { _id: new fields.DocumentIdField(), @@ -34,6 +40,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { name: new fields.StringField({ initial: undefined }), description: new fields.HTMLField(), img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }), + chatDisplay: new fields.BooleanField({ initial: true, label: 'Display in chat' }), actionType: new fields.StringField({ choices: SYSTEM.ITEM.actionTypes, initial: 'action', nullable: true }), cost: new fields.ArrayField( new fields.SchemaField({ @@ -62,10 +69,53 @@ export class DHBaseAction extends foundry.abstract.DataModel { required: false, blank: true, initial: null - }) + }), + ...this.defineExtraSchema() }; } + static defineExtraSchema() { + const extraFields = { + damage: new DHDamageField(), + roll: new fields.SchemaField({ + type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }), + trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), + difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) + }), + save: new fields.SchemaField({ + trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), + difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) + }), + target: new fields.SchemaField({ + type: new fields.StringField({ + choices: SYSTEM.ACTIONS.targetTypes, + initial: SYSTEM.ACTIONS.targetTypes.any.id, + nullable: true, initial: null + }), + amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) + }), + effects: new fields.ArrayField( // ActiveEffect + new fields.SchemaField({ + _id: new fields.DocumentIdField() + }) + ), + healing: new fields.SchemaField({ + type: new fields.StringField({ + choices: SYSTEM.GENERAL.healingTypes, + required: true, + blank: false, + initial: SYSTEM.GENERAL.healingTypes.health.id, + label: 'Healing' + }), + value: new fields.EmbeddedDataField(DHActionDiceData) + }) + }, + extraSchemas = {}; + + this.extraSchemas.forEach(s => extraSchemas[s] = extraFields[s]); + return extraSchemas; + } + prepareData() {} get index() { @@ -81,7 +131,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { } get chatTemplate() { - return 'systems/daggerheart/templates/chat/attack-roll.hbs'; + return 'systems/daggerheart/templates/chat/duality-roll.hbs'; } static getRollType(parent) { @@ -117,98 +167,132 @@ export class DHBaseAction extends foundry.abstract.DataModel { } async use(event, ...args) { - // throw new Error("Activity must implement a 'use' method."); - const data = { - itemUUID: this.item, - activityId: this.id + let config = { + event, + title: this.item.name, + source: { + itemId: this.item._id, + actionId: this._id + }, + hasDamage: !!this.damage?.parts?.length, + chatMessage: { + template: this.chatTemplate + } }; + + this.proceedChatDisplay(config); - if(this.cost?.length) { - const hasCost = await this.checkCost(); - if(!hasCost) return ui.notifications.warn("You don't have the resources to use that action."); - } - if(this.target?.type) { - const hasTarget = await this.checkTarget(); - } - if(this.range) { - const hasRange = await this.checkRange(); - } - if (this.roll?.type && this.roll?.trait) { - const modifierValue = this.actor.system.traits[this.roll.trait].value; - const config = { - event: event, - title: this.item.name, - roll: { - modifier: modifierValue, - label: game.i18n.localize(abilities[this.roll.trait].label), - type: this.actionType, - difficulty: this.roll?.difficulty - }, - chatMessage: { - template: this.chatTemplate - } - }; - if (this.target?.type) config.checkTarget = true; - if (this.damage.parts.length) { - config.damage = { - value: this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '), - type: this.damage.parts[0].type - }; - } - if (this.effects.length) { - // Apply Active Effects. In Chat Message ? - } - data.roll = await this.actor.diceRoll(config); - } - if(this.withMessage || true) { + // Display Costs Dialog & Check if Actor get enough resources + config.costs = await this.getCost(config); + if(!config.costs.hasCost) return ui.notifications.warn("You don't have the resources to use that action."); - } + // Filter selected targets based on Target parameters + config.targets = await this.getTarget(config); + if(!config.targets) return ui.notifications.warn("Too many targets selected for that actions."); + + // Filter selected targets based on Range parameters + config.range = await this.checkRange(config); + if(!config.range.hasRange) return ui.notifications.warn("No Target within range."); - return data; + // Proceed with Roll + await this.proceedRoll(config); + + if (this.effects.length) { + // Apply Active Effects. In Chat Message ? + } + + // Update Actor resources based on Action Cost configuration + this.spendCost(config.costs.values); + + return config; } - async checkCost() { - if(!this.cost.length || !this.actor) return true; - console.log(this.actor, this.cost) - return this.cost.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.value * (c.scalable ? c.step : 1)), true); + /* ROLL */ + async proceedRoll(config) { + if (!this.roll?.type || !this.roll?.trait) return; + const modifierValue = this.actor.system.traits[this.roll.trait].value; + config = { + ...config, + roll: { + modifier: modifierValue, + label: game.i18n.localize(abilities[this.roll.trait].label), + type: this.actionType, + difficulty: this.roll?.difficulty + } + } + config.roll.evaluated = await this.actor.diceRoll(config); + } + /* ROLL */ + + /* COST */ + async getCost(config) { + if(!this.cost?.length || !this.actor) return {values: [], hasCost: true}; + let cost = foundry.utils.deepClone(this.cost); + if (!config.event.shiftKey) { + const dialogClosed = new Promise((resolve, _) => { + new CostSelectionDialog(cost, resolve).render(true); + }); + cost = await dialogClosed; + } + return {values: cost, hasCost: cost.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true)}; } - async checkTarget() { - /* targets = Array.from(game.user.targets).map(x => { - const target = { - id: x.id, - name: x.actor.name, - img: x.actor.img, - difficulty: x.actor.system.difficulty, - evasion: x.actor.system.evasion?.value - }; + async spendCost(config) { + if(!config.costs?.values?.length) return; + return await this.actor.modifyResource(config.costs.values); + } + /* COST */ - target.hit = target.difficulty ? roll.total >= target.difficulty : roll.total >= target.evasion; + /* TARGET */ + async getTarget(config) { + if(this.target.type === SYSTEM.ACTIONS.targetTypes.self.id) return this.formatTarget(this.actor.token ?? this.actor.prototypeToken); + let targets = Array.from(game.user.targets); + // foundry.CONST.TOKEN_DISPOSITIONS.FRIENDLY + if(this.target?.type && this.target.type !== SYSTEM.ACTIONS.targetTypes.any.id) { + targets = targets.filter(t => this.isTargetFriendly(t)); + if(this.target.amount && targets.length > this.target.amount) return false; + } + return targets.map(t => this.formatTarget(t)); + } - return target; - }); */ - const targets = targets = Array.from(game.user.targets).map(x => { - return { - id, - name: this.actor.name, - img: x.actor.img, - difficulty: x.actor.system.difficulty, - evasion: x.actor.system.evasion?.value - } - }); - console.log(this.target) - if(this.target.type === SYSTEM.ACTIONS.targetTypes.self.id) return this.actor; - if(this.target.amount) { + isTargetFriendly(target) { + const actorDisposition = this.actor.token ? this.actor.token.disposition : this.actor.prototypeToken.disposition, + targetDisposition = target.document.disposition; + return (this.target.type === SYSTEM.ACTIONS.targetTypes.friendly.id && actorDisposition === targetDisposition) || (this.target.type === SYSTEM.ACTIONS.targetTypes.hostile.id && (actorDisposition + targetDisposition === 0)) + } + formatTarget(actor) { + return { + id: actor.id, + name: actor.actor.name, + img: actor.actor.img, + difficulty: actor.actor.system.difficulty, + evasion: actor.actor.system.evasion?.value } } + /* TARGET */ - async checkRange() { - console.log(this.range) + /* RANGE */ + async checkRange(config) { + if(!this.range || !this.actor) return true; + return {values: [], hasRange: true}; } + /* RANGE */ + + /* EFFECTS */ + async applyEffects(config) { + if(!this.effects?.length) return; + } + /* EFFECTS */ + + /* CHAT */ + async proceedChatDisplay(config) { + if(!this.chatDisplay) return; + } + /* CHAT */ } -const extraDefineSchema = (field, option) => { +/* const extraDefineSchema = (field, option) => { return { [field]: { // damage: new fields.SchemaField({ @@ -239,56 +323,120 @@ const extraDefineSchema = (field, option) => { ) }[field] }; -}; +}; */ export class DHDamageAction extends DHBaseAction { directDamage = true; - static defineSchema() { + static extraSchemas = ['damage', 'target', 'effects']; + + /* static defineSchema() { return { ...super.defineSchema(), ...extraDefineSchema('damage'), ...extraDefineSchema('target'), ...extraDefineSchema('effects') }; - } + } */ async use(event, ...args) { const messageData = await super.use(event, args); if(!this.directDamage) return; - const roll = await this.rollDamage(); + return await this.rollDamage(event, messageData); + } + + async rollDamage(event, messageData) { + let formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '); + + if (!formula || formula == '') return; + let roll = { formula: formula, total: formula }, + bonusDamage = []; + + if (!event.shiftKey) { + const dialogClosed = new Promise((resolve, _) => { + new DamageSelectionDialog(formula, bonusDamage, resolve).render(true); + }); + const result = await dialogClosed; + bonusDamage = result.bonusDamage; + formula = result.rollString; + + /* const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope); + if (automateHope && result.hopeUsed) { + await this.update({ + 'system.resources.hope.value': this.system.resources.hope.value - result.hopeUsed + }); + } */ + } + + if (isNaN(formula)) { + roll = await new Roll(formula, this.getRollData()).evaluate(); + } if(!roll) return; - const cls = getDocumentClass('ChatMessage'), + const dice = [], + modifiers = []; + for (var i = 0; i < roll.terms.length; i++) { + const term = roll.terms[i]; + if (term.faces) { + dice.push({ + type: `d${term.faces}`, + rolls: term.results.map(x => x.result), + total: term.results.reduce((acc, x) => acc + x.result, 0) + }); + } else if (term.operator) { + } else if (term.number) { + const operator = i === 0 ? '' : roll.terms[i - 1].operator; + modifiers.push({ value: term.number, operator: operator }); + } + } + + // if(messageData?.system?.damage) { + // } else { + const cls = getDocumentClass('ChatMessage'), + systemData = { + title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }), + roll: formula, + damage: { + total: roll.total, + type: this.damage.parts[0].type // Handle multiple type damage + }, + dice: dice, + modifiers: modifiers, + targets: [] + }, + msg = new cls({ + type: 'damageRoll', + user: game.user.id, + sound: CONFIG.sounds.dice, + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/chat/damage-roll.hbs', + systemData + ), + rolls: [roll] + }); + + cls.create(msg.toObject()); + // } + + + /* const cls = getDocumentClass('ChatMessage'), msg = new cls({ user: game.user.id, content: await foundry.applications.handlebars.renderTemplate( this.chatTemplate, { - ...roll, + ...{ + roll: roll.formula, + total: roll.total, + dice: roll.dice, + type: this.damage.parts.map(p => p.type) + }, ...messageData } ) }); - cls.create(msg.toObject()); - } - - async rollDamage() { - const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '); - console.log(this, formula) - if (!formula || formula == '') return; - - let roll = { formula: formula, total: formula }; - if (isNaN(formula)) { - roll = await new Roll(formula, this.getRollData()).evaluate(); - } - console.log(roll) - return { - roll: roll.formula, - total: roll.total, - dice: roll.dice, - type: this.damage.parts.map(p => p.type) - } + cls.create(msg.toObject()); */ } get chatTemplate() { @@ -338,21 +486,24 @@ export class DHDamageAction extends DHBaseAction { export class DHAttackAction extends DHDamageAction { directDamage = false; + // static extraSchemas = []; - static defineSchema() { + static extraSchemas = [...super.extraSchemas, ...['roll', 'save']]; + + /* static defineSchema() { return { ...super.defineSchema(), ...extraDefineSchema('roll'), ...extraDefineSchema('save') }; - } + } */ static getRollType(parent) { return parent.type === 'weapon' ? 'weapon' : 'spellcast'; } get chatTemplate() { - return 'systems/daggerheart/templates/chat/attack-roll.hbs'; + return 'systems/daggerheart/templates/chat/duality-roll.hbs'; } prepareData() { @@ -400,23 +551,7 @@ export class DHAttackAction extends DHDamageAction { } */ export class DHHealingAction extends DHBaseAction { - static defineSchema() { - return { - ...super.defineSchema(), - healing: new fields.SchemaField({ - type: new fields.StringField({ - choices: SYSTEM.GENERAL.healingTypes, - required: true, - blank: false, - initial: SYSTEM.GENERAL.healingTypes.health.id, - label: 'Healing' - }), - value: new fields.EmbeddedDataField(DHActionDiceData) - }), - ...extraDefineSchema('target'), - ...extraDefineSchema('effects') - }; - } + static extraSchemas = ['target', 'effects', 'healing']; async use(event, ...args) { const messageData = await super.use(event, args), @@ -488,12 +623,7 @@ export class DHSummonAction extends DHBaseAction { } export class DHEffectAction extends DHBaseAction { - static defineSchema() { - return { - ...super.defineSchema(), - ...extraDefineSchema('effects') - }; - } + static extraSchemas = ['effects']; } export class DHMacroAction extends DHBaseAction { diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 0a02677f..eefd5cd3 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -28,13 +28,18 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { hit: new fields.BooleanField({ initial: false }) }) ), - damage: new fields.SchemaField( + hasDamage: new fields.BooleanField({ initial: false }), + /* damage: new fields.SchemaField( { value: new fields.StringField({}), type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }) }, { nullable: true, initial: null } - ) + ), */ + action: new fields.SchemaField({ + itemId: new fields.StringField(), + actionId: new fields.StringField() + }) }; } diff --git a/module/data/chat-message/dualityRoll.mjs b/module/data/chat-message/dualityRoll.mjs index 60283b7d..fe5f6833 100644 --- a/module/data/chat-message/dualityRoll.mjs +++ b/module/data/chat-message/dualityRoll.mjs @@ -39,26 +39,31 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel { hit: new fields.BooleanField({ initial: false }) }) ), - damage: new fields.SchemaField({ - value: new fields.StringField({}), - type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }), - bonusDamage: new fields.ArrayField( - new fields.SchemaField({ - value: new fields.StringField({}), - type: new fields.StringField({ - choices: Object.keys(SYSTEM.GENERAL.damageTypes), - integer: false - }), - initiallySelected: new fields.BooleanField(), - appliesOn: new fields.StringField( - { choices: Object.keys(SYSTEM.EFFECTS.applyLocations) }, - { nullable: true, initial: null } - ), - description: new fields.StringField({}), - hopeIncrease: new fields.StringField({ nullable: true }) - }), - { nullable: true, initial: null } - ) + hasDamage: new fields.BooleanField({ initial: false }), + // damage: new fields.SchemaField({ + // value: new fields.StringField({}), + // type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }), + // bonusDamage: new fields.ArrayField( + // new fields.SchemaField({ + // value: new fields.StringField({}), + // type: new fields.StringField({ + // choices: Object.keys(SYSTEM.GENERAL.damageTypes), + // integer: false + // }), + // initiallySelected: new fields.BooleanField(), + // appliesOn: new fields.StringField( + // { choices: Object.keys(SYSTEM.EFFECTS.applyLocations) }, + // { nullable: true, initial: null } + // ), + // description: new fields.StringField({}), + // hopeIncrease: new fields.StringField({ nullable: true }) + // }), + // { nullable: true, initial: null } + // ) + // }), + action: new fields.SchemaField({ + itemId: new fields.StringField(), + actionId: new fields.StringField() }) }; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index b8982cf5..62c160d5 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -256,7 +256,7 @@ export default class DhpActor extends Actor { * @param {boolean} [config.roll.simple=false] * @param {string} [config.roll.type] * @param {number} [config.roll.difficulty] - * @param {any} [config.damage] + * @param {boolean} [config.hasDamage] * @param {object} [config.chatMessage] * @param {string} config.chatMessage.template * @param {boolean} [config.chatMessage.mute] @@ -269,7 +269,6 @@ export default class DhpActor extends Actor { disadvantageDice = 'd6', advantage = config.event.altKey ? true : config.event.ctrlKey ? false : null, targets, - damage = config.damage, modifiers = this.formatRollModifier(config.roll), rollConfig, formula, @@ -354,18 +353,9 @@ export default class DhpActor extends Actor { } } - if (config.checkTarget) { - targets = Array.from(game.user.targets).map(x => { - const target = { - id: x.id, - name: x.actor.name, - img: x.actor.img, - difficulty: x.actor.system.difficulty, - evasion: x.actor.system.evasion?.value - }; - + if (config.targets?.length) { + targets = config.targets.map(target => { target.hit = target.difficulty ? roll.total >= target.difficulty : roll.total >= target.evasion; - return target; }); } @@ -377,14 +367,16 @@ export default class DhpActor extends Actor { dice, roll, modifiers: modifiers.filter(x => x.label), - advantageState: advantage + advantageState: advantage, + action: config.source, + hasDamage: config.hasDamage }; if (this.type === 'character') { configRoll.hope = { dice: hopeDice, value: hope }; configRoll.fear = { dice: fearDice, value: fear }; configRoll.advantage = { dice: advantageDice, value: roll.dice[2]?.results[0].result ?? null }; } - if (damage) configRoll.damage = damage; + // if (damage) configRoll.damage = damage; if (targets) configRoll.targets = targets; const systemData = this.type === 'character' && !config.roll.simple ? new DHDualityRoll(configRoll) : configRoll, @@ -396,7 +388,7 @@ export default class DhpActor extends Actor { content: config.chatMessage.template, rolls: [roll] }); - + await cls.create(msg.toObject()); } return roll; @@ -494,7 +486,7 @@ export default class DhpActor extends Actor { : damage >= this.system.damageThresholds.minor ? 1 : 0; - await this.modifyResource(hpDamage, type); + await this.modifyResource({value: hpDamage, type}); /* const update = { 'system.resources.hitPoints.value': Math.min( this.system.resources.hitPoints.value + hpDamage, @@ -516,33 +508,39 @@ export default class DhpActor extends Actor { } */ } - async modifyResource(value, type) { - let resource, target, update; - switch (type) { - case 'armorStrack': - resource = 'system.stacks.value'; - target = this.armor; - update = Math.min(this.marks.value + value, this.marks.max); - break; - default: - resource = `system.resources.${type}`; - target = this; - update = Math.min(this.resources[type].value + value, this.resources[type].max); - break; - } - if(!resource || !target || !update) return; - if (game.user.isGM) { - await target.update(update); - } else { - await game.socket.emit(`system.${SYSTEM.id}`, { - action: socketEvent.GMUpdate, - data: { - action: GMUpdateEvent.UpdateDocument, - uuid: target.uuid, - update: update - } - }); - } + async modifyResource(resources) { + if(!resources.length) return; + let updates = { actor: { target: this, resources: {} }, armor: { target: this.armor, resources: {} } }; + resources.forEach(r => { + switch (type) { + case 'armorStrack': + // resource = 'system.stacks.value'; + // target = this.armor; + // update = Math.min(this.marks.value + value, this.marks.max); + updates.armor.resources['system.stacks.value'] = Math.min(this.marks.value + value, this.marks.max); + break; + default: + // resource = `system.resources.${type}`; + // target = this; + // update = Math.min(this.resources[type].value + value, this.resources[type].max); + updates.armor.resources[`system.resources.${type}`] = Math.min(this.resources[type].value + value, this.resources[type].max); + break; + } + }) + Object.values(updates).forEach(async (u) => { + if (game.user.isGM) { + await u.target.update(u.resources); + } else { + await game.socket.emit(`system.${SYSTEM.id}`, { + action: socketEvent.GMUpdate, + data: { + action: GMUpdateEvent.UpdateDocument, + uuid: u.target.uuid, + update: u.resources + } + }); + } + }) } /* async takeHealing(healing, type) { diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 3339b92a..a3a59f6d 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -53,13 +53,19 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo event.stopPropagation(); const actor = game.actors.get(message.system.origin); if (!actor || !game.user.isGM) return true; - - await actor.damageRoll( - message.system.title, - message.system.damage, - message.system.targets.filter(x => x.hit).map(x => ({ id: x.id, name: x.name, img: x.img })), - event.shiftKey - ); + if(message.system.action?.itemId && message.system.action?.actionId) { + const item = actor.items.get(message.system.action?.itemId), + action = item?.system?.actions?.find(a => a._id === message.system.action.actionId); + if(!item || !action || !action?.rollDamage) return; + await action.rollDamage(event, this); + } else { + await actor.damageRoll( + message.system.title, + message.system.damage, + message.system.targets.filter(x => x.hit).map(x => ({ id: x.id, name: x.name, img: x.img })), + event.shiftKey + ); + } }; hoverTarget = event => { @@ -107,7 +113,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); for (var target of targets) { - await target.actor.modifyResource(healing, event.currentTarget.dataset.type); + await target.actor.modifyResource([{value: healing, type: event.currentTarget.dataset.type}]); } }; diff --git a/templates/chat/adversary-attack-roll.hbs b/templates/chat/adversary-attack-roll.hbs index 75d661ae..f6051bfe 100644 --- a/templates/chat/adversary-attack-roll.hbs +++ b/templates/chat/adversary-attack-roll.hbs @@ -41,7 +41,7 @@ {{/if}}
- +
\ No newline at end of file diff --git a/templates/chat/damage-roll.hbs b/templates/chat/damage-roll.hbs index 8bd65834..6d9c9399 100644 --- a/templates/chat/damage-roll.hbs +++ b/templates/chat/damage-roll.hbs @@ -23,7 +23,7 @@ -
{{total}}
+
{{damage.total}}
diff --git a/templates/chat/duality-roll.hbs b/templates/chat/duality-roll.hbs index b1d59835..ee50df7d 100644 --- a/templates/chat/duality-roll.hbs +++ b/templates/chat/duality-roll.hbs @@ -128,9 +128,9 @@ {{/each}}
{{/if}} -
- {{#if damage.value}} - +
+ {{#if hasDamage}} + {{/if}}
{{roll.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}
diff --git a/templates/views/action.hbs b/templates/views/action.hbs index 55a6ba9a..8711cd98 100644 --- a/templates/views/action.hbs +++ b/templates/views/action.hbs @@ -20,6 +20,7 @@ {{formField fields.name value=source.name label="Name" name="name"}} {{formField fields.img value=source.img label="Icon" name="img"}} {{formField fields.actionType value=source.actionType label="Type" name="actionType" localize=true}} + {{formField fields.chatDisplay value=source.chatDisplay name="chatDisplay"}}
diff --git a/templates/views/costSelection.hbs b/templates/views/costSelection.hbs new file mode 100644 index 00000000..9900ed91 --- /dev/null +++ b/templates/views/costSelection.hbs @@ -0,0 +1,16 @@ +
+ {{#each cost as | c index |}} +
+
+ + + {{#if scalable}} + + {{/if}} +
+
+ {{/each}} +
+ +
+
\ No newline at end of file diff --git a/templates/views/damageSelection.hbs b/templates/views/damageSelection.hbs index 5479da46..a86f42d5 100644 --- a/templates/views/damageSelection.hbs +++ b/templates/views/damageSelection.hbs @@ -2,10 +2,10 @@
- +
- {{#each this.bonusDamage as |damage index|}} + {{#each bonusDamage as |damage index|}}