From 3b1f61aa0ae0b562a3722caf6961cb93b9d96047 Mon Sep 17 00:00:00 2001 From: ThaumRystra <9525416+ThaumRystra@users.noreply.github.com> Date: Sat, 11 Nov 2023 13:31:15 +0200 Subject: [PATCH] Added and fixed some tests for tag targeted effects --- .../parseCalculationFields.js | 2 +- .../computeByType/computeCalculation.js | 14 ++- .../computeVariable/getAggregatorResult.js | 10 +- .../tests/computeCalculations.testFn.js | 114 ++++++++++++++++++ .../computeComputation/tests/index.js | 4 + .../client/ui/properties/forms/EffectForm.vue | 8 -- 6 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js diff --git a/app/imports/api/engine/computation/buildComputation/parseCalculationFields.js b/app/imports/api/engine/computation/buildComputation/parseCalculationFields.js index 6ef2b165..1c8cb208 100644 --- a/app/imports/api/engine/computation/buildComputation/parseCalculationFields.js +++ b/app/imports/api/engine/computation/buildComputation/parseCalculationFields.js @@ -55,7 +55,7 @@ function parseAllCalculationFields(prop, schemas) { // For each computed key in the schema schemas[prop.type]?.computedFields?.()?.forEach(calcKey => { // Determine the level the calculation should compute down to - let parseLevel = schemas[prop.type].getDefinition(calcKey).parseLevel || 'reduce'; + let parseLevel = schemas[prop.type].getDefinition(calcKey).parseLevel || 'compile'; // Special case of effects, when targeting by tags compile if (prop.type === 'effect' && prop.targetByTags) parseLevel = 'compile'; diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js index c205cbca..efe9a7ed 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js @@ -9,16 +9,18 @@ export default function computeCalculation(computation, node) { if (!calcObj) return; // resolve the parse node into the initial value resolveCalculationNode(calcObj, calcObj.parseNode, computation.scope); - // Store the unaffected value - if (calcObj.effectIds || calcObj.proficiencyIds) { - calcObj.unaffected = toPrimitiveOrString(calcObj.valueNode); - } + // link and aggregate the effects and proficiencies linkCalculationEffects(node, computation); aggregateCalculationEffects(calcObj, id => computation.propsById[id]); linkCalculationProficiencies(node, computation) aggregateCalculationProficiencies(calcObj, id => computation.propsById[id], computation.scope['proficiencyBonus']?.value || 0); + // Store the unaffected value + if (calcObj.effectIds || calcObj.proficiencyIds) { + calcObj.unaffected = toPrimitiveOrString(calcObj.valueNode); + } + // Resolve the valueNode after effects and proficiencies have been applied to it resolveCalculationNode(calcObj, calcObj.valueNode, computation.scope); @@ -145,14 +147,14 @@ export function aggregateCalculationEffects(calcObj, getEffectFromId) { if (aggregator.min) { calcObj.valueNode = call.create({ functionName: 'max', - args: [calcObj.valueNode, aggregator.min] + args: [calcObj.valueNode, ...aggregator.min] }); } // Max if (aggregator.max) { calcObj.valueNode = call.create({ functionName: 'min', - args: [calcObj.valueNode, aggregator.max] + args: [calcObj.valueNode, ...aggregator.max] }); } } diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/getAggregatorResult.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/getAggregatorResult.js index 3a9f7316..9e8b7ebb 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/getAggregatorResult.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/getAggregatorResult.js @@ -1,6 +1,6 @@ import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js'; -export default function getAggregatorResult(node){ +export default function getAggregatorResult(node) { // Work out the base value as the greater of the deining stat value // This baseValue comes from aggregating definitions let statBase = node.data.baseValue; @@ -12,9 +12,9 @@ export default function getAggregatorResult(node){ if (!aggregator) return statBase; let base; - if (!Number.isFinite(aggregator.base)){ + if (!Number.isFinite(aggregator.base)) { base = statBase || 0; - } else if (!Number.isFinite(statBase)){ + } else if (!Number.isFinite(statBase)) { base = aggregator.base || 0; } else { base = Math.max(aggregator.base, statBase); @@ -29,9 +29,9 @@ export default function getAggregatorResult(node){ if (aggregator.set !== undefined) { result = aggregator.set; } - if (!node.data.definingProp?.decimal && Number.isFinite(result)){ + if (!node.data.definingProp?.decimal && Number.isFinite(result)) { result = Math.floor(result); - } else if (Number.isFinite(result)){ + } else if (Number.isFinite(result)) { result = stripFloatingPointOddities(result); } diff --git a/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js b/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js new file mode 100644 index 00000000..f7471b23 --- /dev/null +++ b/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js @@ -0,0 +1,114 @@ +import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js'; +import { assert } from 'chai'; +import computeCreatureComputation from '../../computeCreatureComputation.js'; +import clean from '../../utility/cleanProp.testFn.js'; + +export default function () { + const computation = buildComputationFromProps(testProperties); + computeCreatureComputation(computation); + const prop = id => computation.propsById[id]; + // Tags target effects on attributes + assert.equal(prop('taggedCon').value, 26, 'Tagged targeted effects affect attribute values'); + assert.equal(prop('taggedCon').baseValue.value, 10, 'Tag targeted effects target the attribute itself, not the base value'); + // Tag target effects on a calculation + assert.equal(prop('attackAction').attackRoll.value, 20, 'Tag targeted effects change the attack roll correctly'); + // Tag target effects can deal with rolls + assert.equal(prop('attackAction').attackRoll.value, 20, 'Tag targeted effects change the attack roll correctly'); + assert.equal(prop('attackAction2').attackRoll.value, 'min(3 + d4, d100)', 'Tag targeted effects change the attack roll correctly'); +} + +var testProperties = [ + // Constitution plus some effects that target it by tag + clean({ + _id: 'taggedCon', + variableName: 'constitution', + type: 'attribute', + attributeType: 'ability', + baseValue: { + calculation: '10' + }, + tags: ['tag'] + }), + clean({ + _id: 'add3ToCon', + type: 'effect', + operation: 'add', + amount: { + calculation: '3' + }, + targetByTags: true, + targetTags: ['tag'], + }), + clean({ + _id: 'mulConBy2', + type: 'effect', + operation: 'mul', + amount: { + calculation: '2' + }, + targetByTags: true, + targetTags: ['tag'], + }), + + // Attack action plus some effects that target it by tag + clean({ + _id: 'attackAction', + type: 'action', + ancestors: [{ id: 'charId' }], + attackRoll: { + calculation: '3' + }, + tags: ['attackTag'] + }), + clean({ + _id: 'add1ToAttack', + type: 'effect', + operation: 'add', + amount: { + calculation: '1' + }, + targetByTags: true, + targetTags: ['attackTag'], + }), + clean({ + _id: 'mulAttackBy5', + type: 'effect', + operation: 'mul', + amount: { + calculation: '5' + }, + targetByTags: true, + targetTags: ['attackTag'], + }), + + // Attack action plus some effects that target it by tag but have rolled values + clean({ + _id: 'attackAction2', + type: 'action', + ancestors: [{ id: 'charId' }], + attackRoll: { + calculation: '3' + }, + tags: ['attackTag2'] + }), + clean({ + _id: 'addD4ToAttackRoll', + type: 'effect', + operation: 'add', + amount: { + calculation: 'd4' + }, + targetByTags: true, + targetTags: ['attackTag2'], + }), + clean({ + _id: 'MaxAttackByD100', + type: 'effect', + operation: 'max', + amount: { + calculation: 'd100' + }, + targetByTags: true, + targetTags: ['attackTag2'], + }), +]; diff --git a/app/imports/api/engine/computation/computeComputation/tests/index.js b/app/imports/api/engine/computation/computeComputation/tests/index.js index 768fee2d..b97bb486 100644 --- a/app/imports/api/engine/computation/computeComputation/tests/index.js +++ b/app/imports/api/engine/computation/computeComputation/tests/index.js @@ -1,5 +1,6 @@ import computeAction from './computeAction.testFn.js'; import computeAttribute from './computeAttribute.testFn.js'; +import computeCalculations from './computeCalculations.testFn.js'; import computeClasses from './computeClasses.testFn.js'; import computeConstants from './computeConstants.testFn.js'; import computeInventory from './computeInventory.testFn.js'; @@ -14,6 +15,9 @@ export default [{ }, { text: 'Computes attributes', fn: computeAttribute, +}, { + text: 'Computes calculations', + fn: computeCalculations, }, { text: 'Computes classes', fn: computeClasses, diff --git a/app/imports/client/ui/properties/forms/EffectForm.vue b/app/imports/client/ui/properties/forms/EffectForm.vue index fbf57736..08897394 100644 --- a/app/imports/client/ui/properties/forms/EffectForm.vue +++ b/app/imports/client/ui/properties/forms/EffectForm.vue @@ -8,7 +8,6 @@