More testing of action system, now with test coverage
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { assert } from 'chai';
|
||||
import {
|
||||
allLogContent,
|
||||
allMutations,
|
||||
allUpdates,
|
||||
createTestCreature,
|
||||
@@ -7,12 +8,13 @@ import {
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
import { Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import { LogContent, Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import Alea from 'alea';
|
||||
|
||||
const [
|
||||
creatureId, targetCreatureId, targetCreature2Id,
|
||||
emptyActionId, attackActionId, usesActionId, attackMissId,
|
||||
creatureId, targetCreatureId, targetCreature2Id, emptyActionId, selfActionId, attackActionId,
|
||||
usesActionId, attackMissId, attackNoTargetId, usesResourcesActionId, ammoId, resourceAttId, consumeAmmoId,
|
||||
consumeResourceId, noUsesActionId, insufficientResourcesActionId
|
||||
] = randomIds;
|
||||
|
||||
const actionTestCreature = {
|
||||
@@ -22,6 +24,13 @@ const actionTestCreature = {
|
||||
{
|
||||
_id: emptyActionId,
|
||||
type: 'action',
|
||||
summary: { text: 'Summary text 1 + 1 = {1 + 1}' }
|
||||
},
|
||||
// Attack that targets self
|
||||
{
|
||||
_id: selfActionId,
|
||||
type: 'action',
|
||||
target: 'self',
|
||||
},
|
||||
// Attack that hits
|
||||
{
|
||||
@@ -35,6 +44,12 @@ const actionTestCreature = {
|
||||
type: 'action',
|
||||
attackRoll: { calculation: '-5' },
|
||||
},
|
||||
// Attack that has no target
|
||||
{
|
||||
_id: attackNoTargetId,
|
||||
type: 'action',
|
||||
attackRoll: { calculation: '1' },
|
||||
},
|
||||
// Disable crits
|
||||
{
|
||||
type: 'attribute',
|
||||
@@ -56,6 +71,57 @@ const actionTestCreature = {
|
||||
usesUsed: 1,
|
||||
reset: 'longRest',
|
||||
},
|
||||
// Not enough uses
|
||||
{
|
||||
_id: noUsesActionId,
|
||||
type: 'action',
|
||||
uses: { calculation: '5' },
|
||||
usesUsed: 5,
|
||||
reset: 'longRest',
|
||||
},
|
||||
// Uses Resources
|
||||
{
|
||||
_id: ammoId,
|
||||
type: 'item',
|
||||
quantity: 12,
|
||||
tags: ['ammo']
|
||||
},
|
||||
{
|
||||
_id: resourceAttId,
|
||||
type: 'attribute',
|
||||
name: 'Resource Name',
|
||||
attributeType: 'stat',
|
||||
baseValue: { calculation: '7' },
|
||||
variableName: 'resourceVar',
|
||||
},
|
||||
{
|
||||
_id: usesResourcesActionId,
|
||||
type: 'action',
|
||||
resources: {
|
||||
itemsConsumed: [{
|
||||
_id: consumeAmmoId,
|
||||
tag: 'ammo',
|
||||
quantity: { calculation: '3' },
|
||||
itemId: ammoId,
|
||||
}],
|
||||
attributesConsumed: [{
|
||||
_id: consumeResourceId,
|
||||
variableName: 'resourceVar',
|
||||
quantity: { calculation: '2' },
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
_id: insufficientResourcesActionId,
|
||||
type: 'action',
|
||||
resources: {
|
||||
attributesConsumed: [{
|
||||
_id: consumeResourceId,
|
||||
variableName: 'resourceVar',
|
||||
quantity: { calculation: '9001' },
|
||||
}]
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
@@ -106,11 +172,43 @@ describe('Apply Action Properties', function () {
|
||||
const action = await runActionById(emptyActionId);
|
||||
assert.exists(action);
|
||||
assert.deepEqual(allMutations(action), [{
|
||||
contents: [{ name: 'Action' }],
|
||||
contents: [{
|
||||
name: 'Action',
|
||||
value: 'Summary text 1 + 1 = 2',
|
||||
}],
|
||||
targetIds: [],
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should target self when set', async function () {
|
||||
const action = await runActionById(selfActionId);
|
||||
assert.exists(action);
|
||||
assert.deepEqual(allMutations(action), [{
|
||||
contents: [{
|
||||
name: 'Action',
|
||||
}],
|
||||
targetIds: [creatureId],
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should make attack rolls against no targets', async function () {
|
||||
const action = await runActionById(attackNoTargetId, []);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
contents: [{ name: 'Action' }],
|
||||
targetIds: [],
|
||||
}, {
|
||||
contents: [{
|
||||
name: 'To Hit',
|
||||
value: '1d20 [10] + 1\n**11**',
|
||||
inline: true,
|
||||
}],
|
||||
targetIds: [],
|
||||
}
|
||||
];
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
})
|
||||
|
||||
it('should make attack rolls against multiple creatures', async function () {
|
||||
const action = await runActionById(attackActionId, [
|
||||
targetCreatureId,
|
||||
@@ -151,6 +249,19 @@ describe('Apply Action Properties', function () {
|
||||
assert.deepEqual(allUpdates(action), expectedUpdates);
|
||||
});
|
||||
|
||||
it('should fail to make attacks that have no uses left', async function () {
|
||||
const action = await runActionById(noUsesActionId, [targetCreatureId]);
|
||||
const expectedContent: LogContent[] = [
|
||||
{
|
||||
name: 'Action'
|
||||
}, {
|
||||
name: 'Error',
|
||||
value: 'Action does not have enough uses left'
|
||||
}
|
||||
]
|
||||
assert.deepEqual(allLogContent(action), expectedContent);
|
||||
});
|
||||
|
||||
it('should make attack rolls that miss', async function () {
|
||||
const action = await runActionById(attackMissId, [targetCreatureId]);
|
||||
const expectedMutations: Mutation[] = [
|
||||
@@ -169,4 +280,59 @@ describe('Apply Action Properties', function () {
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
it('actions should consume resources', async function () {
|
||||
const action = await runActionById(usesResourcesActionId, []);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
contents: [{ name: 'Action' }],
|
||||
targetIds: []
|
||||
},
|
||||
{
|
||||
contents: [{
|
||||
inline: true,
|
||||
name: 'Attribute damaged',
|
||||
value: '−2 Resource Name',
|
||||
}],
|
||||
targetIds: [creatureId],
|
||||
updates: [{
|
||||
inc: {
|
||||
damage: 2,
|
||||
value: -2
|
||||
},
|
||||
propId: resourceAttId,
|
||||
type: 'attribute'
|
||||
}],
|
||||
},
|
||||
{
|
||||
targetIds: [],
|
||||
updates: [
|
||||
{
|
||||
inc: {
|
||||
quantity: -3
|
||||
},
|
||||
propId: ammoId,
|
||||
type: 'item',
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
it('should handle insufficient resources', async function () {
|
||||
const action = await runActionById(insufficientResourcesActionId, []);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
contents: [{
|
||||
name: 'Action'
|
||||
}, {
|
||||
name: 'Error',
|
||||
value: 'This creature doesn\'t have sufficient resources to perform this action',
|
||||
}],
|
||||
targetIds: [],
|
||||
},
|
||||
];
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -21,33 +21,36 @@ export default async function applyActionProperty(
|
||||
const targetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
|
||||
//Log the name and summary, check that the property has enough resources to fire
|
||||
const content: LogContent = { name: getPropertyTitle(prop) };
|
||||
if (prop.summary?.text) {
|
||||
await recalculateInlineCalculations(prop.summary, action, 'reduce', userInput);
|
||||
content.value = prop.summary.value;
|
||||
}
|
||||
if (prop.silent) content.silenced = true;
|
||||
result.appendLog(content, targetIds);
|
||||
result.appendLog({
|
||||
name: getPropertyTitle(prop),
|
||||
...prop.summary && { value: prop.summary.value },
|
||||
...prop.silent && { silenced: true },
|
||||
}, targetIds);
|
||||
|
||||
// Check Uses
|
||||
if (prop.usesLeft <= 0) {
|
||||
if (!prop.silent) result.appendLog({
|
||||
result.appendLog({
|
||||
name: 'Error',
|
||||
value: `${prop.name || 'action'} does not have enough uses left`,
|
||||
value: `${getPropertyTitle(prop)} does not have enough uses left`,
|
||||
...prop.silent && { silenced: true },
|
||||
}, targetIds);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check Resources
|
||||
if (prop.insufficientResources) {
|
||||
if (!prop.silent) result.appendLog({
|
||||
result.appendLog({
|
||||
name: 'Error',
|
||||
value: 'This creature doesn\'t have sufficient resources to perform this action',
|
||||
...prop.silent && { silenced: true },
|
||||
}, targetIds);
|
||||
return;
|
||||
}
|
||||
|
||||
spendResources(action, prop, targetIds, result, userInput);
|
||||
await spendResources(action, prop, targetIds, result, userInput);
|
||||
|
||||
const attack: CalculatedField = prop.attackRoll || prop.attackRollBonus;
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ import { applyTriggers } from '/imports/api/engine/action/functions/applyTaskGro
|
||||
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
|
||||
import getPropertyTitle from '/imports/api/utility/getPropertyTitle';
|
||||
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
|
||||
export default async function applyDamagePropTask(
|
||||
task: DamagePropTask, action: EngineAction, result: TaskResult, userInput
|
||||
): Promise<number> {
|
||||
|
||||
const prop = task.prop;
|
||||
|
||||
if (task.targetIds.length > 1) {
|
||||
@@ -22,7 +24,7 @@ export default async function applyDamagePropTask(
|
||||
|
||||
// Set the scope properties
|
||||
result.pushScope = {};
|
||||
if (prop.operation === 'increment') {
|
||||
if (operation === 'increment') {
|
||||
if (value >= 0) {
|
||||
result.pushScope['~damage'] = { value };
|
||||
} else {
|
||||
@@ -67,7 +69,7 @@ export default async function applyDamagePropTask(
|
||||
value: `${statName}${operation === 'set' ? ' set to' : ''}` +
|
||||
` ${value}`,
|
||||
inline: true,
|
||||
silenced: prop.silent,
|
||||
...prop.silent && { silenced: true },
|
||||
}, task.targetIds);
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ export default async function applyDamagePropTask(
|
||||
name: title,
|
||||
value: `${getPropertyTitle(targetProp)} set to ${value}`,
|
||||
inline: true,
|
||||
silenced: prop.silent,
|
||||
...prop.silent && { silenced: true },
|
||||
}]
|
||||
});
|
||||
} else if (operation === 'increment') {
|
||||
@@ -120,10 +122,10 @@ export default async function applyDamagePropTask(
|
||||
type: targetProp.type,
|
||||
}],
|
||||
contents: [{
|
||||
name: 'Attribute damage',
|
||||
value: `${getPropertyTitle(targetProp)} ${value}`,
|
||||
name: 'Attribute damaged',
|
||||
value: `${numberToSignedString(-value)} ${getPropertyTitle(targetProp)}`,
|
||||
inline: true,
|
||||
silenced: prop.silent,
|
||||
...prop.silent && { silenced: true },
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,11 +43,13 @@ export default async function applyItemAsAmmoTask(task: ItemAsAmmoTask, action:
|
||||
type: 'item',
|
||||
}],
|
||||
// Log the item name as a heading if it has child properties to apply
|
||||
contents: itemChildren.length ? [{
|
||||
name: getPropertyTitle(item) || 'Ammo',
|
||||
inline: false,
|
||||
silenced: prop.silent,
|
||||
}] : undefined,
|
||||
...itemChildren.length && {
|
||||
contents: [{
|
||||
name: getPropertyTitle(item) || 'Ammo',
|
||||
inline: false,
|
||||
silenced: prop.silent,
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
||||
await applyTriggers(action, item, [action.creatureId], 'ammo.after', userInput);
|
||||
|
||||
@@ -53,7 +53,7 @@ export default async function applyTask(
|
||||
|
||||
// Create a result an push it to the action results, pass it to the apply function to modify
|
||||
const result = new TaskResult(task.prop._id, task.targetIds);
|
||||
result.scope[`#${prop.type}`] = prop;
|
||||
result.scope[`#${prop.type}`] = { _propId: prop._id };
|
||||
action.results.push(result);
|
||||
|
||||
// Apply the property
|
||||
|
||||
Reference in New Issue
Block a user