Fixed failing tests and action engine props
This commit is contained in:
@@ -3,8 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
|
||||
const damageProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.damage',
|
||||
|
||||
@@ -2,6 +2,9 @@ import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import array from '/imports/parser/parseTree/array';
|
||||
import constant, { isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import Context from '/imports/parser/types/Context';
|
||||
|
||||
//set up the collection for creature variables
|
||||
const CreatureVariables = new Mongo.Collection('creatureVariables');
|
||||
@@ -45,6 +48,15 @@ export function getNumberFromScope(name, scope) {
|
||||
return parseNode.value;
|
||||
}
|
||||
|
||||
export async function getConstantValueFromScope(
|
||||
name, scope
|
||||
) {
|
||||
const parseNode = getParseNodeFromScope(name, scope);
|
||||
if (!parseNode) return;
|
||||
if (parseNode.parseType !== 'constant') return;
|
||||
return parseNode.value;
|
||||
}
|
||||
|
||||
export function getParseNodeFromScope(name, scope): ParseNode | undefined {
|
||||
let value = getFromScope(name, scope);
|
||||
if (!value) return;
|
||||
|
||||
@@ -4,8 +4,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { union } from 'lodash';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
|
||||
@@ -9,14 +9,6 @@ import {
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
import { Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import Alea from 'alea';
|
||||
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
process.on('unhandledRejection', (error, p) => {
|
||||
console.dir(error.stack);
|
||||
console.error('Unhandled Rejection at:', p, 'reason:', error)
|
||||
process.exit(1)
|
||||
});
|
||||
|
||||
const [
|
||||
creatureId, targetCreatureId, targetCreature2Id,
|
||||
|
||||
@@ -102,7 +102,7 @@ async function applyAttackToTarget(
|
||||
const targetScope = getVariables(targetId);
|
||||
const targetArmor = getNumberFromScope('armor', targetScope)
|
||||
|
||||
if (Number.isFinite(targetArmor)) {
|
||||
if (targetArmor !== undefined) {
|
||||
let name = criticalHit ? 'Critical Hit!' :
|
||||
criticalMiss ? 'Critical Miss!' :
|
||||
result > targetArmor ? 'Hit!' : 'Miss!';
|
||||
|
||||
@@ -57,7 +57,7 @@ export default async function applyBuffProperty(
|
||||
//Log the buff
|
||||
let logValue = prop.description?.value
|
||||
if (prop.description?.text) {
|
||||
recalculateInlineCalculations(prop.description, action, 'resolve', userInput);
|
||||
recalculateInlineCalculations(prop.description, action, 'reduce', userInput);
|
||||
logValue = prop.description?.value;
|
||||
}
|
||||
result.appendLog({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { some, includes, difference, intersection } from 'lodash';
|
||||
|
||||
import { getParseNodeFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import { getConstantValueFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
|
||||
@@ -9,15 +9,16 @@ import { PropTask } from '/imports/api/engine/action/tasks/Task';
|
||||
import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import { isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
import Context from '../../../../parser/types/Context';
|
||||
import toString from '/imports/parser/toString';
|
||||
import { getPropertiesOfType } from '/imports/api/engine/loadCreatures';
|
||||
import applyTask from '/imports/api/engine/action/tasks/applyTask';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags';
|
||||
import Context from '/imports/parser/types/Context';
|
||||
import applySavingThrowProperty from '/imports/api/engine/action/applyProperties/applySavingThrowProperty';
|
||||
|
||||
export default async function applyDamageProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
) {
|
||||
const prop = task.prop;
|
||||
const scope = getEffectiveActionScope(action);
|
||||
@@ -28,7 +29,7 @@ export default async function applyDamageProperty(
|
||||
// Choose target
|
||||
const damageTargets = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
// Determine if the hit is critical
|
||||
const criticalHit = getParseNodeFromScope('~criticalHit', scope)?.value
|
||||
const criticalHit = await getConstantValueFromScope('~criticalHit', scope)
|
||||
&& prop.damageType !== 'healing'; // Can't critically heal
|
||||
// Double the damage rolls if the hit is critical
|
||||
const context = new Context({
|
||||
@@ -40,7 +41,7 @@ export default async function applyDamageProperty(
|
||||
const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage';
|
||||
|
||||
// roll the dice only and store that string
|
||||
recalculateCalculation(prop.amount, action, 'compile', userInput);
|
||||
recalculateCalculation(prop.amount, action, 'compile', inputProvider);
|
||||
const { result: rolled } = await resolve('roll', prop.amount.valueNode, scope, context);
|
||||
if (rolled.parseType !== 'constant') {
|
||||
logValue.push(toString(rolled));
|
||||
@@ -72,7 +73,7 @@ export default async function applyDamageProperty(
|
||||
typeof damage !== 'number'
|
||||
|| !isFinite(damage)
|
||||
) {
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, inputProvider);
|
||||
}
|
||||
|
||||
// Round the damage to a whole number
|
||||
@@ -80,7 +81,7 @@ export default async function applyDamageProperty(
|
||||
scope['~damage'] = { value: damage };
|
||||
|
||||
// Convert extra damage into the stored type
|
||||
const lastDamageType = getParseNodeFromScope('~lastDamageType')?.value;
|
||||
const lastDamageType = await getConstantValueFromScope('~lastDamageType', scope);
|
||||
if (prop.damageType === 'extra' && typeof lastDamageType === 'string') {
|
||||
prop.damageType = lastDamageType;
|
||||
}
|
||||
@@ -95,10 +96,10 @@ export default async function applyDamageProperty(
|
||||
(prop.damageType !== 'healing' ? ' damage ' : '');
|
||||
|
||||
// If there is a save, calculate the save damage
|
||||
let damageOnSave, saveNode, saveRoll;
|
||||
let damageOnSave, saveProp, saveRoll;
|
||||
if (prop.save) {
|
||||
if (prop.save.damageFunction?.calculation) {
|
||||
recalculateCalculation(prop.save.damageFunction, action, 'compile', userInput);
|
||||
recalculateCalculation(prop.save.damageFunction, action, 'compile', inputProvider);
|
||||
context.errors = [];
|
||||
const { result: saveDamageRolled } = await resolve(
|
||||
'roll', prop.save.damageFunction.valueNode, scope, context
|
||||
@@ -112,14 +113,14 @@ export default async function applyDamageProperty(
|
||||
if (
|
||||
!isFiniteNode(saveDamageResult)
|
||||
) {
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, inputProvider);
|
||||
}
|
||||
// Round the damage to a whole number
|
||||
damageOnSave = Math.floor(saveDamageResult.value);
|
||||
} else {
|
||||
damageOnSave = Math.floor(damage / 2);
|
||||
}
|
||||
saveNode = {
|
||||
saveProp = {
|
||||
node: {
|
||||
...prop.save,
|
||||
name: prop.save.stat,
|
||||
@@ -136,8 +137,11 @@ export default async function applyDamageProperty(
|
||||
|
||||
// If there is a saving throw, apply that first
|
||||
if (prop.save) {
|
||||
await applySavingThrow(saveNode, actionContext);
|
||||
if (getParseNodeFromScope('~saveSucceeded', scope)?.value) {
|
||||
await applySavingThrowProperty({
|
||||
prop: saveProp,
|
||||
targetIds: task.targetIds,
|
||||
}, action, result, inputProvider);
|
||||
if (await getConstantValueFromScope('~saveSucceeded', scope)) {
|
||||
// Log the total damage
|
||||
logValue.push(toString(reduced));
|
||||
// Log the save damage
|
||||
@@ -165,14 +169,18 @@ export default async function applyDamageProperty(
|
||||
|
||||
// Deal the damage to the target
|
||||
await dealDamage(
|
||||
action, prop, result, userInput, target, prop.damageType, damageToApply
|
||||
action, prop, result, inputProvider, target, prop.damageType, damageToApply
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// There are no targets, just log the result
|
||||
logValue.push(`**${damage}** ${suffix}`);
|
||||
if (prop.save) {
|
||||
await applySavingThrow(saveNode, actionContext);
|
||||
await applySavingThrowProperty(saveProp, action, result, inputProvider);
|
||||
await applySavingThrowProperty({
|
||||
prop: saveProp,
|
||||
targetIds: task.targetIds,
|
||||
}, action, result, inputProvider);
|
||||
logValue.push(`**${damageOnSave}** ${suffix} on a successful save`);
|
||||
}
|
||||
}
|
||||
@@ -181,7 +189,7 @@ export default async function applyDamageProperty(
|
||||
value: logValue.join('\n'),
|
||||
inline: true,
|
||||
}, damageTargets);
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargets, inputProvider);
|
||||
}
|
||||
|
||||
function damageFunctionText(save) {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import recalculateInlineCalculations from '/imports/api/engine/action/functions/recalculateInlineCalculations';
|
||||
import { PropTask } from '/imports/api/engine/action/tasks/Task';
|
||||
import TaskResult, { LogContent } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
|
||||
export default async function applyNoteProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
let contents: LogContent[] | undefined = undefined;
|
||||
const logContent: LogContent = {};
|
||||
if (prop.name) logContent.name = prop.name;
|
||||
if (prop.summary?.text) {
|
||||
await recalculateInlineCalculations(prop.summary, action);
|
||||
await recalculateInlineCalculations(prop.summary, action, 'reduce', inputProvider);
|
||||
logContent.value = prop.summary.value;
|
||||
}
|
||||
|
||||
@@ -21,7 +22,7 @@ export default async function applyNoteProperty(
|
||||
}
|
||||
// Log description
|
||||
if (prop.description?.text) {
|
||||
await recalculateInlineCalculations(prop.description, action);
|
||||
await recalculateInlineCalculations(prop.description, action, 'reduce', inputProvider);
|
||||
if (!contents) contents = [];
|
||||
contents.push({ value: prop.description.value });
|
||||
}
|
||||
@@ -31,5 +32,5 @@ export default async function applyNoteProperty(
|
||||
targetIds: task.targetIds,
|
||||
});
|
||||
}
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import { rollAndReduceCalculation } from '/imports/api/engine/action/functions/recalculateCalculation';
|
||||
import { PropTask } from '/imports/api/engine/action/tasks/Task';
|
||||
import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import { isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
import toString from '/imports/parser/toString';
|
||||
|
||||
export default async function roll(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput
|
||||
export default async function applyRollProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
// If there isn't a calculation, just apply the children instead
|
||||
if (!prop.roll?.calculation) {
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
|
||||
const logValue: string[] = [];
|
||||
@@ -19,7 +21,7 @@ export default async function roll(
|
||||
// roll the dice only and store that string
|
||||
const {
|
||||
rolled, reduced, errors
|
||||
} = await rollAndReduceCalculation(prop.roll, action);
|
||||
} = await rollAndReduceCalculation(prop.roll, action, inputProvider);
|
||||
|
||||
if (rolled.parseType !== 'constant') {
|
||||
logValue.push(toString(rolled));
|
||||
@@ -38,8 +40,8 @@ export default async function roll(
|
||||
}
|
||||
|
||||
// If we didn't end up with a constant or a number of finite value, give up
|
||||
if (reduced?.parseType !== 'constant' || (reduced.valueType === 'number' && !isFinite(reduced.value))) {
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
|
||||
if (reduced?.parseType !== 'constant' || !isFiniteNode(reduced)) {
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
const value = reduced.value;
|
||||
|
||||
@@ -54,5 +56,5 @@ export default async function roll(
|
||||
}, task.targetIds);
|
||||
|
||||
// Apply children
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
|
||||
@@ -1,57 +1,67 @@
|
||||
// TODO
|
||||
import { getFromScope } from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import { applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
|
||||
import recalculateCalculation from '/imports/api/engine/action/functions/recalculateCalculation';
|
||||
import { applyUnresolvedEffects } from '/imports/api/engine/action/methods/doCheck';
|
||||
import { PropTask } from '/imports/api/engine/action/tasks/Task';
|
||||
import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import { getVariables } from '/imports/api/engine/loadCreatures';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { isFiniteNode } from '/imports/parser/parseTree/constant';
|
||||
|
||||
export default function applySavingThrow(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.doc
|
||||
const originalTargets = actionContext.targets;
|
||||
export default async function applySavingThrowProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
|
||||
let saveTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||
const prop = task.prop;
|
||||
const originalTargetIds = task.targetIds;
|
||||
|
||||
recalculateCalculation(prop.dc, actionContext);
|
||||
const saveTargetIds = prop.target === 'self' ? [action.creatureId] : originalTargetIds;
|
||||
|
||||
const dc = (prop.dc?.value);
|
||||
if (!isFinite(dc)) {
|
||||
actionContext.addLog({
|
||||
if (saveTargetIds.length > 1)
|
||||
|
||||
recalculateCalculation(prop.dc, action, 'reduce', inputProvider);
|
||||
|
||||
if (!isFiniteNode(prop.dc)) {
|
||||
result.appendLog({
|
||||
name: 'Error',
|
||||
value: 'Saving throw requires a DC',
|
||||
});
|
||||
return node.children.forEach(child => applyProperty(child, actionContext));
|
||||
}, saveTargetIds);
|
||||
return applyDefaultAfterPropTasks(action, prop, saveTargetIds, inputProvider);
|
||||
}
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
|
||||
const dc = (prop.dc?.value);
|
||||
if (!prop.silent) result.appendLog({
|
||||
name: prop.name,
|
||||
value: `DC **${dc}**`,
|
||||
inline: true,
|
||||
});
|
||||
const scope = actionContext.scope;
|
||||
...prop.silent && { silenced: prop.silent }
|
||||
}, saveTargetIds);
|
||||
const scope = await getEffectiveActionScope(action);
|
||||
|
||||
// If there are no save targets, apply all children as if the save both
|
||||
// succeeeded and failed
|
||||
if (!saveTargets?.length) {
|
||||
scope['~saveFailed'] = { value: true };
|
||||
scope['~saveSucceeded'] = { value: true };
|
||||
return applyChildren(node, actionContext);
|
||||
// succeeded and failed
|
||||
if (!saveTargetIds?.length) {
|
||||
result.scope = {
|
||||
['~saveFailed']: { value: true },
|
||||
['~saveSucceeded']: { value: true },
|
||||
}
|
||||
return applyDefaultAfterPropTasks(action, prop, saveTargetIds, inputProvider);
|
||||
}
|
||||
|
||||
// Each target makes the saving throw
|
||||
saveTargets.forEach(target => {
|
||||
delete scope['~saveFailed'];
|
||||
delete scope['~saveSucceeded'];
|
||||
delete scope['~saveDiceRoll'];
|
||||
delete scope['~saveRoll'];
|
||||
for (const targetId of saveTargetIds) {
|
||||
|
||||
const applyChildrenToTarget = function () {
|
||||
actionContext.targets = [target];
|
||||
return applyChildren(node, actionContext);
|
||||
};
|
||||
|
||||
const save = target.variables[prop.stat];
|
||||
const save = getFromScope('save', getVariables(targetId));
|
||||
|
||||
if (!save) {
|
||||
actionContext.addLog({
|
||||
result.appendLog({
|
||||
name: 'Saving throw error',
|
||||
value: 'No saving throw found: ' + prop.stat,
|
||||
});
|
||||
return applyChildrenToTarget();
|
||||
}, [targetId]);
|
||||
applyDefaultAfterPropTasks(action, prop, [targetId], inputProvider);
|
||||
}
|
||||
|
||||
let rollModifierText = numberToSignedString(save.value, true);
|
||||
@@ -60,9 +70,9 @@ export default function applySavingThrow(node, actionContext) {
|
||||
rollModifierText += effectString;
|
||||
rollModifier += effectBonus;
|
||||
|
||||
let value, values, resultPrefix;
|
||||
let value, resultPrefix;
|
||||
if (save.advantage === 1) {
|
||||
const [a, b] = rollDice(2, 20);
|
||||
const [[a, b]] = await inputProvider.rollDice([{ number: 2, diceSize: 20 }]);
|
||||
if (a >= b) {
|
||||
value = a;
|
||||
resultPrefix = `Advantage\n1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
|
||||
@@ -71,7 +81,7 @@ export default function applySavingThrow(node, actionContext) {
|
||||
resultPrefix = `Advantage\n1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
|
||||
}
|
||||
} else if (save.advantage === -1) {
|
||||
const [a, b] = rollDice(2, 20);
|
||||
const [[a, b]] = await inputProvider.rollDice([{ number: 2, diceSize: 20 }]);
|
||||
if (a <= b) {
|
||||
value = a;
|
||||
resultPrefix = `Disadvantage\n1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
|
||||
@@ -80,26 +90,24 @@ export default function applySavingThrow(node, actionContext) {
|
||||
resultPrefix = `Disadvantage\n1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
|
||||
}
|
||||
} else {
|
||||
values = rollDice(1, 20);
|
||||
value = values[0];
|
||||
const [[rolledValue]] = await inputProvider.rollDice([{ number: 1, diceSize: 20 }]);
|
||||
value = rolledValue;
|
||||
resultPrefix = `1d20 [ ${value} ] ${rollModifierText}`
|
||||
}
|
||||
scope['~saveDiceRoll'] = { value };
|
||||
const result = value + rollModifier || 0;
|
||||
scope['~saveRoll'] = { value: result };
|
||||
const saveSuccess = result >= dc;
|
||||
const resultValue = value + rollModifier || 0;
|
||||
scope['~saveRoll'] = { value: resultValue };
|
||||
const saveSuccess = resultValue >= dc;
|
||||
if (saveSuccess) {
|
||||
scope['~saveSucceeded'] = { value: true };
|
||||
} else {
|
||||
scope['~saveFailed'] = { value: true };
|
||||
}
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
if (!prop.silent) result.appendLog({
|
||||
name: saveSuccess ? 'Successful save' : 'Failed save',
|
||||
value: resultPrefix + '\n**' + result + '**',
|
||||
value: resultPrefix + '\n**' + resultValue + '**',
|
||||
inline: true,
|
||||
});
|
||||
return applyChildrenToTarget();
|
||||
});
|
||||
// reset the targets after the save to each child
|
||||
actionContext.targets = originalTargets;
|
||||
}, [targetId]);
|
||||
return applyDefaultAfterPropTasks(action, prop, [targetId], inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
// TODO
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
import { applyAfterTasksSkipChildren, applyDefaultAfterPropTasks } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import recalculateCalculation from '/imports/api/engine/action/functions/recalculateCalculation';
|
||||
import { PropTask } from '/imports/api/engine/action/tasks/Task';
|
||||
import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
||||
|
||||
export default function applyToggle(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.doc
|
||||
recalculateCalculation(prop.condition, actionContext);
|
||||
export default async function applyToggle(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
|
||||
const prop = task.prop;
|
||||
await recalculateCalculation(prop.condition, action, 'reduce', inputProvider);
|
||||
if (prop.condition?.value) {
|
||||
return applyChildren(node, actionContext);
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
} else {
|
||||
return applyAfterTasksSkipChildren(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getPropertyChildren, getSingleProperty } from '/imports/api/engine/load
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import applyTask from '../tasks/applyTask';
|
||||
import { PropTask } from '../tasks/Task';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
|
||||
/**
|
||||
* Get all the child tasks of a given property
|
||||
@@ -13,11 +14,11 @@ import { PropTask } from '../tasks/Task';
|
||||
* @returns
|
||||
*/
|
||||
export async function applyChildren(
|
||||
action: EngineAction, prop, targetIds: string[], userInput
|
||||
action: EngineAction, prop, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
const children = await getPropertyChildren(action.creatureId, prop);
|
||||
for (const childProp of children) {
|
||||
await applyTask(action, { prop: childProp, targetIds }, userInput);
|
||||
await applyTask(action, { prop: childProp, targetIds }, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,24 +29,24 @@ export async function applyChildren(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyAfterChildrenTriggers(
|
||||
action: EngineAction, prop, targetIds: string[], userInput
|
||||
action: EngineAction, prop, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
if (!prop.triggerIds?.afterChildren) return;
|
||||
for (const triggerId of prop.triggerIds.afterChildren) {
|
||||
const trigger = await getSingleProperty(action.creatureId, triggerId);
|
||||
if (!trigger) continue;
|
||||
await applyTask(action, { prop: trigger, targetIds }, userInput);
|
||||
await applyTask(action, { prop: trigger, targetIds }, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyAfterTriggers(
|
||||
action: EngineAction, prop, targetIds: string[], userInput
|
||||
action: EngineAction, prop, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
if (!prop.triggerIds?.after) return;
|
||||
for (const triggerId of prop.triggerIds.after) {
|
||||
const trigger = await getSingleProperty(action.creatureId, triggerId);
|
||||
if (!trigger) continue;
|
||||
await applyTask(action, { prop: trigger, targetIds }, userInput);
|
||||
await applyTask(action, { prop: trigger, targetIds }, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +61,11 @@ export async function applyAfterTriggers(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyDefaultAfterPropTasks(
|
||||
action: EngineAction, prop, targetIds: string[], userInput
|
||||
action: EngineAction, prop, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
await applyAfterTriggers(action, prop, targetIds, userInput);
|
||||
await applyChildren(action, prop, targetIds, userInput);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterTriggers(action, prop, targetIds, inputProvider);
|
||||
await applyChildren(action, prop, targetIds, inputProvider);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, inputProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,10 +78,10 @@ export async function applyDefaultAfterPropTasks(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyAfterTasksSkipChildren(
|
||||
action: EngineAction, prop, targetIds: string[], userInput
|
||||
action: EngineAction, prop, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
await applyAfterTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterTriggers(action, prop, targetIds, inputProvider);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, inputProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,11 +94,11 @@ export async function applyAfterTasksSkipChildren(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyAfterPropTasksForSingleChild(
|
||||
action: EngineAction, prop, childProp, targetIds: string[], userInput
|
||||
action: EngineAction, prop, childProp, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
await applyAfterTriggers(action, prop, targetIds, userInput);
|
||||
await applyTask(action, { prop: childProp, targetIds }, userInput);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterTriggers(action, prop, targetIds, inputProvider);
|
||||
await applyTask(action, { prop: childProp, targetIds }, inputProvider);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, inputProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,13 +111,13 @@ export async function applyAfterPropTasksForSingleChild(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyAfterPropTasksForSomeChildren(
|
||||
action: EngineAction, prop, children, targetIds: string[], userInput
|
||||
action: EngineAction, prop, children, targetIds: string[], inputProvider: InputProvider
|
||||
) {
|
||||
await applyAfterTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterTriggers(action, prop, targetIds, inputProvider);
|
||||
for (const childProp of children) {
|
||||
await applyTask(action, { prop: childProp, targetIds }, userInput);
|
||||
await applyTask(action, { prop: childProp, targetIds }, inputProvider);
|
||||
}
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, userInput);
|
||||
await applyAfterChildrenTriggers(action, prop, targetIds, inputProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,14 +129,14 @@ export async function applyAfterPropTasksForSomeChildren(
|
||||
* @returns
|
||||
*/
|
||||
export async function applyTriggers(
|
||||
action: EngineAction, prop, targetIds: string[], triggerPath: string, userInput
|
||||
action: EngineAction, prop, targetIds: string[], triggerPath: string, inputProvider: InputProvider
|
||||
) {
|
||||
const triggerIds = get(prop?.triggers, triggerPath);
|
||||
if (!triggerIds) return;
|
||||
for (const triggerId of triggerIds) {
|
||||
const trigger = await getSingleProperty(action.creatureId, triggerId);
|
||||
if (!trigger) continue;
|
||||
await applyTask(action, { prop: trigger, targetIds }, userInput);
|
||||
await applyTask(action, { prop: trigger, targetIds }, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +147,7 @@ export async function applyTriggers(
|
||||
* @returns Copies of the task, but with a single target each
|
||||
*/
|
||||
export async function applyTaskToEachTarget(
|
||||
action: EngineAction, task: PropTask, targetIds: string[] = task.targetIds, userInput
|
||||
action: EngineAction, task: PropTask, targetIds: string[] = task.targetIds, inputProvider: InputProvider
|
||||
) {
|
||||
if (targetIds.length <= 1) throw 'Must have multiple targets to split a task';
|
||||
// If there are targets, apply a new task to each target
|
||||
@@ -154,6 +155,6 @@ export async function applyTaskToEachTarget(
|
||||
await applyTask(action, {
|
||||
...task,
|
||||
targetIds: [targetId]
|
||||
}, userInput);
|
||||
}, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import embedInlineCalculations from '/imports/api/engine/computation/utility/embedInlineCalculations';
|
||||
import recalculateCalculation from './recalculateCalculation'
|
||||
import { InlineCalculation } from '/imports/api/properties/subSchemas/inlineCalculationField';
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import ResolveLevel from '/imports/parser/types/ResolveLevel';
|
||||
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
|
||||
export default async function recalculateInlineCalculations(inlineCalcObj, action, parseLevel, userInput) {
|
||||
export default async function recalculateInlineCalculations(
|
||||
inlineCalcObj: InlineCalculation, action: EngineAction,
|
||||
parseLevel: ResolveLevel, userInput: InputProvider
|
||||
) {
|
||||
// Skip if there are no calculations
|
||||
if (!inlineCalcObj?.inlineCalculations?.length) return;
|
||||
// Recalculate each calculation with the current scope
|
||||
|
||||
@@ -5,9 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import recalculateCalculation from '../../actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
|
||||
// TODO Migrate this to the new action engine
|
||||
|
||||
@@ -9,16 +9,16 @@ import InputProvider from '/imports/api/engine/action/functions/InputProvider';
|
||||
|
||||
// DamagePropTask promises a number of actual damage done
|
||||
export default async function applyTask(
|
||||
action: EngineAction, task: DamagePropTask, userInput: InputProvider
|
||||
action: EngineAction, task: DamagePropTask, inputProvider: InputProvider
|
||||
): Promise<number>
|
||||
|
||||
// Other tasks promise nothing
|
||||
export default async function applyTask(
|
||||
action: EngineAction, task: PropTask | ItemAsAmmoTask, userInput: InputProvider
|
||||
action: EngineAction, task: PropTask | ItemAsAmmoTask, inputProvider: InputProvider
|
||||
): Promise<void>
|
||||
|
||||
export default async function applyTask(
|
||||
action: EngineAction, task: Task, userInput: InputProvider
|
||||
action: EngineAction, task: Task, inputProvider: InputProvider
|
||||
): Promise<void | number> {
|
||||
action.taskCount += 1;
|
||||
if (action.taskCount > 100) throw 'Only 100 properties can be applied at once';
|
||||
@@ -28,9 +28,9 @@ export default async function applyTask(
|
||||
action.results.push(result);
|
||||
switch (task.subtaskFn) {
|
||||
case 'damageProp':
|
||||
return applyDamagePropTask(task, action, result, userInput);
|
||||
return applyDamagePropTask(task, action, result, inputProvider);
|
||||
case 'consumeItemAsAmmo':
|
||||
return applyItemAsAmmoTask(task, action, result, userInput);
|
||||
return applyItemAsAmmoTask(task, action, result, inputProvider);
|
||||
}
|
||||
} else {
|
||||
// Get property
|
||||
@@ -47,7 +47,7 @@ export default async function applyTask(
|
||||
for (const triggerId of prop.triggerIds.before) {
|
||||
const trigger = await getSingleProperty(action.creatureId, triggerId);
|
||||
if (!trigger) continue;
|
||||
await applyTask(action, { prop: trigger, targetIds: task.targetIds }, userInput);
|
||||
await applyTask(action, { prop: trigger, targetIds: task.targetIds }, inputProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,6 @@ export default async function applyTask(
|
||||
action.results.push(result);
|
||||
|
||||
// Apply the property
|
||||
return applyProperties[prop.type]?.(task, action, result, userInput);
|
||||
return applyProperties[prop.type]?.(task, action, result, inputProvider);
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,10 @@ describe('Call Node', function () {
|
||||
assert.isEmpty(context.errors)
|
||||
assert.equal(toString(result), 'min(unknownVariable, 3, 3d30)');
|
||||
});
|
||||
it('reduces', async function () {
|
||||
const callNode = parse('min( unknownVariable, 1 + 2, 3d30 )');
|
||||
const { result, context } = await resolve('reduce', callNode, undefined, undefined, inputProviderForTests);
|
||||
assert.isEmpty(context.errors)
|
||||
assert.equal(toString(result), '0');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -173,16 +173,13 @@ const call: CallFactory = {
|
||||
expectedType = argumentsExpected[index];
|
||||
}
|
||||
if (expectedType === 'parseNode') return;
|
||||
if (
|
||||
node.parseType !== expectedType
|
||||
|| (
|
||||
node.parseType === 'constant'
|
||||
&& node.valueType !== expectedType
|
||||
)
|
||||
) failed = true;
|
||||
failed = !(
|
||||
node.parseType === expectedType
|
||||
|| (node.parseType === 'constant' && node.valueType === expectedType)
|
||||
);
|
||||
if (failed && fn === 'reduce') {
|
||||
const typeName = typeof expectedType === 'string' ? expectedType : expectedType.constructor.name;
|
||||
const nodeName = node.parseType;
|
||||
const nodeName = node.parseType === 'constant' ? node.valueType : node.parseType;
|
||||
context.error(`Incorrect arguments to ${callNode.functionName} function` +
|
||||
`expected ${typeName} got ${nodeName}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
process.on('unhandledRejection', (error, p) => {
|
||||
console.dir(error.stack);
|
||||
console.error('Unhandled Rejection at:', p, 'reason:', error)
|
||||
process.exit(1)
|
||||
});
|
||||
Reference in New Issue
Block a user