More action test cases
This commit is contained in:
@@ -10,11 +10,13 @@ import {
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
import { LogContent, Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import Alea from 'alea';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
const [
|
||||
creatureId, targetCreatureId, targetCreature2Id, emptyActionId, selfActionId, attackActionId,
|
||||
usesActionId, attackMissId, attackNoTargetId, usesResourcesActionId, ammoId, resourceAttId, consumeAmmoId,
|
||||
consumeResourceId, noUsesActionId, insufficientResourcesActionId
|
||||
usesActionId, attackMissId, attackNoTargetId, usesResourcesActionId, ammoId, resourceAttId,
|
||||
consumeAmmoId, consumeResourceId, noUsesActionId, insufficientResourcesActionId,
|
||||
attributeResetByEventId, eventActionId, advantageAttackId, advantageEffectId
|
||||
] = randomIds;
|
||||
|
||||
const actionTestCreature = {
|
||||
@@ -44,6 +46,20 @@ const actionTestCreature = {
|
||||
type: 'action',
|
||||
attackRoll: { calculation: '-5' },
|
||||
},
|
||||
// Attack that has Advantage
|
||||
{
|
||||
_id: advantageAttackId,
|
||||
type: 'action',
|
||||
attackRoll: { calculation: '0' },
|
||||
tags: ['hasAdvantage'],
|
||||
},
|
||||
{
|
||||
_id: advantageEffectId,
|
||||
type: 'effect',
|
||||
operation: 'advantage',
|
||||
targetByTags: true,
|
||||
targetTags: ['hasAdvantage'],
|
||||
},
|
||||
// Attack that has no target
|
||||
{
|
||||
_id: attackNoTargetId,
|
||||
@@ -121,7 +137,24 @@ const actionTestCreature = {
|
||||
quantity: { calculation: '9001' },
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
// Events and resetting attributes
|
||||
{
|
||||
_id: attributeResetByEventId,
|
||||
type: 'attribute',
|
||||
name: 'Attribute Reset By testEvent Event',
|
||||
attributeType: 'stat',
|
||||
baseValue: { calculation: '27' },
|
||||
damage: 13,
|
||||
variableName: 'resetByEventAtt',
|
||||
reset: 'testEvent'
|
||||
},
|
||||
{
|
||||
_id: eventActionId,
|
||||
type: 'action',
|
||||
actionType: 'event',
|
||||
variableName: 'testEvent',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -280,6 +313,26 @@ describe('Apply Action Properties', function () {
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
it('should make attack rolls that roll with advantage', async function () {
|
||||
const prop = await CreatureProperties.findOneAsync(advantageAttackId);
|
||||
assert.equal(prop.attackRoll.advantage, 1, 'The attack roll should have advantage');
|
||||
const action = await runActionById(advantageAttackId, [targetCreatureId]);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
contents: [{ name: 'Action' }],
|
||||
targetIds: [targetCreatureId],
|
||||
}, {
|
||||
contents: [{
|
||||
inline: true,
|
||||
name: 'Hit! (Advantage)',
|
||||
value: '1d20 [ ~~10~~, 11 ] + 0\n**11**',
|
||||
}],
|
||||
targetIds: [targetCreatureId],
|
||||
}
|
||||
];
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
it('actions should consume resources', async function () {
|
||||
const action = await runActionById(usesResourcesActionId, []);
|
||||
const expectedMutations: Mutation[] = [
|
||||
@@ -335,4 +388,37 @@ describe('Apply Action Properties', function () {
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
it('should reset attributes when events happen', async function () {
|
||||
const action = await runActionById(eventActionId, []);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
contents: [{
|
||||
name: 'Action'
|
||||
}],
|
||||
targetIds: [],
|
||||
},
|
||||
{
|
||||
contents: [
|
||||
{
|
||||
inline: true,
|
||||
name: 'Attribute damaged',
|
||||
value: '+13 Attribute Reset By testEvent Event',
|
||||
},
|
||||
],
|
||||
targetIds: [creatureId],
|
||||
updates: [
|
||||
{
|
||||
inc: {
|
||||
damage: -13,
|
||||
value: 13,
|
||||
},
|
||||
propId: attributeResetByEventId,
|
||||
type: 'attribute',
|
||||
},
|
||||
],
|
||||
}
|
||||
];
|
||||
assert.deepEqual(allMutations(action), expectedMutations);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { applyAfterChildrenTriggers, applyAfterTriggers, applyChildren } from '/
|
||||
import recalculateCalculation from '/imports/api/engine/action/functions/recalculateCalculation';
|
||||
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { getNumberFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import { getConstantValueFromScope, getNumberFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import { CalculatedField } from '/imports/api/properties/subSchemas/computedField';
|
||||
|
||||
@@ -100,6 +100,7 @@ async function applyAttackToTarget(
|
||||
result,
|
||||
criticalHit,
|
||||
criticalMiss,
|
||||
advantage
|
||||
} = await rollAttack(attack, scope, taskResult.pushScope, userInput);
|
||||
|
||||
const targetScope = getVariables(targetId);
|
||||
@@ -109,9 +110,9 @@ async function applyAttackToTarget(
|
||||
let name = criticalHit ? 'Critical Hit!' :
|
||||
criticalMiss ? 'Critical Miss!' :
|
||||
result > targetArmor ? 'Hit!' : 'Miss!';
|
||||
if (scope['~attackAdvantage']?.value === 1) {
|
||||
if (advantage === 1) {
|
||||
name += ' (Advantage)';
|
||||
} else if (scope['~attackAdvantage']?.value === -1) {
|
||||
} else if (advantage === -1) {
|
||||
name += ' (Disadvantage)';
|
||||
}
|
||||
|
||||
@@ -187,10 +188,16 @@ async function applyAttackWithoutTarget(action, prop, attack, taskResult: TaskRe
|
||||
});
|
||||
}
|
||||
|
||||
async function rollAttack(attack, scope, resultPushScope, userInput: InputProvider) {
|
||||
async function rollAttack(attack, scope: any, resultPushScope, userInput: InputProvider) {
|
||||
const advantage: 0 | 1 | -1 = await userInput.advantage(
|
||||
(!!attack.advantage && !attack.disadvantage) ? 1 :
|
||||
(!attack.advantage && !!attack.disadvantage) ? -1 :
|
||||
0
|
||||
);
|
||||
const rollModifierText = numberToSignedString(attack.value, true);
|
||||
let value, resultPrefix;
|
||||
if (scope['~attackAdvantage']?.value === 1) {
|
||||
|
||||
if (advantage === 1) {
|
||||
const [[a, b]] = await userInput.rollDice([{ number: 2, diceSize: 20 }]);
|
||||
if (a >= b) {
|
||||
value = a;
|
||||
@@ -199,7 +206,7 @@ async function rollAttack(attack, scope, resultPushScope, userInput: InputProvid
|
||||
value = b;
|
||||
resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
|
||||
}
|
||||
} else if (scope['~attackAdvantage']?.value === -1) {
|
||||
} else if (advantage === -1) {
|
||||
const [[a, b]] = await userInput.rollDice([{ number: 2, diceSize: 20 }]);
|
||||
if (a <= b) {
|
||||
value = a;
|
||||
@@ -216,7 +223,7 @@ async function rollAttack(attack, scope, resultPushScope, userInput: InputProvid
|
||||
const result = value + attack.value;
|
||||
resultPushScope['~attackRoll'] = { value: result };
|
||||
const { criticalHit, criticalMiss } = applyCrits(value, scope, resultPushScope);
|
||||
return { resultPrefix, result, value, criticalHit, criticalMiss };
|
||||
return { resultPrefix, result, value, criticalHit, criticalMiss, advantage };
|
||||
}
|
||||
|
||||
function applyCrits(value, scope, resultPushScope) {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
type InputProvider = {
|
||||
/**
|
||||
* Roll dice
|
||||
* @param dice How many dice
|
||||
* @param diceSize How many faces per die
|
||||
*/
|
||||
rollDice(
|
||||
dice: { number: number, diceSize: number }[]
|
||||
): Promise<number[][]>;
|
||||
@@ -12,6 +17,10 @@ type InputProvider = {
|
||||
choices: ({ _id: string } & Record<string, any>)[],
|
||||
quantity?: [min: number, max: number],
|
||||
): Promise<string[]>;
|
||||
/**
|
||||
* Get advantage, natural, or disadvantage for a d20 roll
|
||||
*/
|
||||
advantage(suggestedAdvantage: 0 | 1 | -1): Promise<0 | 1 | -1>;
|
||||
}
|
||||
|
||||
export default InputProvider;
|
||||
@@ -10,10 +10,11 @@ const inputProviderForTests: InputProvider = {
|
||||
const result: number[][] = [];
|
||||
for (const diceRoll of dice) {
|
||||
const averageRoll = Math.round(diceRoll.diceSize / 2);
|
||||
// Return an array full of averagely rolled dice
|
||||
// Return an array full of averagely rolled dice, increasing by 1 for every dice
|
||||
result.push(
|
||||
new Array(diceRoll.number)
|
||||
.fill(averageRoll)
|
||||
.map((value, index) => (value + index - 1) % diceRoll.diceSize + 1)
|
||||
)
|
||||
}
|
||||
return result;
|
||||
@@ -28,6 +29,12 @@ const inputProviderForTests: InputProvider = {
|
||||
chosen.push(choices[i]._id);
|
||||
}
|
||||
return chosen;
|
||||
},
|
||||
/**
|
||||
* For testing, always return the suggested advantage, as if the user never chose differently
|
||||
*/
|
||||
async advantage(suggestedAdvantage) {
|
||||
return suggestedAdvantage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user