Files
DiceCloud/app/imports/api/engine/action/applyProperties/applyActionProperty.test.ts
2024-02-27 15:30:40 +02:00

339 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { assert } from 'chai';
import {
allLogContent,
allMutations,
allUpdates,
createTestCreature,
randomIds,
removeAllCreaturesAndProps,
runActionById
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
import { LogContent, Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
import Alea from 'alea';
const [
creatureId, targetCreatureId, targetCreature2Id, emptyActionId, selfActionId, attackActionId,
usesActionId, attackMissId, attackNoTargetId, usesResourcesActionId, ammoId, resourceAttId, consumeAmmoId,
consumeResourceId, noUsesActionId, insufficientResourcesActionId
] = randomIds;
const actionTestCreature = {
_id: creatureId,
props: [
// Empty
{
_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
{
_id: attackActionId,
type: 'action',
attackRoll: { calculation: '10' },
},
// Attack that misses
{
_id: attackMissId,
type: 'action',
attackRoll: { calculation: '-5' },
},
// Attack that has no target
{
_id: attackNoTargetId,
type: 'action',
attackRoll: { calculation: '1' },
},
// Disable crits
{
type: 'attribute',
attributeType: 'stat',
variableName: '~criticalHitTarget',
baseValue: { calculation: '21' },
},
{
type: 'attribute',
attributeType: 'stat',
variableName: '~criticalMissTarget',
baseValue: { calculation: '0' },
},
// Has uses
{
_id: usesActionId,
type: 'action',
uses: { calculation: '3' },
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' },
}]
}
}
],
}
const actionTargetCreature = {
_id: targetCreatureId,
props: [
{
type: 'attribute',
attributeType: 'stat',
variableName: 'armor',
baseValue: { calculation: '10' },
}
]
}
const actionTargetCreature2 = {
_id: targetCreature2Id,
props: [
{
type: 'attribute',
attributeType: 'stat',
variableName: 'armor',
baseValue: { calculation: '10' },
}
]
}
describe('Apply Action Properties', function () {
// Increase timeout
this.timeout(8000);
before(async function () {
await removeAllCreaturesAndProps();
await createTestCreature(actionTestCreature);
await createTestCreature(actionTargetCreature);
await createTestCreature(actionTargetCreature2);
});
it('should generate random numbers reliably given consistent seeds', function () {
const aleaFraction = Alea('test', 'seeds');
const randomNumbers = [aleaFraction(), aleaFraction(), aleaFraction()];
assert.deepEqual(randomNumbers, [
0.19889510236680508, 0.9176857066340744, 0.042551583144813776
]);
});
it('should run empty actions', async function () {
const action = await runActionById(emptyActionId);
assert.exists(action);
assert.deepEqual(allMutations(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,
targetCreature2Id,
]);
const expectedMutations: Mutation[] = [
{
contents: [{ name: 'Action' }],
targetIds: [targetCreatureId, targetCreature2Id]
}, {
contents: [{
inline: true,
name: 'Hit!',
value: '1d20 [10] + 10\n**20**',
}],
targetIds: [targetCreatureId],
}, {
contents: [{
inline: true,
name: 'Hit!',
value: '1d20 [10] + 10\n**20**',
}],
targetIds: [targetCreature2Id],
},
];
assert.deepEqual(allMutations(action), expectedMutations);
});
it('should make attack rolls that use uses', async function () {
const action = await runActionById(usesActionId, [targetCreatureId]);
const expectedUpdates: Update[] = [
{
propId: usesActionId,
type: 'action',
inc: { usesUsed: 1, usesLeft: -1 },
}
]
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[] = [
{
contents: [{ name: 'Action' }],
targetIds: [targetCreatureId],
}, {
contents: [{
inline: true,
name: 'Miss!',
value: '1d20 [10] 5\n**5**', // DiceCloud uses unicode minus
}],
targetIds: [targetCreatureId],
}
];
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);
});
});