Fixed bug where tag-targeting a calculation's base value would crash the sheet

This commit is contained in:
ThaumRystra
2025-01-16 20:32:30 +02:00
parent 0bf8fdc6d3
commit be47b90c32
5 changed files with 95 additions and 6 deletions

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Meteor: Test",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/app",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"test"
],
"outputCapture": "std",
}
]
}

View File

@@ -119,7 +119,7 @@ export function aggregateCalculationEffects(calcObj, getEffectFromId) {
if (aggregator.base) {
calcObj.valueNode = call.create({
functionName: 'max',
args: [calcObj.valueNode, aggregator.base]
args: [calcObj.valueNode, ...aggregator.base]
});
}
// Add

View File

@@ -4,15 +4,15 @@ import { buildComputationFromProps } from '/imports/api/engine/computation/build
import propsFromForest from '/imports/api/engine/computation/utility/propsFromForest.testFn';
import { cleanAndValidate } from '/imports/api/utility/TypedSimpleSchema';
export default function buildTestComputation(testCreature: TestCreature) {
export default function buildTestComputation(testCreature: Partial<TestCreature>) {
const creature = cleanAndValidate(Creatures.simpleSchema(), {
_id: testCreature._id,
_id: testCreature._id || Random.id(),
name: testCreature.name || 'Test Creature',
dirty: true,
owner: Random.id(),
readers: [],
writers: [],
});
const props = propsFromForest(testCreature.props, creature._id);
const props = propsFromForest(testCreature.props || [], creature._id);
return buildComputationFromProps(props, creature, {});
}

View File

@@ -0,0 +1,59 @@
import { expect } from 'chai';
import buildTestComputation from '/imports/api/engine/computation/computeComputation/tstFns/buildTestComputation'
import computeCreatureComputation from '/imports/api/engine/computation/computeCreatureComputation';
import { CreaturePropertyTypes } from '/imports/api/creature/creatureProperties/CreatureProperties';
describe('Tag targeting', function () {
it('Can target an attribute with a base value', async function () {
const computation = buildTestComputation({
props: [
{
_id: 'attributeToTargetId',
type: 'attribute',
attributeType: 'ability',
variableName: 'strength',
baseValue: { calculation: '12' },
tags: ['tag'],
}, {
_id: 'tagTargetEffectId',
type: 'effect',
operation: 'base',
targetByTags: true,
amount: {
calculation: '20',
},
targetTags: ['tag'],
}
]
});
await computeCreatureComputation(computation);
const prop = computation.propsById['attributeToTargetId'] as CreaturePropertyTypes['attribute'];
expect(prop.value).to.equal(20);
});
it('Can target a calculation with a base value', async function () {
const computation = buildTestComputation({
props: [
{
_id: 'actionToTargetId',
type: 'action',
attackRoll: {
calculation: '12 + 2',
},
tags: ['tag'],
}, {
_id: 'tagTargetEffectId',
type: 'effect',
operation: 'base',
amount: {
calculation: '20',
},
targetByTags: true,
targetTags: ['tag'],
}
]
});
await computeCreatureComputation(computation);
const prop = computation.propsById['actionToTargetId'] as CreaturePropertyTypes['action'];
expect(prop.attackRoll?.value).to.equal(20);
});
});

View File

@@ -19,13 +19,13 @@ export default async function resolve(
): Promise<ResolvedResult> {
if (!node) throw new Error('Node must be supplied');
const factory = factories[node.parseType];
const handlerFunction: ResolveLevelFunction<ParseNode> = factory[fn];
if (!factory) {
throw new Meteor.Error(`Parse node type: ${node.parseType} not implemented`);
}
const handlerFunction = getHandlerFunction(fn, factory);
if ('resolve' in factory) {
return factory.resolve(fn, node as any, scope, context, inputProvider, resolve);
} else if (fn in factory) {
} else if (handlerFunction) {
return handlerFunction(node, scope, context, inputProvider, resolve);
} else if (fn === 'reduce' && 'roll' in factory) {
return factory.roll(node as any, scope, context, inputProvider, resolve)
@@ -36,6 +36,19 @@ export default async function resolve(
}
}
function getHandlerFunction<T extends ParseNode>(
fn: ResolveLevel, factory: typeof factories[T['parseType']]
): ResolveLevelFunction<T> | undefined {
if (!(fn in factory)) return;
if (fn === 'roll' && 'roll' in factory) {
return factory.roll as ResolveLevelFunction<T>;
} else if (fn === 'reduce' && 'reduce' in factory) {
return factory.reduce as ResolveLevelFunction<T>;
} else if (fn === 'compile' && 'compile' in factory) {
return factory.compile as ResolveLevelFunction<T>;
}
}
const computationInputProvider: InputProvider = {
/**
* By default, just roll the dice as usual