diff --git a/lang/en.json b/lang/en.json index 0b43ee8f..0732f34e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -531,6 +531,14 @@ "Stress": { "Name": "Stress", "Stress": "STR" + }, + "Hope": { + "Name": "Hope", + "Abbreviation": "HO" + }, + "ArmorStack": { + "Name": "Armor Stack", + "Stress": "AS" } }, "ArmorFeature": { diff --git a/module/applications/config/Action.mjs b/module/applications/config/Action.mjs index 5c992ae8..75127b60 100644 --- a/module/applications/config/Action.mjs +++ b/module/applications/config/Action.mjs @@ -60,7 +60,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { context.tabs = this._getTabs(); context.config = SYSTEM; if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id)); - if (this.action.damage?.hasOwnProperty('includeBase')) context.hasBaseDamage = !!this.action.parent.damage; + 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/roll.mjs b/module/applications/roll.mjs new file mode 100644 index 00000000..0d4d2a05 --- /dev/null +++ b/module/applications/roll.mjs @@ -0,0 +1,34 @@ + +export default class DHRoll extends Roll { + static async build(config={}, message={}) { + const roll = await this.buildConfigure(); + await this.buildEvaluate(config, message={}); + await this.buildPost(config, message={}); + return roll; + } + + static async buildConfigure(config={}, message={}) { + config.hooks = [...(config.hooks ?? []), ""]; + for ( const hook of config.hooks ) { + if ( Hooks.call(`dnd5e.preRoll${hook.capitalize()}`, config, message) === false ) return null; + } + } + + static async buildEvaluate(roll, config={}, message={}) { + if(config.evaluate !== false) await roll.evalutate(); + } + + static async buildPost(config, message) { + for ( const hook of config.hooks ) { + if ( Hooks.call(`dnd5e.postRoll${hook.capitalize()}`, config, message) === false ) return null; + } + // Create Chat Message + await this.toMessage(roll, message.data); + } + + static async toMessage(roll, data) { + + const cls = getDocumentClass("ChatMessage"); + const msg = new cls(data); + } +} \ No newline at end of file diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 8c67de17..663a29f4 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -4,21 +4,21 @@ export const actionTypes = { name: 'DAGGERHEART.Actions.Types.Attack.Name', icon: 'fa-swords' }, - spellcast: { - id: 'spellcast', - name: 'DAGGERHEART.Actions.Types.Spellcast.Name', - icon: 'fa-book-sparkles' - }, + // spellcast: { + // id: 'spellcast', + // name: 'DAGGERHEART.Actions.Types.Spellcast.Name', + // icon: 'fa-book-sparkles' + // }, healing: { id: 'healing', name: 'DAGGERHEART.Actions.Types.Healing.Name', icon: 'fa-kit-medical' }, - resource: { - id: 'resource', - name: 'DAGGERHEART.Actions.Types.Resource.Name', - icon: 'fa-honey-pot' - }, + // resource: { + // id: 'resource', + // name: 'DAGGERHEART.Actions.Types.Resource.Name', + // icon: 'fa-honey-pot' + // }, damage: { id: 'damage', name: 'DAGGERHEART.Actions.Types.Damage.Name', diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index cea1fe55..6e2103c5 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -70,6 +70,16 @@ export const healingTypes = { id: 'stress', label: 'DAGGERHEART.HealingType.Stress.Name', abbreviation: 'DAGGERHEART.HealingType.Stress.Abbreviation' + }, + hope: { + id: 'hope', + label: 'DAGGERHEART.HealingType.Hope.Name', + abbreviation: 'DAGGERHEART.HealingType.Hope.Abbreviation' + }, + armorStack: { + id: 'armorStack', + label: 'DAGGERHEART.HealingType.ArmorStack.Name', + abbreviation: 'DAGGERHEART.HealingType.ArmorStack.Abbreviation' } }; diff --git a/module/data/action/_module.mjs b/module/data/action/_module.mjs index c9088886..23d4e3c1 100644 --- a/module/data/action/_module.mjs +++ b/module/data/action/_module.mjs @@ -5,16 +5,16 @@ import { DHEffectAction, DHHealingAction, DHMacroAction, - DHResourceAction, - DHSpellCastAction, + // DHResourceAction, + // DHSpellCastAction, DHSummonAction } from './action.mjs'; export const actionsTypes = { base: DHBaseAction, attack: DHAttackAction, - spellcast: DHSpellCastAction, - resource: DHResourceAction, + // spellcast: DHSpellCastAction, + // resource: DHResourceAction, damage: DHDamageAction, healing: DHHealingAction, summon: DHSummonAction, diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index 7a976f29..274f4041 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -14,6 +14,16 @@ const fields = foundry.data.fields; - Range Check - Area of effect and measurement placement - Auto use costs and action + + 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 */ export class DHBaseAction extends foundry.abstract.DataModel { @@ -49,9 +59,9 @@ export class DHBaseAction extends foundry.abstract.DataModel { }), range: new fields.StringField({ choices: SYSTEM.GENERAL.range, - required: true, - blank: false, - initial: 'self' + required: false, + blank: true, + initial: null }) }; } @@ -74,7 +84,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { return 'systems/daggerheart/templates/chat/attack-roll.hbs'; } - static getRollType() { + static getRollType(parent) { return 'ability'; } @@ -83,17 +93,36 @@ export class DHBaseAction extends foundry.abstract.DataModel { updateSource.img ??= parent?.img ?? parent?.system?.img; if (parent?.system?.trait) { updateSource['roll'] = { - type: this.getRollType(), + type: this.getRollType(parent), trait: parent.system.trait }; } + if(parent?.type === 'weapon' && !!this.schema.fields.damage) { + updateSource['damage'] = {includeBase: true}; + } if (parent?.system?.range) { updateSource['range'] = parent?.system?.range; } return updateSource; } - async use(event) { + getRollData() { + const actorData = this.actor.getRollData(false); + return { + ...actorData.toObject(), + prof: actorData.proficiency?.value ?? 1, + cast: actorData.spellcast?.value ?? 1, + scale: this.cost.length ? this.cost.reduce((a,c) => {a[c.type] = c.value; return a},{}) : 1 + } + } + + async use(event, ...args) { + // throw new Error("Activity must implement a 'use' method."); + const data = { + itemUUID: this.item, + activityId: this.id + }; + 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."); @@ -104,7 +133,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { if(this.range) { const hasRange = await this.checkRange(); } - if (this.roll.type && this.roll.trait) { + if (this.roll?.type && this.roll?.trait) { const modifierValue = this.actor.system.traits[this.roll.trait].value; const config = { event: event, @@ -129,8 +158,13 @@ export class DHBaseAction extends foundry.abstract.DataModel { if (this.effects.length) { // Apply Active Effects. In Chat Message ? } - return this.actor.diceRoll(config); + data.roll = await this.actor.diceRoll(config); } + if(this.withMessage || true) { + + } + + return data; } async checkCost() { @@ -174,22 +208,22 @@ export class DHBaseAction extends foundry.abstract.DataModel { } } -const tmpTargetObject = () => { - -} - const extraDefineSchema = (field, option) => { return { [field]: { // damage: new fields.SchemaField({ // parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)) // }), - damage: new DHDamageField(option), + 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, @@ -207,72 +241,61 @@ const extraDefineSchema = (field, option) => { }; }; -export class DHAttackAction extends DHBaseAction { - static defineSchema() { - return { - ...super.defineSchema(), - ...extraDefineSchema('damage', true), - ...extraDefineSchema('roll'), - ...extraDefineSchema('target'), - ...extraDefineSchema('effects') - }; - } +export class DHDamageAction extends DHBaseAction { + directDamage = true; - static getRollType() { - return 'weapon'; - } - - prepareData() { - super.prepareData(); - if (this.damage.includeBase && !!this.item?.system?.damage) { - const baseDamage = this.getParentDamage(); - this.damage.parts.unshift(new DHDamageData(baseDamage)); - } - } - - getParentDamage() { - return { - multiplier: 'proficiency', - dice: this.item?.system?.damage.value, - bonus: this.item?.system?.damage.bonus ?? 0, - type: this.item?.system?.damage.type, - base: true - }; - } - - // Temporary until full formula parser - // getDamageFormula() { - // return this.damage.parts.map(p => p.formula).join(' + '); - // } -} - -export class DHSpellCastAction extends DHBaseAction { static defineSchema() { return { ...super.defineSchema(), ...extraDefineSchema('damage'), - ...extraDefineSchema('roll'), ...extraDefineSchema('target'), ...extraDefineSchema('effects') }; } - static getRollType() { - return 'spellcast'; - } -} + async use(event, ...args) { + const messageData = await super.use(event, args); + if(!this.directDamage) return; + const roll = await this.rollDamage(); + if(!roll) return; + const cls = getDocumentClass('ChatMessage'), + msg = new cls({ + user: game.user.id, + content: await foundry.applications.handlebars.renderTemplate( + this.chatTemplate, + { + ...roll, + ...messageData + } + ) + }); -export class DHDamageAction extends DHBaseAction { - static defineSchema() { + 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 { - ...super.defineSchema(), - ...extraDefineSchema('damage', false), - ...extraDefineSchema('target'), - ...extraDefineSchema('effects') - }; + roll: roll.formula, + total: roll.total, + dice: roll.dice, + type: this.damage.parts.map(p => p.type) + } } - async use(event) { + get chatTemplate() { + return 'systems/daggerheart/templates/chat/damage-roll.hbs'; + } + + /* async use(event, ...args) { const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '); if (!formula || formula == '') return; @@ -295,9 +318,87 @@ export class DHDamageAction extends DHBaseAction { }); cls.create(msg.toObject()); - } + } */ + + /* async applyDamage(targets, value) { + const promises = []; + for(let t of targets) { + if(!t) continue; + promises.push(new Promise(async (resolve, reject) => { + await t.takeDamage(value, 'physical'); // Apply one instance of damage per parts ? + resolve(); + }) + ) + } + return Promise.all(promises).then((values) => { + return values; + }); + } */ } +export class DHAttackAction extends DHDamageAction { + directDamage = false; + + 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'; + } + + prepareData() { + super.prepareData(); + if (this.damage.includeBase && !!this.item?.system?.damage) { + const baseDamage = this.getParentDamage(); + this.damage.parts.unshift(new DHDamageData(baseDamage)); + } + } + + getParentDamage() { + return { + multiplier: 'proficiency', + dice: this.item?.system?.damage.value, + bonus: this.item?.system?.damage.bonus ?? 0, + type: this.item?.system?.damage.type, + base: true + }; + } + + /* async use(event, ...args) { + + } */ + + // Temporary until full formula parser + // getDamageFormula() { + // return this.damage.parts.map(p => p.formula).join(' + '); + // } +} + +/* export class DHSpellCastAction extends DHBaseAction { + static defineSchema() { + return { + ...super.defineSchema(), + ...extraDefineSchema('damage'), + ...extraDefineSchema('roll'), + ...extraDefineSchema('target'), + ...extraDefineSchema('effects') + }; + } + + static getRollType(parent) { + return 'spellcast'; + } +} */ + export class DHHealingAction extends DHBaseAction { static defineSchema() { return { @@ -317,30 +418,38 @@ export class DHHealingAction extends DHBaseAction { }; } - async use(event) { + async use(event, ...args) { + const messageData = await super.use(event, args), + roll = await this.rollHealing(), + cls = getDocumentClass('ChatMessage'), + msg = new cls({ + user: game.user.id, + content: await foundry.applications.handlebars.renderTemplate( + this.chatTemplate, + { + ...roll, + ...messageData + } + ) + }); + + cls.create(msg.toObject()); + } + + async rollHealing() { const formula = this.healing.value.getFormula(this.actor); if (!formula || formula == '') return; - // const roll = await super.use(event); let roll = { formula: formula, total: formula }; if (isNaN(formula)) { - roll = await new Roll(formula).evaluate(); + roll = await new Roll(formula, this.getRollData()).evaluate(); + } + return { + roll: roll.formula, + total: roll.total, + dice: roll.dice, + type: this.healing.type } - - const cls = getDocumentClass('ChatMessage'); - const msg = new cls({ - user: game.user.id, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/chat/healing-roll.hbs', - { - roll: roll.formula, - total: roll.total, - type: this.healing.type - } - ) - }); - - cls.create(msg.toObject()); } get chatTemplate() { @@ -348,7 +457,7 @@ export class DHHealingAction extends DHBaseAction { } } -export class DHResourceAction extends DHBaseAction { +/* export class DHResourceAction extends DHBaseAction { static defineSchema() { return { ...super.defineSchema(), @@ -367,7 +476,7 @@ export class DHResourceAction extends DHBaseAction { }) }; } -} +} */ export class DHSummonAction extends DHBaseAction { static defineSchema() { @@ -395,7 +504,7 @@ export class DHMacroAction extends DHBaseAction { }; } - async use(event) { + async use(event, ...args) { const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID, macro = await fromUuid(fixUUID); try { diff --git a/module/data/action/actionDice.mjs b/module/data/action/actionDice.mjs index 9fd445cc..591a2baf 100644 --- a/module/data/action/actionDice.mjs +++ b/module/data/action/actionDice.mjs @@ -23,16 +23,17 @@ export class DHActionDiceData extends foundry.abstract.DataModel { getFormula(actor) { return this.custom.enabled ? this.custom.formula - : `${actor.system[this.multiplier] ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; + : `${actor.system[this.multiplier].value ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; } } export class DHDamageField extends fields.SchemaField { - constructor(hasBase, options, context = {}) { + constructor(options, context = {}) { const damageFields = { - parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)) + parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)), + includeBase: new fields.BooleanField({ initial: false }) }; - if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true }); + // if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true }); super(damageFields, options, context); } } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 7391a075..b8982cf5 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -494,8 +494,8 @@ export default class DhpActor extends Actor { : damage >= this.system.damageThresholds.minor ? 1 : 0; - - const update = { + await this.modifyResource(hpDamage, type); + /* const update = { 'system.resources.hitPoints.value': Math.min( this.system.resources.hitPoints.value + hpDamage, this.system.resources.hitPoints.max @@ -513,10 +513,39 @@ export default class DhpActor extends Actor { update: update } }); + } */ + } + + 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 takeHealing(healing, type) { + /* async takeHealing(healing, type) { let update = {}; switch (type) { case SYSTEM.GENERAL.healingTypes.health.id: @@ -549,7 +578,7 @@ export default class DhpActor extends Actor { } }); } - } + } */ //Move to action-scope? /* async useAction(action) { diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 1b575aa8..3339b92a 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -107,7 +107,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(healing, event.currentTarget.dataset.type); + await target.actor.modifyResource(healing, event.currentTarget.dataset.type); } }; diff --git a/templates/chat/damage-roll.hbs b/templates/chat/damage-roll.hbs index b57a8f4e..8bd65834 100644 --- a/templates/chat/damage-roll.hbs +++ b/templates/chat/damage-roll.hbs @@ -11,7 +11,7 @@
{{rolls.length}}{{type}} - {{this.total}} + {{total}}
    {{#each rolls}} @@ -23,7 +23,7 @@ -
    {{damage.total}}
    +
    {{total}}
    diff --git a/templates/chat/healing-roll.hbs b/templates/chat/healing-roll.hbs index 60d20fe6..34a5c58e 100644 --- a/templates/chat/healing-roll.hbs +++ b/templates/chat/healing-roll.hbs @@ -1,19 +1,19 @@
    -
    {{this.roll}}
    +
    {{roll}}
      {{#each dice}} -
    1. {{this.value}}
    2. +
    3. {{value}}
    4. {{/each}} {{#each modifiers}}
    5. {{this}}
    6. {{/each}}
    -
    {{this.total}}
    +
    {{total}}
    - +
    \ No newline at end of file