Fixed bug where tag-targeting a calculation's base value would crash the sheet
This commit is contained in:
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal 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",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -119,7 +119,7 @@ export function aggregateCalculationEffects(calcObj, getEffectFromId) {
|
|||||||
if (aggregator.base) {
|
if (aggregator.base) {
|
||||||
calcObj.valueNode = call.create({
|
calcObj.valueNode = call.create({
|
||||||
functionName: 'max',
|
functionName: 'max',
|
||||||
args: [calcObj.valueNode, aggregator.base]
|
args: [calcObj.valueNode, ...aggregator.base]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Add
|
// Add
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import { buildComputationFromProps } from '/imports/api/engine/computation/build
|
|||||||
import propsFromForest from '/imports/api/engine/computation/utility/propsFromForest.testFn';
|
import propsFromForest from '/imports/api/engine/computation/utility/propsFromForest.testFn';
|
||||||
import { cleanAndValidate } from '/imports/api/utility/TypedSimpleSchema';
|
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(), {
|
const creature = cleanAndValidate(Creatures.simpleSchema(), {
|
||||||
_id: testCreature._id,
|
_id: testCreature._id || Random.id(),
|
||||||
name: testCreature.name || 'Test Creature',
|
name: testCreature.name || 'Test Creature',
|
||||||
dirty: true,
|
dirty: true,
|
||||||
owner: Random.id(),
|
owner: Random.id(),
|
||||||
readers: [],
|
readers: [],
|
||||||
writers: [],
|
writers: [],
|
||||||
});
|
});
|
||||||
const props = propsFromForest(testCreature.props, creature._id);
|
const props = propsFromForest(testCreature.props || [], creature._id);
|
||||||
return buildComputationFromProps(props, creature, {});
|
return buildComputationFromProps(props, creature, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -19,13 +19,13 @@ export default async function resolve(
|
|||||||
): Promise<ResolvedResult> {
|
): Promise<ResolvedResult> {
|
||||||
if (!node) throw new Error('Node must be supplied');
|
if (!node) throw new Error('Node must be supplied');
|
||||||
const factory = factories[node.parseType];
|
const factory = factories[node.parseType];
|
||||||
const handlerFunction: ResolveLevelFunction<ParseNode> = factory[fn];
|
|
||||||
if (!factory) {
|
if (!factory) {
|
||||||
throw new Meteor.Error(`Parse node type: ${node.parseType} not implemented`);
|
throw new Meteor.Error(`Parse node type: ${node.parseType} not implemented`);
|
||||||
}
|
}
|
||||||
|
const handlerFunction = getHandlerFunction(fn, factory);
|
||||||
if ('resolve' in factory) {
|
if ('resolve' in factory) {
|
||||||
return factory.resolve(fn, node as any, scope, context, inputProvider, resolve);
|
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);
|
return handlerFunction(node, scope, context, inputProvider, resolve);
|
||||||
} else if (fn === 'reduce' && 'roll' in factory) {
|
} else if (fn === 'reduce' && 'roll' in factory) {
|
||||||
return factory.roll(node as any, scope, context, inputProvider, resolve)
|
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 = {
|
const computationInputProvider: InputProvider = {
|
||||||
/**
|
/**
|
||||||
* By default, just roll the dice as usual
|
* By default, just roll the dice as usual
|
||||||
|
|||||||
Reference in New Issue
Block a user