Fix TypeScript errors in action engine
This commit is contained in:
8
app/imports/@types/validated-method.d.ts
vendored
8
app/imports/@types/validated-method.d.ts
vendored
@@ -13,15 +13,15 @@ declare module 'meteor/mdg:validated-method' {
|
||||
callAsync: Argument<TRun> extends NoArguments
|
||||
// methods with no argument can be called with () or just a callback
|
||||
?
|
||||
& ((unusedArg: any, callback: (error: Meteor.Error, result: Return<TRun>) => void) => void)
|
||||
& ((callback: (error: Meteor.Error | undefined, result: Return<TRun>) => void) => void)
|
||||
& ((unusedArg: any, callback?: (error: Meteor.Error, result: Return<TRun>) => void) => void)
|
||||
& ((callback?: (error: Meteor.Error | undefined, result: Return<TRun>) => void) => void)
|
||||
& (() => Return<TRun>)
|
||||
// methods with arguments require those arguments to be called
|
||||
:
|
||||
& ((
|
||||
arg: Argument<TRun>,
|
||||
callback: (error: Meteor.Error | undefined, result: Return<TRun>) => void,
|
||||
callback?: (error: Meteor.Error | undefined, result: Return<TRun>) => void,
|
||||
) => void)
|
||||
& ((arg: Argument<TRun>) => Return<TRun>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
|
||||
const updateCreatureProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.update',
|
||||
@@ -11,14 +10,10 @@ const updateCreatureProperty = new ValidatedMethod({
|
||||
// We cannot change these fields with a simple update
|
||||
switch (path[0]) {
|
||||
case 'type':
|
||||
case 'order':
|
||||
case 'parent':
|
||||
case 'ancestors':
|
||||
case 'root':
|
||||
case 'left':
|
||||
case 'right':
|
||||
case 'parentId':
|
||||
case 'damage':
|
||||
throw new Meteor.Error('Permission denied',
|
||||
'This property can\'t be updated directly');
|
||||
}
|
||||
@@ -30,13 +25,12 @@ const updateCreatureProperty = new ValidatedMethod({
|
||||
},
|
||||
run({ _id, path, value }) {
|
||||
// Permission
|
||||
let property = CreatureProperties.findOne(_id, {
|
||||
const property = CreatureProperties.findOne(_id, {
|
||||
fields: { type: 1, root: 1 }
|
||||
});
|
||||
let rootCreature = getRootCreatureAncestor(property);
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
assertDocEditPermission(property, this.userId);
|
||||
|
||||
let pathString = path.join('.');
|
||||
const pathString = path.join('.');
|
||||
let modifier;
|
||||
// unset empty values
|
||||
if (value === null || value === undefined) {
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
import { LogContent, Mutation, Update } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import Alea from 'alea';
|
||||
@@ -19,7 +20,7 @@ const [
|
||||
attributeResetByEventId, eventActionId, advantageAttackId, advantageEffectId, disadvantageAttackId, disadvantageEffectId,
|
||||
] = getRandomIds(100);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
// Empty
|
||||
@@ -138,7 +139,8 @@ const actionTestCreature = {
|
||||
_id: consumeResourceId,
|
||||
variableName: 'resourceVar',
|
||||
quantity: { calculation: '2' },
|
||||
}]
|
||||
}],
|
||||
conditions: [],
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -149,7 +151,9 @@ const actionTestCreature = {
|
||||
_id: consumeResourceId,
|
||||
variableName: 'resourceVar',
|
||||
quantity: { calculation: '9001' },
|
||||
}]
|
||||
}],
|
||||
itemsConsumed: [],
|
||||
conditions: [],
|
||||
}
|
||||
},
|
||||
// Events and resetting attributes
|
||||
@@ -172,7 +176,7 @@ const actionTestCreature = {
|
||||
],
|
||||
}
|
||||
|
||||
const actionTargetCreature = {
|
||||
const actionTargetCreature: TestCreature = {
|
||||
_id: targetCreatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -184,7 +188,7 @@ const actionTargetCreature = {
|
||||
]
|
||||
}
|
||||
|
||||
const actionTargetCreature2 = {
|
||||
const actionTargetCreature2: TestCreature = {
|
||||
_id: targetCreature2Id,
|
||||
props: [
|
||||
{
|
||||
@@ -329,7 +333,9 @@ describe('Apply Action Properties', function () {
|
||||
|
||||
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');
|
||||
assert(prop);
|
||||
assert(prop.type === 'action')
|
||||
assert.equal(prop.attackRoll?.advantage, 1, 'The attack roll should have advantage');
|
||||
const action = await runActionById(advantageAttackId, [targetCreatureId]);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
@@ -349,7 +355,9 @@ describe('Apply Action Properties', function () {
|
||||
|
||||
it('should make attack rolls that roll with disadvantage', async function () {
|
||||
const prop = await CreatureProperties.findOneAsync(disadvantageAttackId);
|
||||
assert.equal(prop.attackRoll.disadvantage, 1, 'The attack roll should have disadvantage');
|
||||
assert(prop);
|
||||
assert(prop.type === 'action');
|
||||
assert.equal(prop.attackRoll?.disadvantage, 1, 'The attack roll should have disadvantage');
|
||||
const action = await runActionById(disadvantageAttackId, [targetCreatureId]);
|
||||
const expectedMutations: Mutation[] = [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ import { getNumberFromScope } from '/imports/api/creature/creatures/CreatureVari
|
||||
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
|
||||
import { CalculatedField } from '/imports/api/properties/subSchemas/computedField';
|
||||
import applyResetTask from '/imports/api/engine/action/tasks/applyResetTask';
|
||||
import { CreaturePropertyTypes } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
export default async function applyActionProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput: InputProvider
|
||||
@@ -101,6 +102,8 @@ async function applyAttackToTarget(
|
||||
task: PropTask, action: EngineAction, attack: CalculatedField, targetId: string,
|
||||
taskResult: TaskResult, userInput: InputProvider
|
||||
) {
|
||||
const prop = task.prop as CreaturePropertyTypes['action'] | CreaturePropertyTypes['spell'];
|
||||
|
||||
taskResult.pushScope = {
|
||||
'~attackHit': {},
|
||||
'~attackMiss': {},
|
||||
@@ -138,7 +141,7 @@ async function applyAttackToTarget(
|
||||
name,
|
||||
value: `${resultPrefix}\n**${result}**`,
|
||||
inline: true,
|
||||
...task.prop.silent && { silenced: true },
|
||||
...prop.silent && { silenced: true },
|
||||
});
|
||||
|
||||
if (criticalMiss || result < targetArmor) {
|
||||
@@ -151,12 +154,12 @@ async function applyAttackToTarget(
|
||||
name: 'Error',
|
||||
value: 'Target has no `armor`',
|
||||
inline: true,
|
||||
...task.prop.silent && { silenced: true },
|
||||
...prop.silent && { silenced: true },
|
||||
}, {
|
||||
name: criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : 'To Hit',
|
||||
value: `${resultPrefix}\n**${result}**`,
|
||||
inline: true,
|
||||
...task.prop.silent && { silenced: true },
|
||||
...prop.silent && { silenced: true },
|
||||
});
|
||||
}
|
||||
if (contents.length) {
|
||||
|
||||
@@ -7,11 +7,17 @@ import TaskResult from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import applyTask from '/imports/api/engine/action/tasks/applyTask';
|
||||
import { getSingleProperty, getVariables } from '/imports/api/engine/loadCreatures';
|
||||
import getPropertyTitle from '/imports/api/utility/getPropertyTitle';
|
||||
import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
export default async function applyAdjustmentProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'adjustment') {
|
||||
throw new Meteor.Error('wrong-property', `Expected an adjustment, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const damageTargetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
|
||||
if (damageTargetIds.length > 1) {
|
||||
@@ -30,7 +36,7 @@ export default async function applyAdjustmentProperty(
|
||||
|
||||
// Evaluate the amount
|
||||
await recalculateCalculation(prop.amount, action, 'reduce', userInput);
|
||||
const value = +prop.amount.value;
|
||||
const value = Number(prop.amount.value ?? 0);
|
||||
if (!isFinite(value)) {
|
||||
result.appendLog({
|
||||
name: 'Error',
|
||||
@@ -44,8 +50,8 @@ export default async function applyAdjustmentProperty(
|
||||
throw new Meteor.Error('1 target Expected', 'At this step, only a single target is supported');
|
||||
}
|
||||
const targetId = damageTargetIds[0];
|
||||
let stat;
|
||||
if (targetId) {
|
||||
let stat: CreatureProperty | undefined;
|
||||
if (targetId && prop.stat) {
|
||||
const statId = getVariables(targetId)?.[prop.stat]?._propId;
|
||||
stat = statId && getSingleProperty(targetId, statId);
|
||||
if (!stat?.type) {
|
||||
@@ -64,7 +70,7 @@ export default async function applyAdjustmentProperty(
|
||||
title: getPropertyTitle(prop),
|
||||
operation: prop.operation,
|
||||
value,
|
||||
targetProp: stat ?? { _id: 'dummyStat', name: prop.stat, type: 'attribute' },
|
||||
targetProp: stat ?? { name: prop.stat ?? '' },
|
||||
},
|
||||
}, userInput);
|
||||
return applyDefaultAfterPropTasks(action, prop, damageTargetIds, userInput);
|
||||
|
||||
@@ -12,10 +12,23 @@ export default async function applyBranchProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'branch') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a branch, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const targets = task.targetIds;
|
||||
|
||||
switch (prop.branchType) {
|
||||
case 'if': {
|
||||
if (!prop.condition) {
|
||||
result.appendLog({
|
||||
name: 'Branch Error',
|
||||
value: 'If branch does not have a condition set',
|
||||
silenced: prop.silent,
|
||||
}, targets);
|
||||
return applyAfterTasksSkipChildren(action, prop, targets, userInput);
|
||||
}
|
||||
await recalculateCalculation(prop.condition, action, 'reduce', userInput);
|
||||
if (prop.condition?.value) {
|
||||
return applyDefaultAfterPropTasks(action, prop, targets, userInput);
|
||||
@@ -28,8 +41,17 @@ export default async function applyBranchProperty(
|
||||
if (!children.length) {
|
||||
return applyAfterTasksSkipChildren(action, prop, targets, userInput);
|
||||
}
|
||||
if (!prop.condition) {
|
||||
result.appendLog({
|
||||
name: 'Branch Error',
|
||||
value: 'Index branch does not have a condition set',
|
||||
silenced: prop.silent,
|
||||
}, targets);
|
||||
return applyAfterTasksSkipChildren(action, prop, targets, userInput);
|
||||
}
|
||||
await recalculateCalculation(prop.condition, action, 'reduce', userInput);
|
||||
if (!isFinite(prop.condition?.value)) {
|
||||
let index = Number(prop.condition.value);
|
||||
if (!isFinite(index)) {
|
||||
result.appendLog({
|
||||
name: 'Branch Error',
|
||||
value: `Index did not resolve into a valid number, got \`${prop.condition?.value}\` instead`,
|
||||
@@ -37,7 +59,7 @@ export default async function applyBranchProperty(
|
||||
}, targets);
|
||||
return applyAfterTasksSkipChildren(action, prop, targets, userInput);
|
||||
}
|
||||
let index = Math.floor(prop.condition?.value);
|
||||
index = Math.floor(index);
|
||||
if (index < 1) index = 1;
|
||||
if (index > children.length) index = children.length;
|
||||
const child = children[index - 1];
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, targetCreatureId, buffId
|
||||
] = getRandomIds(100);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -36,7 +37,7 @@ const actionTestCreature = {
|
||||
],
|
||||
};
|
||||
|
||||
const actionTargetCreature = {
|
||||
const actionTargetCreature: TestCreature = {
|
||||
_id: targetCreatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -87,7 +88,6 @@ describe('Apply Buff Properties', function () {
|
||||
},
|
||||
left: 1,
|
||||
right: 4,
|
||||
parentId: null,
|
||||
root: {
|
||||
collection: 'creatures',
|
||||
id: targetCreatureId,
|
||||
|
||||
@@ -24,6 +24,11 @@ export default async function applyBuffProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput: InputProvider
|
||||
) {
|
||||
const prop = EJSON.clone(task.prop);
|
||||
|
||||
if (prop.type !== 'buff') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a buff, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const targetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
|
||||
// Log the buff and return if there are no targets
|
||||
@@ -55,7 +60,7 @@ export default async function applyBuffProperty(
|
||||
renewDocIds({
|
||||
docArray: targetPropList,
|
||||
idMap: {
|
||||
[prop.parentId]: null,
|
||||
...prop.parentId && { [prop.parentId]: null },
|
||||
[prop.root.id]: target,
|
||||
},
|
||||
collectionMap: { [prop.root.collection]: 'creatures' }
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, otherCreatureId, buffId, removeParentBuffId, removeTargetBuffsId,
|
||||
] = getRandomIds(100);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -43,7 +44,7 @@ const actionTestCreature = {
|
||||
],
|
||||
};
|
||||
|
||||
const actionOtherCreature = {
|
||||
const actionOtherCreature: TestCreature = {
|
||||
_id: otherCreatureId,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -7,11 +7,12 @@ import getEffectivePropTags from '/imports/api/engine/computation/utility/getEff
|
||||
import { applyDefaultAfterPropTasks, applyTaskToEachTarget } from '/imports/api/engine/action/functions/applyTaskGroups';
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
|
||||
import { CreaturePropertyTypes } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
export default async function applyBuffRemoverProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput: InputProvider
|
||||
) {
|
||||
const prop = task.prop;
|
||||
const prop = task.prop as CreaturePropertyTypes['buffRemover'];
|
||||
|
||||
const targetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
|
||||
|
||||
@@ -7,7 +7,12 @@ export default async function applyCreatureTemplateProperty(
|
||||
task: PropTask, action: EngineAction, result, userInput
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
//Log the Creature that is about to be summoned
|
||||
|
||||
if (prop.type !== 'creature') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a creature, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
// Log the Creature that is about to be summoned
|
||||
let logValue = prop.description?.value
|
||||
if (prop.description?.text) {
|
||||
await recalculateInlineCalculations(prop.description, action, 'reduce', userInput);
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
import { critInputProvider } from '../functions/userInput/inputProviderForTests.testFn';
|
||||
|
||||
@@ -12,7 +13,7 @@ const [
|
||||
creatureId, targetCreatureId, targetCreature2Id, damageTargetId, damageSelfId, targetCreatureHitPointsId, targetCreature2HitPointsId, selfHitPointsId, damageWithEffectsId, effectId, effect2Id,
|
||||
] = getRandomIds(20);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -61,7 +62,7 @@ const actionTestCreature = {
|
||||
],
|
||||
}
|
||||
|
||||
const actionTargetCreature = {
|
||||
const actionTargetCreature: TestCreature = {
|
||||
_id: targetCreatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -75,7 +76,7 @@ const actionTargetCreature = {
|
||||
]
|
||||
}
|
||||
|
||||
const actionTargetCreature2 = {
|
||||
const actionTargetCreature2: TestCreature = {
|
||||
_id: targetCreature2Id,
|
||||
props: [
|
||||
{
|
||||
@@ -247,7 +248,7 @@ describe('Apply Damage Properties', function () {
|
||||
const [
|
||||
creatureId, damageId, actionId
|
||||
] = getRandomIds(3);
|
||||
const testCreature = {
|
||||
const testCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -16,11 +16,17 @@ import InputProvider from '/imports/api/engine/action/functions/userInput/InputP
|
||||
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';
|
||||
import { assert } from 'chai';
|
||||
|
||||
export default async function applyDamageProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
) {
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'damage') {
|
||||
throw new Meteor.Error('wrong-property', `Expected damage, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const scope = await getEffectiveActionScope(action);
|
||||
|
||||
// Choose target
|
||||
@@ -66,7 +72,7 @@ export default async function applyDamageProperty(
|
||||
damage = reduced.value;
|
||||
}
|
||||
} else if (reduced.parseType === 'error') {
|
||||
prop.amount.value = null;
|
||||
prop.amount.value = undefined;
|
||||
} else {
|
||||
prop.amount.value = toString(reduced);
|
||||
}
|
||||
@@ -104,6 +110,7 @@ export default async function applyDamageProperty(
|
||||
if (prop.save.damageFunction?.calculation) {
|
||||
await recalculateCalculation(prop.save.damageFunction, action, 'compile', inputProvider);
|
||||
context.errors = [];
|
||||
assert(prop.save.damageFunction.valueNode, 'Expected value to be defined after recalculateCalculation');
|
||||
const { result: saveDamageRolled } = await resolve(
|
||||
'roll', prop.save.damageFunction.valueNode, scope, context, inputProvider
|
||||
);
|
||||
@@ -154,7 +161,7 @@ export default async function applyDamageProperty(
|
||||
} else {
|
||||
logValue.push(
|
||||
'**Damage on successful save**',
|
||||
prop.save.damageFunction.calculation,
|
||||
prop.save.damageFunction?.calculation ?? '',
|
||||
saveRoll
|
||||
);
|
||||
}
|
||||
@@ -283,14 +290,14 @@ async function dealDamage(
|
||||
healthBars.sort((a, b) => {
|
||||
let diff;
|
||||
if (amount >= 0) {
|
||||
diff = a.healthBarDamageOrder - b.healthBarDamageOrder;
|
||||
diff = (a.healthBarDamageOrder ?? 0) - (b.healthBarDamageOrder ?? 0);
|
||||
} else {
|
||||
diff = a.healthBarHealingOrder - b.healthBarHealingOrder;
|
||||
diff = (a.healthBarHealingOrder ?? 0) - (b.healthBarHealingOrder ?? 0);
|
||||
}
|
||||
if (Number.isFinite(diff)) {
|
||||
return diff;
|
||||
} else {
|
||||
return a.order - b.order;
|
||||
return a.left - b.left;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, folderId
|
||||
] = getRandomIds(100);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -7,5 +7,10 @@ export default async function applyFolderProperty(
|
||||
task: PropTask, action: EngineAction, result, userInput
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'folder') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a folder, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, userInput);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,12 @@ export default async function applyNoteProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
const logContent: LogContent & { silenced: boolean } = {
|
||||
|
||||
if (prop.type !== 'note') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a note, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const logContent: LogContent & { silenced: boolean | undefined; } = {
|
||||
silenced: prop.silent,
|
||||
};
|
||||
if (prop.name) logContent.name = prop.name;
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, rollId,
|
||||
] = getRandomIds(2);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -11,6 +11,11 @@ export default async function applyRollProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, inputProvider: InputProvider
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'roll') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a roll, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
// If there isn't a calculation, just apply the children instead
|
||||
if (!prop.roll?.calculation) {
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
@@ -38,7 +43,7 @@ export default async function applyRollProperty(
|
||||
if (reduced.parseType === 'constant') {
|
||||
prop.roll.value = reduced.value;
|
||||
} else if (reduced.parseType === 'error') {
|
||||
prop.roll.value = null;
|
||||
prop.roll.value = undefined;
|
||||
} else {
|
||||
prop.roll.value = toString(reduced);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, savingThrowId, targetCreatureId, targetCreature2Id
|
||||
] = getRandomIds(4);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -39,7 +40,7 @@ const actionTestCreature = {
|
||||
],
|
||||
}
|
||||
|
||||
const actionTargetCreature = {
|
||||
const actionTargetCreature: TestCreature = {
|
||||
_id: targetCreatureId,
|
||||
props: [
|
||||
{
|
||||
@@ -49,7 +50,7 @@ const actionTargetCreature = {
|
||||
},
|
||||
],
|
||||
}
|
||||
const actionTargetCreature2 = {
|
||||
const actionTargetCreature2: TestCreature = {
|
||||
_id: targetCreature2Id,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -16,13 +16,19 @@ export default async function applySavingThrowProperty(
|
||||
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'savingThrow') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a savingThrow, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const saveTargetIds = prop.target === 'self' ? [action.creatureId] : task.targetIds;
|
||||
|
||||
if (saveTargetIds.length > 1) {
|
||||
return applyTaskToEachTarget(action, task, saveTargetIds, inputProvider);
|
||||
}
|
||||
|
||||
recalculateCalculation(prop.dc, action, 'reduce', inputProvider);
|
||||
if (prop.dc) {
|
||||
recalculateCalculation(prop.dc, action, 'reduce', inputProvider);
|
||||
}
|
||||
|
||||
if (!isFiniteNode(prop.dc?.valueNode)) {
|
||||
result.appendLog({
|
||||
@@ -33,7 +39,7 @@ export default async function applySavingThrowProperty(
|
||||
return applyDefaultAfterPropTasks(action, prop, saveTargetIds, inputProvider);
|
||||
}
|
||||
|
||||
const dc = (prop.dc?.value);
|
||||
const dc = Number(prop.dc?.value ?? 0);
|
||||
result.appendLog({
|
||||
name: getPropertyTitle(prop),
|
||||
value: `DC **${dc}**`,
|
||||
@@ -54,7 +60,7 @@ export default async function applySavingThrowProperty(
|
||||
}
|
||||
|
||||
// Each target makes the saving throw
|
||||
const save = getFromScope(prop.stat, getVariables(targetId));
|
||||
const save = prop.stat ? getFromScope(prop.stat, getVariables(targetId)) : undefined;
|
||||
|
||||
if (!save) {
|
||||
result.appendLog({
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
createTestCreature,
|
||||
getRandomIds,
|
||||
removeAllCreaturesAndProps,
|
||||
runActionById
|
||||
runActionById,
|
||||
TestCreature
|
||||
} from '/imports/api/engine/action/functions/actionEngineTest.testFn';
|
||||
|
||||
const [
|
||||
creatureId, trueToggleId, falseToggleId,
|
||||
] = getRandomIds(3);
|
||||
|
||||
const actionTestCreature = {
|
||||
const actionTestCreature: TestCreature = {
|
||||
_id: creatureId,
|
||||
props: [
|
||||
{
|
||||
|
||||
@@ -10,6 +10,20 @@ export default async function applyToggle(
|
||||
): Promise<void> {
|
||||
|
||||
const prop = task.prop;
|
||||
|
||||
if (prop.type !== 'toggle') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a toggle, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
if (!prop.condition) {
|
||||
result.appendLog({
|
||||
name: 'Toggle Error',
|
||||
value: 'toggle does not have a condition set',
|
||||
silenced: prop.silent,
|
||||
}, task.targetIds);
|
||||
return applyAfterTasksSkipChildren(action, prop, task.targetIds, inputProvider);
|
||||
}
|
||||
|
||||
await recalculateCalculation(prop.condition, action, 'reduce', inputProvider);
|
||||
if (prop.condition?.value) {
|
||||
return applyDefaultAfterPropTasks(action, prop, task.targetIds, inputProvider);
|
||||
|
||||
@@ -9,7 +9,12 @@ export default async function applyTriggerProperty(
|
||||
task: PropTask, action: EngineAction, result: TaskResult, userInput
|
||||
): Promise<void> {
|
||||
const prop = task.prop;
|
||||
const logContent: LogContent & { silenced: boolean } = {
|
||||
|
||||
if (prop.type !== 'trigger') {
|
||||
throw new Meteor.Error('wrong-property', `Expected a trigger, got ${prop.type} instead`);
|
||||
}
|
||||
|
||||
const logContent: LogContent & { silenced: boolean | undefined } = {
|
||||
name: getPropertyTitle(prop),
|
||||
silenced: prop.silent,
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export type DamagePropTask = BaseTask & {
|
||||
title?: string;
|
||||
operation: 'increment' | 'set';
|
||||
value: number;
|
||||
targetProp: CreatureProperty;
|
||||
targetProp: CreatureProperty | { name: string, };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -203,11 +203,10 @@ const updateLibraryNode = new ValidatedMethod({
|
||||
// We cannot change these fields with a simple update
|
||||
switch (path[0]) {
|
||||
case 'type':
|
||||
case 'order':
|
||||
case 'parent':
|
||||
case 'ancestors':
|
||||
case 'parentId':
|
||||
case 'root':
|
||||
case 'left':
|
||||
case 'right':
|
||||
case 'parentId':
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ const AdjustmentSchema = createPropertySchema({
|
||||
allowedValues: [
|
||||
'self',
|
||||
'target',
|
||||
],
|
||||
] as const,
|
||||
},
|
||||
// The stat this rolls applies to
|
||||
stat: {
|
||||
@@ -28,7 +28,7 @@ const AdjustmentSchema = createPropertySchema({
|
||||
},
|
||||
operation: {
|
||||
type: String,
|
||||
allowedValues: ['set', 'increment'],
|
||||
allowedValues: ['set', 'increment'] as const,
|
||||
defaultValue: 'increment',
|
||||
},
|
||||
// Prevent the property from showing up in the log
|
||||
|
||||
@@ -24,6 +24,11 @@ const CreatureTemplateSchema = createPropertySchema({
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.url,
|
||||
},
|
||||
// Prevent the property from showing up in the log
|
||||
silent: {
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyCreatureTemplateSchema = createPropertySchema({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import { TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
|
||||
import { InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
|
||||
import { Simplify } from 'type-fest';
|
||||
|
||||
const ErrorSchema = TypedSimpleSchema.from({
|
||||
message: {
|
||||
@@ -12,4 +13,6 @@ const ErrorSchema = TypedSimpleSchema.from({
|
||||
},
|
||||
});
|
||||
|
||||
export type ErrorSchemaType = Simplify<InferType<typeof ErrorSchema>>;
|
||||
|
||||
export default ErrorSchema;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema';
|
||||
import ErrorSchema, { ErrorSchemaType } from '/imports/api/properties/subSchemas/ErrorSchema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import ParseNode from '/imports/parser/parseTree/ParseNode';
|
||||
import { ConstantValueType } from '/imports/parser/parseTree/constant';
|
||||
@@ -15,9 +15,13 @@ export type CalculatedOnlyField = {
|
||||
proficiencyIds?: string[];
|
||||
unaffected?: ConstantValueType;
|
||||
parseNode?: ParseNode;
|
||||
parseError?: any;
|
||||
parseError?: ErrorSchemaType;
|
||||
hash?: number;
|
||||
errors?: any[];
|
||||
advantage?: number;
|
||||
disadvantage?: number;
|
||||
fail?: number;
|
||||
conditional?: string[];
|
||||
errors?: ErrorSchemaType[];
|
||||
}
|
||||
|
||||
export type CalculatedField = FieldToCalculate & CalculatedOnlyField;
|
||||
@@ -41,19 +45,19 @@ function computedOnlyField(field) {
|
||||
const schemaObj = {
|
||||
// The value (or calculation string) before any effects/proficiencies are applied or rolls made
|
||||
[`${field}.unaffected`]: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
type: SimpleSchema.oneOf(String, Number, Boolean),
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
// The value (or calculation string) after applying all effects
|
||||
[`${field}.value`]: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
type: SimpleSchema.oneOf(String, Number, Boolean),
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
// The value as a parse node, after applying all effects
|
||||
[`${field}.valueNode`]: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
type: Object,
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
@@ -167,7 +171,7 @@ function includeParentFields(field, schemaObj) {
|
||||
// This should rarely be used, since the other two will merge correctly when
|
||||
// uncomputed and computedOnly schemas are merged
|
||||
function computedField(field) {
|
||||
return computedField(field).extend(computedOnlyField(field));
|
||||
return fieldToCompute(field).extend(computedOnlyField(field));
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import { TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
|
||||
|
||||
export interface Shared {
|
||||
owner: string,
|
||||
readers: string[],
|
||||
writers: string[],
|
||||
public: boolean,
|
||||
readersCanCopy?: true,
|
||||
}
|
||||
import { InferType, TypedSimpleSchema } from '/imports/api/utility/TypedSimpleSchema';
|
||||
|
||||
const SharingSchema = TypedSimpleSchema.from({
|
||||
owner: {
|
||||
@@ -47,4 +39,6 @@ const SharingSchema = TypedSimpleSchema.from({
|
||||
},
|
||||
});
|
||||
|
||||
export type Shared = InferType<typeof SharingSchema>;
|
||||
|
||||
export default SharingSchema;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
export default class Context {
|
||||
errors: (Error | { type: string; message: string; })[];
|
||||
errors: { type: string; message: string }[];
|
||||
rolls: { number: number; diceSize: number; values: number[]; }[];
|
||||
options: { [key: string]: any; };
|
||||
|
||||
@@ -18,7 +18,10 @@ export default class Context {
|
||||
message: e,
|
||||
});
|
||||
} else {
|
||||
this.errors.push(e);
|
||||
this.errors.push({
|
||||
type: 'error',
|
||||
message: e.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user