diff --git a/daggerheart.mjs b/daggerheart.mjs index 6b634ffc..c954f264 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -316,9 +316,12 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/views/actionTypes/uuid.hbs', 'systems/daggerheart/templates/views/actionTypes/uses.hbs', 'systems/daggerheart/templates/views/actionTypes/roll.hbs', + 'systems/daggerheart/templates/views/actionTypes/save.hbs', 'systems/daggerheart/templates/views/actionTypes/cost.hbs', 'systems/daggerheart/templates/views/actionTypes/range-target.hbs', 'systems/daggerheart/templates/views/actionTypes/effect.hbs', - 'systems/daggerheart/templates/settings/components/settings-item-line.hbs' + 'systems/daggerheart/templates/settings/components/settings-item-line.hbs', + + 'systems/daggerheart/templates/chat/parts/target-chat.hbs' ]); }; diff --git a/module/applications/chatMessage.mjs b/module/applications/chatMessage.mjs index 084fc768..1328de57 100644 --- a/module/applications/chatMessage.mjs +++ b/module/applications/chatMessage.mjs @@ -1,18 +1,13 @@ -import { DualityRollColor } from '../data/settings/Appearance.mjs'; -import DHDualityRoll from '../data/chat-message/dualityRoll.mjs'; - export default class DhpChatMessage extends foundry.documents.ChatMessage { async renderHTML() { - if (this.type === 'dualityRoll' || this.type === 'adversaryRoll') { - this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system); - } + if(this.system.messageTemplate) this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, this.system); /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ const html = await super.renderHTML(); + this.applyPermission(html); if (this.type === 'dualityRoll') { html.classList.add('duality'); - /* const dualityResult = this.system.dualityResult; */ switch (this.system.roll.result.duality) { case 1: html.classList.add('hope'); @@ -24,11 +19,18 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { html.classList.add('critical'); break; } - /* if (dualityResult === DHDualityRoll.dualityResult.hope) html.classList.add('hope'); - else if (dualityResult === DHDualityRoll.dualityResult.fear) html.classList.add('fear'); - else html.classList.add('critical'); */ } return html; } + + applyPermission(html) { + const elements = html.querySelectorAll('[data-perm-id]'); + elements.forEach(e => { + const uuid = e.dataset.permId, + document = fromUuidSync(uuid); + e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER')); + e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER')); + }); + } } diff --git a/module/applications/config/Action.mjs b/module/applications/config/Action.mjs index a27dc67d..6b5f7b41 100644 --- a/module/applications/config/Action.mjs +++ b/module/applications/config/Action.mjs @@ -65,9 +65,10 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { if (this.action.damage?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.damage; context.getRealIndex = this.getRealIndex.bind(this); + context.getEffectDetails = this.getEffectDetails.bind(this); context.disableOption = this.disableOption.bind(this); context.isNPC = this.action.actor && this.action.actor.type !== 'character'; - context.hasRoll = this.action.hasRoll(); + context.hasRoll = this.action.hasRoll; console.log(context) return context; } @@ -90,24 +91,16 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { return data.damage.parts.find(d => d.base) ? index - 1 : index; } + getEffectDetails(id) { + return this.action.item.effects.get(id); + } + _prepareSubmitData(event, formData) { const submitData = foundry.utils.expandObject(formData.object); for ( const keyPath of this.constructor.CLEAN_ARRAYS ) { const data = foundry.utils.getProperty(submitData, keyPath); if ( data ) foundry.utils.setProperty(submitData, keyPath, Object.values(data)); - /* const data = foundry.utils.getProperty(submitData, keyPath), - originalData = foundry.utils.getProperty(this.action.toObject(), keyPath); - if ( data ) { - const aData = Object.values(data); - originalData.forEach((v,i) => { - aData[i] = {...originalData[i], ...aData[i]}; - }) - foundry.utils.setProperty(submitData, keyPath, aData); - } */ } - // this.element.querySelectorAll("fieldset[disabled] :is(input, select)").forEach(input => { - // foundry.utils.setProperty(submitData, input.name, input.value); - // }); return submitData; } @@ -138,6 +131,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { } static removeElement(event) { + event.stopPropagation(); const data = this.action.toObject(), key = event.target.closest('.action-category-data').dataset.key, index = event.target.dataset.index; @@ -192,5 +186,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]); } - static editEffect(event) {} + static editEffect(event) { + const id = event.target.closest('[data-effect-id]')?.dataset?.effectId; + this.action.item.effects.get(id).sheet.render(true); + } } diff --git a/module/applications/roll.mjs b/module/applications/roll.mjs index 781ffb81..b5f53abc 100644 --- a/module/applications/roll.mjs +++ b/module/applications/roll.mjs @@ -16,7 +16,7 @@ export class DHRoll extends Roll { if (!roll) return; await this.buildEvaluate(roll, config, (message = {})); await this.buildPost(roll, config, (message = {})); - return roll; + return config; } static async buildConfigure(config = {}, message = {}) { @@ -58,7 +58,7 @@ export class DHRoll extends Roll { if (message.data) { } else { const messageData = {}; - await this.toMessage(roll, config); + config.message = await this.toMessage(roll, config); } } @@ -71,10 +71,9 @@ export class DHRoll extends Roll { user: game.user.id, sound: config.mute ? null : CONFIG.sounds.dice, system: config, - content: await this.messageTemplate(config), rolls: [roll] }; - await cls.create(msg); + return await cls.create(msg); } static applyKeybindings(config) { @@ -110,12 +109,6 @@ export class D20Roll extends DHRoll { static messageType = 'adversaryRoll'; - // static messageTemplate = 'systems/daggerheart/templates/chat/adversary-roll.hbs'; - - static messageTemplate = async config => { - return 'systems/daggerheart/templates/chat/adversary-roll.hbs'; - }; - static CRITICAL_TRESHOLD = 20; static DefaultDialog = D20RollDialog; @@ -214,9 +207,9 @@ export class D20Roll extends DHRoll { if (config.targets?.length) { config.targets.forEach(target => { const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; - target.hit = roll.total >= difficulty; + target.hit = this.isCritical || roll.total >= difficulty; }); - } else if (config.roll.difficulty) roll.success = roll.total >= config.roll.difficulty; + } else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty; config.roll.total = roll.total; config.roll.formula = roll.formula; config.roll.advantage = { @@ -260,12 +253,6 @@ export class DualityRoll extends D20Roll { static messageType = 'dualityRoll'; - // static messageTemplate = 'systems/daggerheart/templates/chat/duality-roll.hbs'; - - static messageTemplate = async config => { - return 'systems/daggerheart/templates/chat/duality-roll.hbs'; - }; - static DefaultDialog = D20RollDialog; get dHope() { @@ -395,21 +382,22 @@ export class DamageRoll extends DHRoll { static messageType = 'damageRoll'; - // static messageTemplate = 'systems/daggerheart/templates/chat/damage-roll.hbs'; - static messageTemplate = async config => { - return await foundry.applications.handlebars.renderTemplate( - config.messageTemplate ?? 'systems/daggerheart/templates/chat/damage-roll.hbs', - config - ); - }; - static DefaultDialog = DamageDialog; static async postEvaluate(roll, config = {}) { config.roll = { - result: roll.total, - dice: roll.dice, + total: roll.total, + formula: roll.formula, type: config.type }; + config.roll.dice = []; + roll.dice.forEach(d => { + config.roll.dice.push({ + dice: d.denomination, + total: d.total, + formula: d.formula, + results: d.results + }); + }); } } diff --git a/module/applications/sheets/adversary.mjs b/module/applications/sheets/adversary.mjs index 7243c5e0..26791298 100644 --- a/module/applications/sheets/adversary.mjs +++ b/module/applications/sheets/adversary.mjs @@ -54,7 +54,9 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { context.document = this.document; context.tabs = super._getTabs(this.constructor.TABS); context.systemFields.attack.fields = this.document.system.attack.schema.fields; + context.getEffectDetails = this.getEffectDetails.bind(this); context.isNPC = true; + console.log(context) return context; } @@ -80,6 +82,10 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { this.actor.diceRoll(config); } + getEffectDetails(id) { + return {}; + } + static async attackRoll(event) { this.actor.system.attack.use(event); } diff --git a/module/applications/sheets/character.mjs b/module/applications/sheets/character.mjs index 989ea8c4..6e192d10 100644 --- a/module/applications/sheets/character.mjs +++ b/module/applications/sheets/character.mjs @@ -5,7 +5,6 @@ import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs'; import DaggerheartSheet from './daggerheart-sheet.mjs'; import { abilities } from '../../config/actorConfig.mjs'; import DhlevelUp from '../levelup.mjs'; -import DHDualityRoll from '../../data/chat-message/dualityRoll.mjs'; const { ActorSheetV2 } = foundry.applications.sheets; const { TextEditor } = foundry.applications.ux; @@ -370,47 +369,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', { ability: abilityLabel }), roll: { trait: button.dataset.attribute - /* label: abilityLabel, - modifier: button.dataset.value */ } - /* chatMessage: { - template: 'systems/daggerheart/templates/chat/duality-roll.hbs' - } */ }; this.document.diceRoll(config); - - // Delete when new roll logic test done - /* const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll( - { title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value }, - event.shiftKey - ); - - const cls = getDocumentClass('ChatMessage'); - - const systemContent = new DHDualityRoll({ - title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', { - ability: game.i18n.localize(abilities[button.dataset.attribute].label) - }), - origin: this.document.id, - roll: roll._formula, - modifiers: modifiers, - hope: hope, - fear: fear, - advantage: advantage, - disadvantage: disadvantage - }); - - await cls.create({ - type: 'dualityRoll', - sound: CONFIG.sounds.dice, - system: systemContent, - user: game.user.id, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/chat/duality-roll.hbs', - systemContent - ), - rolls: [roll] - }); */ } static async toggleMarks(_, button) { diff --git a/module/applications/sheets/environment.mjs b/module/applications/sheets/environment.mjs index 7c59fd55..fca33f4d 100644 --- a/module/applications/sheets/environment.mjs +++ b/module/applications/sheets/environment.mjs @@ -53,6 +53,7 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) { const context = await super._prepareContext(_options); context.document = this.document; context.tabs = super._getTabs(this.constructor.TABS); + context.getEffectDetails = this.getEffectDetails.bind(this); return context; } @@ -62,6 +63,10 @@ export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) { this.render(); } + getEffectDetails(id) { + return {}; + } + static async addAdversary() { await this.document.update({ [`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize( diff --git a/module/applications/sheets/item.mjs b/module/applications/sheets/item.mjs index 3b8fcb3f..0b0800f7 100644 --- a/module/applications/sheets/item.mjs +++ b/module/applications/sheets/item.mjs @@ -111,16 +111,18 @@ export default function DHItemMixin(Base) { } } - static async editAction(_, button) { - const action = this.document.system.actions[button.dataset.index]; + static async editAction(event, button) { + const index = event.target.closest('[data-index]').dataset.index, + action = this.document.system.actions[index]; await new DHActionConfig(action).render(true); } static async removeAction(event, button) { event.stopPropagation(); + const action = event.target.closest('[data-index]').dataset.index; await this.document.update({ 'system.actions': this.document.system.actions.filter( - (_, index) => index !== Number.parseInt(button.dataset.index) + (_, index) => index !== Number.parseInt(action) ) }); } diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 8ac9da49..a0af78eb 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -59,3 +59,21 @@ export const targetTypes = { label: 'Any' } }; + +export const damageOnSave = { + none: { + id: 'none', + label: 'None', + mod: 0 + }, + half: { + id: 'half', + label: 'Half Damage', + mod: 0.5 + }, + full: { + id: 'full', + label: 'Full damage', + mod: 1 + } +} diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index fc81641f..3beb92a9 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -1,52 +1,24 @@ -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'; import DhpActor from '../../documents/actor.mjs'; +import D20RollDialog from '../../dialogs/d20RollDialog.mjs'; const fields = foundry.data.fields; +/* + !!! I'm currently refactoring the whole Action thing, it's a WIP !!! +*/ + /* ToDo - - Add setting for Hope/Fear result on Damage, Heal, Resource (Handle Roll result as part of formula if needed) - Add setting and/or checkbox for cost and damage like - Target Check / Target Picker - Range Check - Area of effect and measurement placement - Summon Action create method - - Create classes form Target, Cost, etc ? - Other - - Add optionnal Role for Healing ? - Auto use action <= Into Roll - - Done - - Cost Check - - Auto use costs - - Auto disable selected Cost from other cost list - - Apply ActiveEffect => Add to Chat message like Damage Button ? - - Add Drag & Drop for documentUUID field (Macro & Summon) - - Activity Types List - - Attack => Weapon Attack, Spell Attack, etc... - - Effects => Like Attack without damage - - Damage => Like Attack without roll - - Healing - - Resource => Merge Healing & Resource ? - - Summon - - Sequencer => Trigger a list of Activities set on the item one by one - - Macro - - Actor Modifier - - Weapon Attack - - Spell Attack - - Weapon Damage - - Magical Damage - - Physical Damage ? - - Magical Damage ? - - Healing - - Bard Rally (Math.ceil(LeveL / 5)) */ export class DHBaseAction extends foundry.abstract.DataModel { @@ -105,7 +77,8 @@ export class DHBaseAction extends foundry.abstract.DataModel { }), 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 }) + difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }), + damageMod: new fields.StringField({ initial: SYSTEM.ACTIONS.damageOnSave.none.id, choices: SYSTEM.ACTIONS.damageOnSave }) }), target: new fields.SchemaField({ type: new fields.StringField({ @@ -118,7 +91,8 @@ export class DHBaseAction extends foundry.abstract.DataModel { }), effects: new fields.ArrayField( // ActiveEffect new fields.SchemaField({ - _id: new fields.DocumentIdField() + _id: new fields.DocumentIdField(), + onSave: new fields.BooleanField({ initial: false }) }) ), healing: new fields.SchemaField({ @@ -186,22 +160,122 @@ export class DHBaseAction extends foundry.abstract.DataModel { getRollData() { const actorData = this.actor.getRollData(false); - return { - ...actorData.toObject(), - prof: actorData.proficiency?.value ?? 1, - cast: actorData.spellcast?.value ?? 1, - scale: this.cost.length + + // Remove when included directly in Actor getRollData + actorData.prof = actorData.proficiency?.value ?? 1, + actorData.cast = actorData.spellcast?.value ?? 1, + actorData.scale = this.cost.length ? this.cost.reduce((a, c) => { a[c.type] = c.value; return a; }, {}) : 1, - roll: {} - }; + actorData.roll = {} + + return actorData; } async use(event, ...args) { + const isFastForward = event.shiftKey || (!this.hasRoll && !this.hasSave); + // Prepare base Config + const initConfig = this.initActionConfig(event); + // let config = this.initActionConfig(event); + + // Prepare Targets + const targetConfig = this.prepareTarget(); + if (isFastForward && !targetConfig) return ui.notifications.warn('Too many targets selected for that actions.'); + // config = this.prepareTarget(config); + + // Prepare Range + const rangeConfig = this.prepareRange(); + // config = this.prepareRange(config); + + // Prepare Costs + const costsConfig = this.prepareCost(); + if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action."); + // config = this.prepareUseCost(config) + + // Prepare Uses + const usesConfig = this.prepareUse(); + if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses."); + // config = this.prepareUseCost(config) + + // Prepare Roll Data + const actorData = this.getRollData(); + let config = { + ...initConfig, + targets: targetConfig, + range: rangeConfig, + costs: costsConfig, + uses: usesConfig, + data: actorData + } + + if ( Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false ) return; + + // Display configuration window if necessary + if ( config.dialog.configure && this.requireConfigurationDialog(config) ) { + config = await D20RollDialog.configure(config); + if (!config) return; + } + + if ( this.hasRoll ) { + const rollConfig = this.prepareRoll(config); + config.roll = rollConfig; + config = await this.actor.diceRoll(config); + if (!config) return; + } + + if( this.hasSave ) { + /* config.targets.forEach((t) => { + if(t.hit) { + const target = game.canvas.tokens.get(t.id), + actor = target?.actor; + console.log(actor) + if(!actor) return; + actor.saveRoll({ + event, + title: 'Roll Save', + roll: { + trait: this.save.trait, + difficulty: this.save.difficulty + }, + dialog: { + configure: false + }, + data: actor.getRollData() + }).then(async (result) => { + t.saved = result; + setTimeout(async () => { + const message = ui.chat.collection.get(config.message.id), + msgTargets = message.system.targets, + msgTarget = msgTargets.find(mt => mt.id === t.id); + msgTarget.saved = result; + await message.update({'system.targets': msgTargets}); + },100) + }) + } + }) */ + } + + if ( this.doFollowUp() ) { + if(this.rollDamage) await this.rollDamage(event, config); + if(this.rollHealing) await this.rollHealing(event, config); + if(this.trigger) await this.trigger(event, config); + } + + // Consume resources + await this.consume(config); + + if ( Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false ) return; + + return config; + } + + /* */ + initActionConfig(event) { + return { event, title: this.item.name, source: { @@ -209,79 +283,93 @@ export class DHBaseAction extends foundry.abstract.DataModel { action: this._id // action: this }, + dialog: { + configure: true + }, type: this.type, hasDamage: !!this.damage?.parts?.length, hasHealing: !!this.healing, - hasEffect: !!this.effects?.length - }; - - // this.proceedChatDisplay(config); - - // 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.'); - - // Display Uses/Costs Dialog & Check if Actor get enough resources - config = { - ...config, - ...(await this.getCost(config)) - }; - - if ((!this.hasRoll() || config.event.shiftKey) && (!this.hasCost(config.costs) || !this.hasUses(config.uses))) - return ui.notifications.warn("You don't have the resources to use that action."); - - // Proceed with Roll - config = await this.proceedRoll(config); - if (this.roll && !config.roll.result) return; - - // Update Actor resources based on Action Cost configuration - this.spendCost(config.costs.values); - this.spendUses(config.uses); - - return config; + hasEffect: !!this.effects?.length, + hasSave: this.hasSave + } } + requireConfigurationDialog(config) { + return !config.event.shiftkey && !this.hasRoll && (config.costs?.length || config.uses); + } + + prepareCost() { + const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : []; + return costs; + } + + prepareUse() { + const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null; + if (uses && !uses.value) uses.value = 0; + return uses; + } + + prepareTarget() { + let targets; + if (this.target?.type === SYSTEM.ACTIONS.targetTypes.self.id) + targets = this.formatTarget(this.actor.token ?? this.actor.prototypeToken); + 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) targets = []; + } + targets = targets.map(t => this.formatTarget(t)); + return targets; + + } + + prepareRange() { + const range = this.range ?? null; + return range; + } + + prepareRoll() { + const roll = { + modifiers: [], + trait: this.roll?.trait, + label: 'Attack', + type: this.actionType, + difficulty: this.roll?.difficulty + }; + return roll; + } + + doFollowUp(config) { + return !this.hasRoll; + } + + async consume(config) { + const resources = config.costs.filter(c => c.enabled !== false).map(c => { + return { type: c.type, value: c.total * -1 }; + }); + await this.actor.modifyResource(resources); + if(config.uses?.enabled) { + const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject()); + newActions[this.index].uses.value++; + await this.item.update({ [`system.${this.systemPath}`]: newActions }); + } + } + /* */ + /* ROLL */ - hasRoll() { - // return this.roll?.type && this.roll?.trait; + get hasRoll() { return !!this.roll?.type; } - - async proceedRoll(config) { - if (!this.hasRoll()) return config; - // const modifierValue = this.actor.system.traits[this.roll.trait].value; - config = { - ...config, - roll: { - modifiers: [], - trait: this.roll?.trait, - // label: game.i18n.localize(abilities[this.roll.trait].label), - label: 'Attack', - type: this.actionType, - difficulty: this.roll?.difficulty - } - }; - // config = await this.actor.diceRoll(config, this); - return this.actor.diceRoll(config, this); - } /* ROLL */ - /* COST */ - async getCost(config) { - let costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : []; - let uses = this.getUses(); - if (!config.event.shiftKey && !this.hasRoll() && !(!costs.length && !uses)) { - const dialogClosed = new Promise((resolve, _) => { - new CostSelectionDialog(costs, uses, this, resolve).render(true); - }); - ({ costs, uses } = await dialogClosed); - } - return { costs, uses }; + /* SAVE */ + get hasSave() { + return !!this.save?.trait; } + /* SAVE */ + + /* COST */ getRealCosts(costs) { const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; @@ -302,28 +390,9 @@ export class DHBaseAction extends foundry.abstract.DataModel { const realCosts = this.getRealCosts(costs); return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true); } - - async spendCost(config) { - if (!config.costs?.values?.length) return; - return await this.actor.modifyResource(config.costs.values); - } /* COST */ /* USES */ - async spendUses(config) { - if (!this.uses.max || config.enabled === false) return; - const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject()); - newActions[this.index].uses.value++; - await this.item.update({ [`system.${this.systemPath}`]: newActions }); - } - - getUses() { - if (!this.uses?.max) return null; - const uses = foundry.utils.deepClone(this.uses); - if (!uses.value) uses.value = 0; - return uses; - } - calcUses(uses) { if(!uses) return null; return { @@ -339,18 +408,6 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* USES */ /* 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)); - } - isTargetFriendly(target) { const actorDisposition = this.actor.token ? this.actor.token.disposition @@ -365,6 +422,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { formatTarget(actor) { return { id: actor.id, + actorId: actor.actor.uuid, name: actor.actor.name, img: actor.actor.img, difficulty: actor.actor.system.difficulty, @@ -374,18 +432,20 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* TARGET */ /* RANGE */ - async checkRange(config) { - if (!this.range || !this.actor) return true; - return { values: [], hasRange: true }; - } + /* RANGE */ /* EFFECTS */ async applyEffects(event, data, force = false) { if (!this.effects?.length || !data.system.targets.length) return; + let effects = this.effects; data.system.targets.forEach(async token => { if (!token.hit && !force) return; - this.effects.forEach(async e => { + if(this.hasSave && token.saved.success === true) { + effects = this.effects.filter(e => e.onSave === true) + } + if(!effects.length) return; + effects.forEach(async e => { const actor = canvas.tokens.get(token.id)?.actor, effect = this.item.effects.get(e._id); if (!actor || !effect) return; @@ -417,28 +477,55 @@ export class DHBaseAction extends foundry.abstract.DataModel { } /* EFFECTS */ - /* CHAT */ - async proceedChatDisplay(config) { - if (!this.chatDisplay) return; + /* SAVE */ + async rollSave(target, event, message) { + if(!target?.actor) return; + target.actor.diceRoll({ + event, + title: 'Roll Save', + roll: { + trait: this.save.trait, + difficulty: this.save.difficulty, + type: "reaction" + }, + data: target.actor.getRollData() + }).then(async (result) => { + this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success}); + }) } - /* CHAT */ + + async updateChatMessage(message, targetId, changes, chain=true) { + setTimeout(async () => { + const chatMessage = ui.chat.collection.get(message._id), + msgTargets = chatMessage.system.targets, + msgTarget = msgTargets.find(mt => mt.id === targetId); + msgTarget.saved = changes; + await chatMessage.update({'system.targets': msgTargets}); + },100); + if(chain) { + if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); + const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id); + relatedChatMessages.forEach(c => { + this.updateChatMessage(c, targetId, changes, false); + }) + } + } + /* SAVE */ } export class DHDamageAction extends DHBaseAction { - directDamage = true; - static extraSchemas = ['damage', 'target', 'effects']; - async use(event, ...args) { + /* async use(event, ...args) { const config = await super.use(event, args); if (!config || ['error', 'warning'].includes(config.type)) return; if (!this.directDamage) return; return await this.rollDamage(event, config); - } + } */ getFormulaValue(part, data) { let formulaValue = part.value; - if(this.hasRoll() && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; + if(this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; return formulaValue; } @@ -448,25 +535,25 @@ export class DHDamageAction extends DHBaseAction { if (!formula || formula == '') return; let roll = { formula: formula, total: formula }, bonusDamage = []; - + const config = { title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }), formula, - targets: (data.system?.targets ?? data.targets).map(x => ({ - id: x.id, - name: x.name, - img: x.img, - hit: true - })) + targets: (data.system?.targets.filter(t => t.hit) ?? data.targets), + hasSave: this.hasSave, + source: data.system?.source }; + if(this.hasSave) config.onSave = this.save.damageMod; + if(data.system) { + config.source.message = data._id; + config.directDamage = false; + } roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); } } export class DHAttackAction extends DHDamageAction { - directDamage = false; - static extraSchemas = [...super.extraSchemas, ...['roll', 'save']]; static getRollType(parent) { @@ -505,16 +592,9 @@ export class DHHealingAction extends DHBaseAction { return 'spellcast'; } - async use(event, ...args) { - const config = await super.use(event, args); - if (!config || ['error', 'warning'].includes(config.type)) return; - if (this.hasRoll()) return; - return await this.rollHealing(event, config); - } - getFormulaValue(data) { let formulaValue = this.healing.value; - if(this.hasRoll() && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt; + if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt; return formulaValue; } @@ -531,13 +611,8 @@ export class DHHealingAction extends DHBaseAction { healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label) }), formula, - targets: (data.system?.targets ?? data.targets).map(x => ({ - id: x.id, - name: x.name, - img: x.img, - hit: true - })), - messageTemplate: 'systems/daggerheart/templates/chat/healing-roll.hbs', + targets: (data.system?.targets ?? data.targets).filter(t => t.hit), + messageType: 'healing', type: this.healing.type }; @@ -557,9 +632,9 @@ export class DHSummonAction extends DHBaseAction { }; } - async use(event, ...args) { + async trigger(event, ...args) { if (!this.canSummon || !canvas.scene) return; - const config = await super.use(event, args); + // const config = await super.use(event, args); } get canSummon() { @@ -614,9 +689,9 @@ export class DHMacroAction extends DHBaseAction { }; } - async use(event, ...args) { - const config = await super.use(event, args); - if (['error', 'warning'].includes(config.type)) return; + async trigger(event, ...args) { + // const config = await super.use(event, args); + // if (['error', 'warning'].includes(config.type)) return; const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID, macro = await fromUuid(fixUUID); try { diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 211e32dd..d97783c8 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -1,6 +1,3 @@ -import DhpActor from '../../documents/actor.mjs'; -import ActionField from '../fields/actionField.mjs'; - export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { static defineSchema() { const fields = foundry.data.fields; @@ -11,16 +8,22 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { targets: new fields.ArrayField( new fields.SchemaField({ id: new fields.StringField({}), + actorId: new fields.StringField({}), name: new fields.StringField({}), img: new fields.StringField({}), difficulty: new fields.NumberField({ integer: true, nullable: true }), evasion: new fields.NumberField({ integer: true }), - hit: new fields.BooleanField({ initial: false }) + hit: new fields.BooleanField({ initial: false }), + saved: new fields.SchemaField({ + result: new fields.NumberField(), + success: new fields.BooleanField({ nullable: true, initial: null }) + }) }) ), hasDamage: new fields.BooleanField({ initial: false }), hasHealing: new fields.BooleanField({ initial: false }), hasEffect: new fields.BooleanField({ initial: false }), + hasSave: new fields.BooleanField({ initial: false }), source: new fields.SchemaField({ actor: new fields.StringField(), item: new fields.StringField(), @@ -28,4 +31,8 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { }) }; } + + get messageTemplate() { + return 'systems/daggerheart/templates/chat/adversary-roll.hbs'; + } } diff --git a/module/data/chat-message/damageRoll.mjs b/module/data/chat-message/damageRoll.mjs index 1b130d76..29ad6fbd 100644 --- a/module/data/chat-message/damageRoll.mjs +++ b/module/data/chat-message/damageRoll.mjs @@ -3,34 +3,35 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { const fields = foundry.data.fields; return { + messageType: new fields.StringField({initial: 'damage'}), title: new fields.StringField(), - // roll: new fields.StringField({ required: true }), roll: new fields.DataField({}), - /* damage: new fields.SchemaField({ - total: new fields.NumberField({ required: true, integer: true }), - type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }) - }), - dice: new fields.ArrayField( - new fields.SchemaField({ - type: new fields.StringField({ required: true }), - rolls: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })), - total: new fields.NumberField({ integer: true }) - }) - ), - modifiers: new fields.ArrayField( - new fields.SchemaField({ - value: new fields.NumberField({ required: true, integer: true }), - operator: new fields.StringField({ required: true, choices: ['+', '-', '*', '/'] }) - }) - ), */ targets: new fields.ArrayField( new fields.SchemaField({ id: new fields.StringField({ required: true }), + actorId: new fields.StringField({}), name: new fields.StringField(), img: new fields.StringField(), - hit: new fields.BooleanField({ initial: false }) + hit: new fields.BooleanField({ initial: false }), + saved: new fields.SchemaField({ + result: new fields.NumberField(), + success: new fields.BooleanField({ nullable: true, initial: null }) + }) }) - ) + ), + hasSave: new fields.BooleanField({ initial: false }), + onSave: new fields.StringField(), + source: new fields.SchemaField({ + actor: new fields.StringField(), + item: new fields.StringField(), + action: new fields.StringField(), + message: new fields.StringField() + }), + directDamage: new fields.BooleanField({initial: true}) }; } + + get messageTemplate() { + return `systems/daggerheart/templates/chat/${this.messageType}-roll.hbs`; + } } diff --git a/module/data/chat-message/dualityRoll.mjs b/module/data/chat-message/dualityRoll.mjs index 55b20444..0f3f12de 100644 --- a/module/data/chat-message/dualityRoll.mjs +++ b/module/data/chat-message/dualityRoll.mjs @@ -14,16 +14,22 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel { targets: new fields.ArrayField( new fields.SchemaField({ id: new fields.StringField({}), + actorId: new fields.StringField({}), name: new fields.StringField({}), img: new fields.StringField({}), difficulty: new fields.NumberField({ integer: true, nullable: true }), evasion: new fields.NumberField({ integer: true }), - hit: new fields.BooleanField({ initial: false }) + hit: new fields.BooleanField({ initial: false }), + saved: new fields.SchemaField({ + result: new fields.NumberField(), + success: new fields.BooleanField({ nullable: true, initial: null }) + }) }) ), hasDamage: new fields.BooleanField({ initial: false }), hasHealing: new fields.BooleanField({ initial: false }), hasEffect: new fields.BooleanField({ initial: false }), + hasSave: new fields.BooleanField({ initial: false }), source: new fields.SchemaField({ actor: new fields.StringField(), item: new fields.StringField(), @@ -31,4 +37,8 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel { }) }; } + + get messageTemplate() { + return 'systems/daggerheart/templates/chat/duality-roll.hbs'; + } } diff --git a/module/dialogs/d20RollDialog.mjs b/module/dialogs/d20RollDialog.mjs index 52b940e7..0b64615b 100644 --- a/module/dialogs/d20RollDialog.mjs +++ b/module/dialogs/d20RollDialog.mjs @@ -50,6 +50,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio async _prepareContext(_options) { const context = await super._prepareContext(_options); + context.hasRoll = !!this.config.roll; context.experiences = Object.keys(this.config.data.experiences).map(id => ({ id, ...this.config.data.experiences[id] @@ -67,6 +68,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.uses = this.action.calcUses(this.config.uses); context.canRoll = context.canRoll && this.action.hasUses(context.uses); } + console.log(context, _options) return context; } @@ -101,9 +103,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio if (!options.submitted) this.config = false; } - static async configure(config = {}) { + static async configure(config = {}, options={}) { return new Promise(resolve => { - const app = new this(config); + const app = new this(config, options); app.addEventListener('close', () => resolve(app.config), { once: true }); app.render({ force: true }); }); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 90ff49d5..6ba3df94 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,9 +1,5 @@ import DamageSelectionDialog from '../applications/damageSelectionDialog.mjs'; -import NpcRollSelectionDialog from '../applications/npcRollSelectionDialog.mjs'; -import RollSelectionDialog from '../applications/rollSelectionDialog.mjs'; import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs'; -import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; -import DHDualityRoll from '../data/chat-message/dualityRoll.mjs'; import DamageReductionDialog from '../applications/damageReductionDialog.mjs'; export default class DhpActor extends Actor { @@ -265,12 +261,14 @@ export default class DhpActor extends Actor { * @param {object} [config.targets] * @param {object} [config.costs] */ - async diceRoll(config, action) { - // config.source = {...(config.source ?? {}), actor: this._id}; + async diceRoll(config) { config.source = {...(config.source ?? {}), actor: this.uuid}; - config.data = this.getRollData() - const roll = await CONFIG.Dice.daggerheart[this.type === 'character' ? 'DualityRoll' : 'D20Roll'].build(config) - return config; + config.data = this.getRollData(); + return await this.rollClass.build(config); + } + + get rollClass() { + return CONFIG.Dice.daggerheart[this.type === 'character' ? 'DualityRoll' : 'D20Roll']; } getRollData() { diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 584a50ce..3f537adf 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -22,6 +22,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.duality-action-healing').forEach(element => element.addEventListener('click', event => this.onRollHealing(event, data.message)) ); + html.querySelectorAll('.target-save-container').forEach(element => + element.addEventListener('click', event => this.onRollSave(event, data.message)) + ); html.querySelectorAll('.duality-action-effect').forEach(element => element.addEventListener('click', event => this.onApplyEffect(event, data.message)) ); @@ -98,6 +101,20 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } }; + onRollSave = async (event, message) => { + event.stopPropagation(); + const actor = await this.getActor(message.system.source.actor), + tokenId = event.target.closest('[data-token]')?.dataset.token, + token = game.canvas.tokens.get(tokenId); + if (!token?.actor || !token.isOwner) return true; + console.log(token.actor.canUserModify(game.user, 'update')); + if (message.system.source.item && message.system.source.action) { + const action = this.getAction(actor, message.system.source.item, message.system.source.action); + if (!action || !action?.hasSave) return; + action.rollSave(token, event, message); + } + }; + onApplyEffect = async (event, message) => { event.stopPropagation(); const actor = await this.getActor(message.system.source.actor); @@ -137,10 +154,24 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo ? message.system.targets.map(target => game.canvas.tokens.get(target.id)) : Array.from(game.user.targets); + if(message.system.onSave && event.currentTarget.dataset.targetHit) { + console.log(message.system.targets) + const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null); + if(pendingingSaves.length) { + const confirm = await foundry.applications.api.DialogV2.confirm({ + window: {title: "Pending Reaction Rolls found"}, + content: `

Some Tokens still need to roll their Reaction Roll.

Are you sure you want to continue ?

Undone reaction rolls will be considered as failed

` + }); + if ( !confirm ) return; + } + } + if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); - for (var target of targets) { - await target.actor.takeDamage(message.system.roll.result, message.system.roll.type); + for (let target of targets) { + let damage = message.system.roll.total; + if(message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1)); + await target.actor.takeDamage(damage, message.system.roll.type); } }; @@ -152,7 +183,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.takeHealing([{ value: message.system.roll.result, type: message.system.roll.type }]); + await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]); } }; diff --git a/styles/chat.less b/styles/chat.less index 8dbf8ddc..9af413a0 100644 --- a/styles/chat.less +++ b/styles/chat.less @@ -6,6 +6,13 @@ } } +fieldset.daggerheart.chat { + padding: 0; + border-left-width: 0; + border-right-width: 0; + border-bottom-width: 0; +} + .daggerheart.chat { &.downtime { display: flex; @@ -227,19 +234,35 @@ background: @miss; } - img { - flex: 0; + img, .target-save-container { width: 22px; height: 22px; - margin-left: 8px; align-self: center; border-color: transparent; } + img { + flex: 0; + margin-left: 8px; + } + + .target-save-container { + margin-right: 8px; + justify-content: center; + display: flex; + align-items: center; + min-height: unset; + border: 1px solid black; + } + .target-inner-container { flex: 1; display: flex; justify-content: center; + font-size: var(--font-size-16); + } + + &:not(:has(.target-save-container)) .target-inner-container { margin-right: @hugeMargin; } } @@ -313,12 +336,29 @@ width: 80px; } } + + [data-use-perm='false'] { + pointer-events: none; + border-color: transparent; + } + [data-view-perm='false'] { + > * { + display: none; + } + &::after { + content: "??"; + } + } } .theme-colorful { .chat-message.duality { border-color: black; padding: 8px 0 0 0; + fieldset.daggerheart.chat { + border-top-width: 0; + display: contents; + } .message-header { color: var(--color-light-3); padding: 0 8px; diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 2fb08edf..f26dddd4 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -1399,6 +1399,12 @@ .chat-message .dice-title { display: none; } +fieldset.daggerheart.chat { + padding: 0; + border-left-width: 0; + border-right-width: 0; + border-bottom-width: 0; +} .daggerheart.chat.downtime { display: flex; flex-direction: column; @@ -1552,18 +1558,32 @@ .daggerheart.chat.roll .target-section .target-container.miss { background: #ff0000; } -.daggerheart.chat.roll .target-section .target-container img { - flex: 0; +.daggerheart.chat.roll .target-section .target-container img, +.daggerheart.chat.roll .target-section .target-container .target-save-container { width: 22px; height: 22px; - margin-left: 8px; align-self: center; border-color: transparent; } +.daggerheart.chat.roll .target-section .target-container img { + flex: 0; + margin-left: 8px; +} +.daggerheart.chat.roll .target-section .target-container .target-save-container { + margin-right: 8px; + justify-content: center; + display: flex; + align-items: center; + min-height: unset; + border: 1px solid black; +} .daggerheart.chat.roll .target-section .target-container .target-inner-container { flex: 1; display: flex; justify-content: center; + font-size: var(--font-size-16); +} +.daggerheart.chat.roll .target-section .target-container:not(:has(.target-save-container)) .target-inner-container { margin-right: 32px; } .daggerheart.chat.roll .dice-actions { @@ -1620,10 +1640,24 @@ .daggerheart.chat.domain-card img { width: 80px; } +.daggerheart.chat [data-use-perm='false'] { + pointer-events: none; + border-color: transparent; +} +.daggerheart.chat [data-view-perm='false'] > * { + display: none; +} +.daggerheart.chat [data-view-perm='false']::after { + content: "??"; +} .theme-colorful .chat-message.duality { border-color: black; padding: 8px 0 0 0; } +.theme-colorful .chat-message.duality fieldset.daggerheart.chat { + border-top-width: 0; + display: contents; +} .theme-colorful .chat-message.duality .message-header { color: var(--color-light-3); padding: 0 8px; diff --git a/templates/chat/adversary-roll.hbs b/templates/chat/adversary-roll.hbs index fa463bad..92bb1c96 100644 --- a/templates/chat/adversary-roll.hbs +++ b/templates/chat/adversary-roll.hbs @@ -26,26 +26,18 @@ -
{{roll.total}}
- {{#if (gt targets.length 0)}} -
- {{#each targets as |target|}} -
- -
- {{#if target.hit}}{{localize "Hit"}}{{else}}{{localize "Miss"}}{{/if}} -
-
- {{/each}} -
- {{/if}} - {{#if hasDamage}} + + +{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}} +{{#if hasDamage}} +
+
- {{/if}}
-
\ No newline at end of file + +{{/if}} \ No newline at end of file diff --git a/templates/chat/damage-roll.hbs b/templates/chat/damage-roll.hbs index 370e14dc..9442fe5a 100644 --- a/templates/chat/damage-roll.hbs +++ b/templates/chat/damage-roll.hbs @@ -1,15 +1,14 @@
{{title}}
-
{{formula}}
+
{{roll.formula}}
{{#each roll.dice}}
- {{formula}} - + {{formula}} {{total}}
    @@ -22,7 +21,12 @@
-
{{roll.result}}
+
{{roll.total}}
+
+
+{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}} +
+
diff --git a/templates/chat/duality-roll.hbs b/templates/chat/duality-roll.hbs index f13a7a40..2d77f815 100644 --- a/templates/chat/duality-roll.hbs +++ b/templates/chat/duality-roll.hbs @@ -92,18 +92,11 @@ {{roll.total}}
- {{#if (gt targets.length 0)}} -
- {{#each targets as |target|}} -
- -
- {{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}} -
-
- {{/each}} -
- {{/if}} +
+ +{{> 'systems/daggerheart/templates/chat/parts/target-chat.hbs'}} +
+
{{#if hasDamage}} diff --git a/templates/chat/healing-roll.hbs b/templates/chat/healing-roll.hbs index 7a089459..3e18a185 100644 --- a/templates/chat/healing-roll.hbs +++ b/templates/chat/healing-roll.hbs @@ -1,7 +1,7 @@
{{title}}
-
{{formula}}
+
{{roll.formula}}
@@ -9,7 +9,6 @@
{{formula}} - {{total}}
    @@ -22,7 +21,7 @@
-
{{roll.result}}
+
{{roll.total}}
diff --git a/templates/chat/parts/target-chat.hbs b/templates/chat/parts/target-chat.hbs new file mode 100644 index 00000000..d2fc81ea --- /dev/null +++ b/templates/chat/parts/target-chat.hbs @@ -0,0 +1,32 @@ +{{#if (gt targets.length 0)}} +
+ Targets +
+
+
+
+ {{#each targets as |target|}} +
+ +
+ {{#if ../directDamage}} +
{{target.name}}
+ {{else}} + {{#if target.hit}}{{localize "Hit"}}{{else}}{{#if (not ../total.alternate)}}{{localize "Miss"}}{{else}}?{{/if}}{{/if}} + {{/if}} +
+ {{#if ../hasSave}} + + {{/if}} +
+ {{/each}} +
+
+
+
+
+{{/if}} \ No newline at end of file diff --git a/templates/sheets/actors/adversary/main.hbs b/templates/sheets/actors/adversary/main.hbs index 995befc8..541c5aca 100644 --- a/templates/sheets/actors/adversary/main.hbs +++ b/templates/sheets/actors/adversary/main.hbs @@ -73,7 +73,7 @@
{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=source.system.attack.damage path="system.attack."}}
- {{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs'}} + {{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=systemFields.attack.fields.effects.element.fields source=source.system.attack.effects}}
diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index c79ed105..68a77d07 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -67,8 +67,8 @@

{{localize "DAGGERHEART.General.Hope"}}

{{#times document.system.resources.hope.max}} - - {{#if (gte ../document.system.resources.hope.value this)}} + + {{#if (gte ../document.system.resources.hope.value (add this 1))}} {{else}} diff --git a/templates/sheets/global/tabs/tab-actions.hbs b/templates/sheets/global/tabs/tab-actions.hbs index 6cc1b652..44c6082d 100644 --- a/templates/sheets/global/tabs/tab-actions.hbs +++ b/templates/sheets/global/tabs/tab-actions.hbs @@ -7,12 +7,14 @@ {{localize "DAGGERHEART.Sheets.Global.Actions"}}
{{#each document.system.actions as |action index|}} -
+
{{action.name}}
- - +
{{/each}} diff --git a/templates/sheets/parts/hope.hbs b/templates/sheets/parts/hope.hbs index eec38297..cfd58d48 100644 --- a/templates/sheets/parts/hope.hbs +++ b/templates/sheets/parts/hope.hbs @@ -6,7 +6,7 @@
{{#times 6}} - + {{#if (gte this ../document.system.resources.hope.max)}}{{/if}} {{/times}} diff --git a/templates/views/action.hbs b/templates/views/action.hbs index 79faf4b8..45373815 100644 --- a/templates/views/action.hbs +++ b/templates/views/action.hbs @@ -41,11 +41,12 @@
{{#if fields.roll}}{{> 'systems/daggerheart/templates/views/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}} + {{#if fields.save}}{{> 'systems/daggerheart/templates/views/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}} {{#if fields.damage}}{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage}}{{/if}} {{#if fields.healing}}{{> 'systems/daggerheart/templates/views/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}} {{#if fields.resource}}{{> 'systems/daggerheart/templates/views/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}} {{#if fields.documentUUID}}{{> 'systems/daggerheart/templates/views/actionTypes/uuid.hbs' fields=fields.documentUUID source=source.documentUUID}}{{/if}} - {{#if fields.effects}}{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs'}}{{/if}} + {{#if fields.effects}}{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
\ No newline at end of file diff --git a/templates/views/actionTypes/damage.hbs b/templates/views/actionTypes/damage.hbs index 1f8f81d1..54fdb6b1 100644 --- a/templates/views/actionTypes/damage.hbs +++ b/templates/views/actionTypes/damage.hbs @@ -14,15 +14,15 @@ {{/unless}} {{#each source.parts as |dmg index|}} {{#if @root.isNPC}} - {{formField ../fields.custom.fields.enabled value=dmg.custom.enabled name=(concat ../path "damage.parts." index ".custom.enabled")}} - - {{#if dmg.custom.enabled}} - {{formField ../fields.custom.fields.formula value=dmg.custom.formula name=(concat ../path "damage.parts." index ".custom.formula") localize=true}} + {{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled")}} + + {{#if dmg.value.custom.enabled}} + {{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}} {{else}}
- {{formField ../fields.flatMultiplier value=dmg.flatMultiplier name=(concat ../path "damage.parts." index ".flatMultiplier") label="Multiplier" }} - {{formField ../fields.dice value=dmg.dice name=(concat ../path "damage.parts." index ".dice")}} - {{formField ../fields.bonus value=dmg.bonus name=(concat ../path "damage.parts." index ".bonus") localize=true}} + {{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="Multiplier" }} + {{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice")}} + {{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true}}
{{/if}} {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}} diff --git a/templates/views/actionTypes/effect.hbs b/templates/views/actionTypes/effect.hbs index 0f99327e..19ef45f3 100644 --- a/templates/views/actionTypes/effect.hbs +++ b/templates/views/actionTypes/effect.hbs @@ -4,15 +4,31 @@
- {{#each @root.effects as | effect index | }} + {{!-- {{#each @root.effects as | effect index | }}
- {{!--
--}} +
- {{!--
--}} + {{formfield }} +
+
+ {{/each}} --}} + {{#each source as | effect index |}} +
+
+ {{#with (@root.getEffectDetails effect._id) as | details |}} +
+ + +
+
+ {{/with}} + + {{formField ../fields.onSave value=effect.onSave name=(concat "effects." index ".onSave")}} +
{{/each}}
diff --git a/templates/views/actionTypes/roll.hbs b/templates/views/actionTypes/roll.hbs index 79fd1f03..7e40a821 100644 --- a/templates/views/actionTypes/roll.hbs +++ b/templates/views/actionTypes/roll.hbs @@ -7,8 +7,8 @@ {{formField fields.bonus label="Bonus" name="roll.bonus" value=source.bonus}} {{else}} {{formField fields.type label="Type" name="roll.type" value=source.type localize=true}} - {{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true}} - {{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty}} + {{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true disabled=(not source.type)}} + {{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty disabled=(not source.type)}} {{/if}}
\ No newline at end of file diff --git a/templates/views/actionTypes/save.hbs b/templates/views/actionTypes/save.hbs new file mode 100644 index 00000000..2b6e3c03 --- /dev/null +++ b/templates/views/actionTypes/save.hbs @@ -0,0 +1,10 @@ +
+ +
Save
+
+
+ {{formField fields.trait label="Trait" name="save.trait" value=source.trait localize=true}} + {{formField fields.difficulty label="Difficulty" name="save.difficulty" value=source.difficulty disabled=(not source.trait)}} + {{formField fields.damageMod label="Damage on Save" name="save.damageMod" value=source.damageMod localize=true disabled=(not source.trait)}} +
+
\ No newline at end of file diff --git a/templates/views/costSelection.hbs b/templates/views/costSelection.hbs index a8246858..39ef7cf7 100644 --- a/templates/views/costSelection.hbs +++ b/templates/views/costSelection.hbs @@ -18,7 +18,7 @@
{{/each}} -
+ {{!--
-
+
--}}
\ No newline at end of file diff --git a/templates/views/rollSelection.hbs b/templates/views/rollSelection.hbs index 679ef0a9..3a9cf786 100644 --- a/templates/views/rollSelection.hbs +++ b/templates/views/rollSelection.hbs @@ -1,4 +1,5 @@
+ {{#if @root.hasRoll}}
@@ -34,8 +35,9 @@
--}} {{!-- {{/if}} --}}
-
- -
+ {{/if}} +
\ No newline at end of file