diff --git a/lang/en.json b/lang/en.json index be797216..2b355ae0 100755 --- a/lang/en.json +++ b/lang/en.json @@ -85,6 +85,13 @@ }, "applyTo": { "label": "Targeted Resource" + }, + "consumeOnSuccess": { + "label": "Consume on Success only", + "short": "(on Success only)" + }, + "cost": { + "stepTooltip": "+{step} per step" } } }, diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 7dce4f06..b8655c91 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -208,7 +208,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return !this.hasRoll; } - async consume(config) { + async consume(config, successCost = false) { const usefulResources = foundry.utils.deepClone(this.actor.system.resources); for (var cost of config.costs) { @@ -220,8 +220,17 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }; } } + const resources = config.costs - .filter(c => c.enabled !== false) + .filter(c => + c.enabled !== false + && + ( + (!successCost && (!c.consumeOnSuccess || config.roll?.success)) + || + (successCost && c.consumeOnSuccess) + ) + ) .map(c => { const resource = usefulResources[c.key]; return { @@ -233,7 +242,17 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }); await this.actor.modifyResource(resources); - if (config.uses?.enabled) this.update({ 'uses.value': this.uses.value + 1 }); + if (config.uses?.enabled + && + ( + (!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) + || + (successCost && config.uses?.consumeOnSuccess) + ) + ) this.update({ 'uses.value': this.uses.value + 1 }); + + if(config.roll?.success || successCost) + (config.message ?? config.parent).update({'system.successConsumed': true}) } /* */ diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index b288b270..d6c8851c 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -42,10 +42,28 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { damage: new fields.ObjectField(), costs: new fields.ArrayField( new fields.ObjectField() - ) + ), + successConsumed: new fields.BooleanField({ initial: false }) }; } + get actionActor() { + if(!this.source.actor) return null; + return fromUuidSync(this.source.actor); + } + + get actionItem() { + const actionActor = this.actionActor; + if(!actionActor || !this.source.item) return null; + return actionActor.items.get(this.source.item); + } + + get action() { + const actionItem = this.actionItem; + if(!actionItem || !this.source.action) return null; + return actionItem.system.actionsList?.find(a => a.id === this.source.action); + } + get messageTemplate() { return 'systems/daggerheart/templates/ui/chat/roll.hbs'; } @@ -74,14 +92,14 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { async updateTargets() { this.currentTargets = this.getTargetList(); - if(!this.targetSelection && this.hasSave) { + if(!this.targetSelection) { this.currentTargets.forEach(ct => { if(this.targets.find(t => t.actorId === ct.actorId)) return; const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId); if(indexTarget === -1) this.oldTargets.push(ct); }); - this.setPendingSaves(); + if(this.hasSave) this.setPendingSaves(); if(this.currentTargets.length) { if(!this.parent._id) return; const updates = await this.parent.update( @@ -91,7 +109,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } } ); - if(!updates) + if(!updates && ui.chat.collection.get(this.parent.id)) ui.chat.updateMessage(this.parent); } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 6d4b084a..b9495e8b 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -9,9 +9,10 @@ export default class CostField extends fields.ArrayField { initial: 'hope' }), keyIsID: new fields.BooleanField(), - value: new fields.NumberField({ nullable: true, initial: 1 }), + value: new fields.NumberField({ nullable: true, initial: 1, min: 0 }), scalable: new fields.BooleanField({ initial: false }), - step: new fields.NumberField({ nullable: true, initial: null }) + step: new fields.NumberField({ nullable: true, initial: null }), + consumeOnSuccess: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label" }) }); super(element, options, context); } @@ -28,9 +29,9 @@ export default class CostField extends fields.ArrayField { static calcCosts(costs) { const resources = CostField.getResources.call(this, costs); return costs.map(c => { - c.scale = c.scale ?? 1; + c.scale = c.scale ?? 0; c.step = c.step ?? 1; - c.total = c.value + (c.scale - 1) * c.step; + c.total = c.value + c.scale * c.step; c.enabled = c.hasOwnProperty('enabled') ? c.enabled : true; c.max = c.key === 'fear' @@ -38,7 +39,7 @@ export default class CostField extends fields.ArrayField { : resources[c.key].isReversed ? resources[c.key].max : resources[c.key].value; - if (c.scalable) c.maxStep = Math.floor(c.max / c.step); + if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step); return c; }); } diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index d06e0009..14ceba61 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -11,7 +11,8 @@ export default class UsesField extends fields.SchemaField { choices: CONFIG.DH.GENERAL.refreshTypes, initial: null, nullable: true - }) + }), + consumeOnSuccess: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label" }) }; super(usesFields, options, context); } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 21d261ff..b30bafbf 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -147,6 +147,7 @@ export default class D20Roll extends DHRoll { const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; target.hit = this.isCritical || roll.total >= difficulty; }); + data.success = config.targets.some(target => target.hit) } else if (config.roll.difficulty) { data.difficulty = config.roll.difficulty; data.success = roll.isCritical || roll.total >= config.roll.difficulty; diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 6acb0372..7340c8a8 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -88,7 +88,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (targets.length === 0) return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); - + for (let target of targets) { let damages = foundry.utils.deepClone(this.system.damage); if ( @@ -106,6 +106,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { }); } + this.consumeOnSuccess(); if (this.system.hasHealing) target.actor.takeHealing(damages); else target.actor.takeDamage(damages); } @@ -132,7 +133,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { const targets = this.getTargetList(); if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); + this.consumeOnSuccess(); await action.applyEffects(event, this, targets); } } + + consumeOnSuccess() { + if(!this.system.successConsumed && !this.system.targetSelection) { + const action = this.system.action; + if(action) action.consume(this.system, true); + } + } } diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 6bda8af6..e96de581 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -381,7 +381,9 @@ } .scalable-input { - display: flex; + display: grid; + grid-template-columns: 1fr 1fr auto; + // display: flex; align-items: center; justify-content: space-between; gap: 10px; @@ -394,6 +396,9 @@ } label { + display: flex; + flex-direction: column; + white-space: nowrap; font-family: @font-body; font-size: 14px; font-weight: 400; @@ -402,6 +407,11 @@ width: 6ch; text-align: end; } + + .hint { + font-size: var(--font-size-10); + font-style: italic; + } } } diff --git a/templates/actionTypes/cost.hbs b/templates/actionTypes/cost.hbs index e956b284..a1b7de48 100644 --- a/templates/actionTypes/cost.hbs +++ b/templates/actionTypes/cost.hbs @@ -4,6 +4,9 @@ {{#each source as |cost index|}} + {{#if @root.hasRoll}} + {{formField ../fields.consumeOnSuccess value=cost.consumeOnSuccess name=(concat "cost." index ".consumeOnSuccess") classes="checkbox" rootId=partId localize=true}} + {{/if}}