From 3801b17fde385ec3be8db0831f3f8f476c766ebc Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 22 Feb 2021 14:07:12 +0200 Subject: [PATCH] Attacks can now critical hit. `criticalHitTarget` overrides the roll required --- .../api/creature/actions/applyAttack.js | 8 +++- .../api/creature/actions/applyDamage.js | 22 ++++++++-- .../api/creature/actions/applyProperties.js | 43 +++++++++---------- .../afterComputation/evaluateString.js | 6 ++- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/app/imports/api/creature/actions/applyAttack.js b/app/imports/api/creature/actions/applyAttack.js index 977ccc67..18f4933e 100644 --- a/app/imports/api/creature/actions/applyAttack.js +++ b/app/imports/api/creature/actions/applyAttack.js @@ -4,13 +4,19 @@ export default function applyAttack({ prop, log, actionContext, + creature, }){ let value = roll(1, 20)[0]; actionContext.attackRoll = {value}; + let criticalHitTarget = creature.variables.criticalHitTarget && + creature.variables.criticalHitTarget.currentValue || 20; + let criticalHit = value >= criticalHitTarget; + if (criticalHit) actionContext.criticalHit = {value: true}; let result = value + prop.rollBonusResult; actionContext.toHit = {value: result}; + log.content.push({ - name: 'To Hit', + name: criticalHit ? 'Critical Hit!' : 'To Hit', resultPrefix: `1d20 [${value}] + ${prop.rollBonusResult} = `, result, }); diff --git a/app/imports/api/creature/actions/applyDamage.js b/app/imports/api/creature/actions/applyDamage.js index 713c1a97..02b9ce45 100644 --- a/app/imports/api/creature/actions/applyDamage.js +++ b/app/imports/api/creature/actions/applyDamage.js @@ -1,6 +1,7 @@ import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js'; import dealDamage from '/imports/api/creature/creatureProperties/methods/dealDamage.js'; import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js'; +import { CompilationContext } from '/imports/parser/parser.js'; export default function applyDamage({ prop, @@ -14,8 +15,19 @@ export default function applyDamage({ ...creature.variables, ...actionContext, }; + if (targets.length === 1){ + scope.target = targets[0].variables; + } + let criticalHit = !!( + actionContext.criticalHit && + actionContext.criticalHit.value && + prop.damageType !== 'healing' // Can't critically heal + ); + let context = new CompilationContext({ + doubleRolls: criticalHit, + }); try { - var {result, errors} = evaluateString(prop.amount, scope, 'reduce'); + var {result, errors} = evaluateString(prop.amount, scope, 'reduce', context); if (typeof result !== 'number') { log.content.push({ error: errors.join(', '), @@ -26,11 +38,13 @@ export default function applyDamage({ error: e.toString(), }); } + let suffix = (criticalHit ? ' critical ' : '') + + prop.damageType + + (prop.damageType !== 'healing' ? ' damage': ''); + if (damageTargets && damageTargets.length) { damageTargets.forEach(target => { let name = prop.damageType === 'healing' ? 'Healing' : 'Damage'; - let suffix = prop.damageType + - prop.damageType !== 'healing' ? ' damage': ''; if (prop.target === 'each'){ result = evaluateString(prop.amount, scope, 'reduce'); } @@ -69,7 +83,7 @@ export default function applyDamage({ log.content.push({ name: prop.damageType === 'healing' ? 'Healing' : 'Damage', result, - details: `${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`, + details: suffix, }); } } diff --git a/app/imports/api/creature/actions/applyProperties.js b/app/imports/api/creature/actions/applyProperties.js index b6a34f31..7d657374 100644 --- a/app/imports/api/creature/actions/applyProperties.js +++ b/app/imports/api/creature/actions/applyProperties.js @@ -52,30 +52,29 @@ function applyProperty(options){ return true; } -export default function applyProperties({ - forest, - creature, - targets, - actionContext, - log, -}){ +function applyPropertyAndWalkChildren({prop, child, targets, ...options}){ + let shouldKeepWalking = applyProperty({ prop, targets, ...options }); + if (shouldKeepWalking){ + applyProperties({ forest: child.children, targets, ...options,}); + } +} + +export default function applyProperties({ forest, targets, ...options}){ forest.forEach(child => { - let walkChildren = applyProperty({ - prop: child.node, - children: child.children, - creature, - targets, - actionContext, - log, - }); - if (walkChildren){ - applyProperties({ - forest: child.children, - creature, - targets, - actionContext, - log, + let prop = child.node; + if (shouldSplit(prop)){ + targets.forEach(target => { + let targets = [target] + applyPropertyAndWalkChildren({ targets, prop, child, ...options}); }); + } else { + applyPropertyAndWalkChildren({prop, child, targets, ...options}); } }); } + +function shouldSplit(prop){ + if (prop.target === 'each'){ + return true; + } +} diff --git a/app/imports/api/creature/computation/afterComputation/evaluateString.js b/app/imports/api/creature/computation/afterComputation/evaluateString.js index 0dfe4115..7cd6934a 100644 --- a/app/imports/api/creature/computation/afterComputation/evaluateString.js +++ b/app/imports/api/creature/computation/afterComputation/evaluateString.js @@ -1,7 +1,7 @@ import { parse, CompilationContext } from '/imports/parser/parser.js'; import ConstantNode from '/imports/parser/parseTree/ConstantNode.js'; -export default function evaluateString(string, scope, fn = 'compile'){ +export default function evaluateString(string, scope, fn = 'compile', context){ let errors = []; if (!string){ errors.push('No string provided'); @@ -18,7 +18,9 @@ export default function evaluateString(string, scope, fn = 'compile'){ errors.push(e); return {result: string, errors}; } - let context = new CompilationContext(); + if (!context){ + context = new CompilationContext({}); + } let result = node[fn](scope, context); if (result instanceof ConstantNode){ return {result: result.value, errors: context.errors}