Fix TypeScript errors in action engine

This commit is contained in:
Thaum Rystra
2025-05-02 15:38:18 +02:00
parent d42d2a724e
commit ae5a159e58
31 changed files with 198 additions and 93 deletions

View File

@@ -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>);
}
}
}

View File

@@ -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) {

View File

@@ -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[] = [
{

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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];

View File

@@ -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,

View File

@@ -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' }

View File

@@ -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: [
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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: [
{

View File

@@ -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;
}
});

View File

@@ -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: [
{

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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: [
{

View File

@@ -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);
}

View File

@@ -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: [
{

View File

@@ -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({

View File

@@ -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: [
{

View File

@@ -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);

View File

@@ -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,
}

View File

@@ -25,7 +25,7 @@ export type DamagePropTask = BaseTask & {
title?: string;
operation: 'increment' | 'set';
value: number;
targetProp: CreatureProperty;
targetProp: CreatureProperty | { name: string, };
};
}

View File

@@ -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;
}
},

View File

@@ -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

View File

@@ -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({

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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,
});
}
}