Lots of progress testing and fixing computation engine
This commit is contained in:
@@ -8,6 +8,7 @@ export default class CreatureComputation {
|
||||
this.propsById = {};
|
||||
this.propsByType = {};
|
||||
this.propsByVariableName = {};
|
||||
this.scope = {};
|
||||
this.props = properties;
|
||||
this.dependencyGraph = createGraph();
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ export default function computeInactiveStatus(node){
|
||||
const prop = node.node;
|
||||
if (isActive(prop)) return;
|
||||
// Unequipped items disable their children, but are not disabled themselves
|
||||
if (prop.type !== 'item'){
|
||||
// All notes do the same
|
||||
if (prop.type !== 'item' && prop.type !== 'note' ){
|
||||
prop.inactive = true;
|
||||
prop.deactivatedBySelf = true;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
*/
|
||||
export default function computeSlotQuantityFilled(node, dependencyGraph){
|
||||
let slot = node.node;
|
||||
if (slot.type !== 'propertySlot' || slot.type !== 'characterClass') return;
|
||||
if (slot.type !== 'propertySlot') return;
|
||||
slot.totalFilled = 0;
|
||||
node.children.forEach(child => {
|
||||
let childProp = child.node;
|
||||
dependencyGraph.addLink(slot._id, childProp._id, 'slotFill')
|
||||
if (childProp.type === 'slotFiller'){
|
||||
slot.totalFilled += child.slotQuantityFilled;
|
||||
dependencyGraph.addLink(slot._id, childProp._id, 'slotFill');
|
||||
if (
|
||||
childProp.type === 'slotFiller' &&
|
||||
Number.isFinite(childProp.slotQuantityFilled)
|
||||
){
|
||||
slot.totalFilled += childProp.slotQuantityFilled;
|
||||
} else {
|
||||
slot.totalFilled++;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export default function linkInventory(forest, dependencyGraph){
|
||||
const top = stack[stack.length - 1];
|
||||
const prop = top.node;
|
||||
if (prop._computationDetails.inventoryChildrenVisited){
|
||||
if (prop.type === 'container') containerStack.pop();
|
||||
stack.pop();
|
||||
handleProp(prop, containerStack, dependencyGraph);
|
||||
} else {
|
||||
@@ -28,7 +29,7 @@ export default function linkInventory(forest, dependencyGraph){
|
||||
|
||||
function handleProp(prop, containerStack, dependencyGraph){
|
||||
// Skip props that aren't part of the inventory
|
||||
if (prop.type !== 'inventory' && prop.type !== 'container') return;
|
||||
if (prop.type !== 'item' && prop.type !== 'container') return;
|
||||
// Determine if this property is carried, items are carried by default
|
||||
let carried = prop.type === 'container' ? prop.carried : true;
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ const linkDependenciesByType = {
|
||||
spell: linkResources,
|
||||
}
|
||||
|
||||
export default function linkTypeDependencies(dependencyGraph, prop){
|
||||
linkDependenciesByType[prop.type]?.(dependencyGraph, prop);
|
||||
export default function linkTypeDependencies(dependencyGraph, prop, computation){
|
||||
linkDependenciesByType[prop.type]?.(dependencyGraph, prop, computation);
|
||||
}
|
||||
|
||||
function linkClassLevel(dependencyGraph, prop){
|
||||
@@ -36,19 +36,20 @@ function linkVariableName(dependencyGraph, prop){
|
||||
}
|
||||
|
||||
function linkResources(dependencyGraph, prop, {propsById}){
|
||||
if (!prop.resources) return;
|
||||
prop.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId) return;
|
||||
const item = propsById[itemConsumed.itemId];
|
||||
if (!item.equipped) {
|
||||
if (!item || item.inactive){
|
||||
// Unlink if the item doesn't exist or is inactive
|
||||
itemConsumed.itemId = undefined;
|
||||
return;
|
||||
}
|
||||
if (!item) return;
|
||||
// none of these dependencies are computed, we can use them immediately
|
||||
prop.available = item.quantity;
|
||||
prop.itemName = item.name;
|
||||
prop.itemIcon = item.icon;
|
||||
prop.itemColor = item.color;
|
||||
itemConsumed.available = item.quantity;
|
||||
itemConsumed.itemName = item.name;
|
||||
itemConsumed.itemIcon = item.icon;
|
||||
itemConsumed.itemColor = item.color;
|
||||
dependencyGraph.addLink(prop._id, item._id, 'inventory');
|
||||
});
|
||||
prop.resources.attributesConsumed.forEach(attConsumed => {
|
||||
|
||||
@@ -5,11 +5,11 @@ import applyFnToKey from '/imports/api/creature/computation/newEngine/utility/ap
|
||||
import { get } from 'lodash';
|
||||
|
||||
export default function parseCalculationFields(prop, schemas){
|
||||
parseInlineCalculationFields(prop, schemas);
|
||||
parseDirectCalculationFields(prop, schemas);
|
||||
discoverInlineCalculationFields(prop, schemas);
|
||||
parseAllCalculationFields(prop, schemas);
|
||||
}
|
||||
|
||||
function parseInlineCalculationFields(prop, schemas){
|
||||
function discoverInlineCalculationFields(prop, schemas){
|
||||
// For each key in the schema
|
||||
schemas[prop.type]._schemaKeys.forEach( key => {
|
||||
// That ends in .inlineCalculations
|
||||
@@ -35,7 +35,7 @@ function parseInlineCalculationFields(prop, schemas){
|
||||
});
|
||||
}
|
||||
|
||||
function parseDirectCalculationFields(prop, schemas){
|
||||
function parseAllCalculationFields(prop, schemas){
|
||||
// For each key in the schema
|
||||
schemas[prop.type]._schemaKeys.forEach( key => {
|
||||
// that ends in '.calculation'
|
||||
|
||||
@@ -4,8 +4,11 @@ import { unset } from 'lodash';
|
||||
export default function removeSchemaFields(schemas, prop){
|
||||
schemas.forEach(schema => {
|
||||
schema._schemaKeys.forEach(key => {
|
||||
// Skip object keys
|
||||
if (schema.getQuickTypeForKey(key) === 'object') return;
|
||||
// Skip object and array keys
|
||||
if (
|
||||
schema.getQuickTypeForKey(key) === 'object' ||
|
||||
schema.getQuickTypeForKey(key) === 'objectArray'
|
||||
) return;
|
||||
// Unset other computed only keys
|
||||
applyFnToKey(prop, key, unset)
|
||||
});
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
const bySelf = (propId, note) => assertDeactivatedBySelf(computation, propId, note);
|
||||
const byAncestor = (propId, note) => assertDeactivatedByAncestor(computation, propId, note);
|
||||
const active = (propId, note) => assertActive(computation, propId, note);
|
||||
|
||||
// Buffs
|
||||
bySelf('buffNotAppliedId');
|
||||
byAncestor('buffNotAppliedChildId');
|
||||
active('buffAppliedId');
|
||||
active('buffAppliedChildId');
|
||||
|
||||
// Items
|
||||
active('itemUnequippedId', 'Unequipped items should be active');
|
||||
byAncestor('itemUnequippedChildId', 'Children of unequipped items should be inactive');
|
||||
active('itemEquippedId');
|
||||
active('itemEquippedChildId');
|
||||
|
||||
// Spells
|
||||
active('spellPreparedId');
|
||||
active('spellPreparedChildId');
|
||||
active('spellAlwaysPreparedId');
|
||||
active('spellAlwaysPreparedChildId');
|
||||
bySelf('spellUnpreparedId');
|
||||
byAncestor('spellUnpreparedChildId');
|
||||
|
||||
// Notes
|
||||
active('NoteId', 'Notes should be active');
|
||||
byAncestor('NoteChildId', 'children of notes should always be inactive');
|
||||
}
|
||||
|
||||
function assertDeactivatedBySelf(computation, propId, note){
|
||||
const prop = computation.propsById[propId];
|
||||
assert.isTrue(prop.deactivatedBySelf, note);
|
||||
assert.isTrue(prop.inactive, 'The property should be inactive');
|
||||
}
|
||||
|
||||
function assertDeactivatedByAncestor(computation, propId, note){
|
||||
const prop = computation.propsById[propId];
|
||||
assert.isTrue(prop.deactivatedByAncestor, note);
|
||||
assert.isTrue(prop.inactive, 'The property should be inactive');
|
||||
}
|
||||
|
||||
function assertActive(computation, propId, note){
|
||||
const prop = computation.propsById[propId];
|
||||
assert.isNotTrue(prop.inactive, note);
|
||||
assert.isNotTrue(prop.deactivatedBySelf);
|
||||
assert.isNotTrue(prop.deactivatedBySelf);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
// Buffs
|
||||
clean({
|
||||
_id: 'buffNotAppliedId',
|
||||
type: 'buff',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'buffNotAppliedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'buffNotAppliedId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'buffAppliedId',
|
||||
type: 'buff',
|
||||
applied: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'buffAppliedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'buffAppliedId'}],
|
||||
}),
|
||||
// Items
|
||||
clean({
|
||||
_id: 'itemUnequippedId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemUnequippedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'itemUnequippedId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemEquippedId',
|
||||
type: 'item',
|
||||
equipped: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemEquippedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'itemEquippedId'}],
|
||||
}),
|
||||
// Spells
|
||||
clean({
|
||||
_id: 'spellPreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{id: 'charId'}],
|
||||
prepared: true,
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellPreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'spellPreparedId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellAlwaysPreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{id: 'charId'}],
|
||||
alwaysPrepared: true,
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellAlwaysPreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'spellAlwaysPreparedId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellUnpreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellUnpreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'spellUnpreparedId'}],
|
||||
}),
|
||||
// Notes
|
||||
clean({
|
||||
_id: 'NoteId',
|
||||
type: 'note',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'NoteChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'NoteId'}],
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,36 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const totalFilled = computation.propsById['slotId'].totalFilled;
|
||||
assert.equal(totalFilled, 4);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
// Slots
|
||||
clean({
|
||||
_id: 'slotId',
|
||||
type: 'propertySlot',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
// Children
|
||||
clean({
|
||||
_id: 'slotFillerId',
|
||||
type: 'slotFiller',
|
||||
slotQuantityFilled: 3,
|
||||
slotFillerType: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'slotId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'slotChildId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'slotId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'slotGrandchildId',
|
||||
type: 'effect',
|
||||
ancestors: [{id: 'charId'}, {id: 'slotId'}, {id: 'slotChildId'}],
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,74 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
assert.include(
|
||||
computation.propsById['conditionToggleChildId']._computationDetails.toggleAncestors,
|
||||
computation.propsById['conditionToggleId'],
|
||||
'Children of toggles should store a reference to their toggle ancestor'
|
||||
)
|
||||
assert.isTrue(
|
||||
!!hasLink('conditionToggleChildId', 'conditionToggleId'),
|
||||
'Children of the condition toggle should depend on it'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('conditionToggleGrandChildId', 'conditionToggleId'),
|
||||
'Descendants of the condition toggle should depend on it'
|
||||
);
|
||||
assert.isFalse(
|
||||
!!hasLink('disabledToggleId', 'disabledToggleChildId'),
|
||||
'The dependency should not be reversed'
|
||||
);
|
||||
assert.isFalse(
|
||||
!!hasLink('disabledToggleChildId', 'disabledToggleId'),
|
||||
'Children of disabled toggle should not depend on it'
|
||||
);
|
||||
assert.isFalse(
|
||||
!!hasLink('enabledToggleChildId', 'enabledToggleId'),
|
||||
'Children of enabled toggle should not depend on it'
|
||||
);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'enabledToggleId',
|
||||
type: 'toggle',
|
||||
enabled: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'disabledToggleId',
|
||||
type: 'toggle',
|
||||
disabled: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleId',
|
||||
type: 'toggle',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
// Children
|
||||
clean({
|
||||
_id: 'enabledToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'enabledToggleId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'disabledToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'disabledToggleId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'conditionToggleId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleGrandChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'conditionToggleId'}, {id: 'conditionToggleChildId'}],
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,54 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
assert.isTrue(
|
||||
!!hasLink('childId', 'spellListId'),
|
||||
'Ancestor references of parent in inline calculations should create dependency'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('grandchildId', 'spellListId'),
|
||||
'References to higher ancestor should create dependency'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('grandchildId', 'strength'),
|
||||
'Variable references create dependencies'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('grandchildId', 'wisdom'),
|
||||
'Variable references create dependencies even if the attributes don\'t exist'
|
||||
);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'spellListId',
|
||||
type: 'spellList',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childId',
|
||||
type: 'spell',
|
||||
description: {
|
||||
text: 'DC {#spellList.dc} save or suck'
|
||||
},
|
||||
ancestors: [{id: 'charId'}, {id: 'spellListId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'grandchildId',
|
||||
type: 'savingThrow',
|
||||
dc: {
|
||||
calculation: '#spellList.dc + strength + wisdom.modifier'
|
||||
},
|
||||
ancestors: [{id: 'charId'}, {id: 'spellListId'}, {id: 'childId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'strengthId',
|
||||
type: 'attribute',
|
||||
variableName: 'strength',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,81 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
|
||||
assert.isTrue(
|
||||
!!hasLink('weightEquipment', 'equippedAttunedItemId'),
|
||||
'weight of equipment depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('valueEquipment', 'equippedAttunedItemId'),
|
||||
'value of equipment depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('weightTotal', 'equippedAttunedItemId'),
|
||||
'weightTotal depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('valueTotal', 'equippedAttunedItemId'),
|
||||
'valueTotal depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('weightCarried', 'equippedAttunedItemId'),
|
||||
'weightCarried depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('valueCarried', 'equippedAttunedItemId'),
|
||||
'valueCarried depends on equipped items'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('weightCarried', 'containerId'),
|
||||
'weightCarried depends on top level containers'
|
||||
);
|
||||
assert.isTrue(
|
||||
!!hasLink('valueCarried', 'containerId'),
|
||||
'valueCarried depends on top level containers'
|
||||
);
|
||||
assert.isFalse(
|
||||
!!hasLink('weightCarried', 'childContainerId'),
|
||||
'weightCarried does not depend on nested containers'
|
||||
);
|
||||
assert.isFalse(
|
||||
!!hasLink('valueCarried', 'childContainerId'),
|
||||
'valueCarried does not depend on nested containers'
|
||||
);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'equippedAttunedItemId',
|
||||
type: 'item',
|
||||
equipped: true,
|
||||
attuned: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'containerId',
|
||||
type: 'container',
|
||||
carried: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childContainerId',
|
||||
type: 'container',
|
||||
carried: true,
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childItemId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'grandchildItemId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}, {id: 'childContainerId'}],
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const getLink = computation.dependencyGraph.hasLink;
|
||||
const getNode = computation.dependencyGraph.getNode;
|
||||
|
||||
assert.equal(
|
||||
getLink('strength', 'strengthId').data, 'definition',
|
||||
'Links variable names to props that define them'
|
||||
);
|
||||
assert.exists(
|
||||
getNode('strength'),
|
||||
'Creates variable name nodes when attributes define them'
|
||||
);
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'strengthId',
|
||||
type: 'attribute',
|
||||
variableName: 'strength',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
];
|
||||
@@ -82,14 +82,14 @@ export function buildComputationFromProps(properties){
|
||||
// Walk the property trees computing things that need to be inherited
|
||||
walkDown(forest, node => {
|
||||
computeInactiveStatus(node);
|
||||
computeToggleDependencies(node);
|
||||
computeSlotQuantityFilled(node);
|
||||
computeToggleDependencies(node, dependencyGraph);
|
||||
computeSlotQuantityFilled(node, dependencyGraph);
|
||||
});
|
||||
|
||||
// Link the inventory dependencies
|
||||
linkInventory(forest, dependencyGraph);
|
||||
|
||||
// Link functions that require the above to be complete
|
||||
// Link functions that require the above to be complete
|
||||
properties.forEach(prop => {
|
||||
linkTypeDependencies(dependencyGraph, prop, computation);
|
||||
linkCalculationDependencies(dependencyGraph, prop, computation);
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { buildComputationFromProps } from './buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import computeInactiveStatus from './buildComputation/tests/computeInactiveStatus.testFn.js';
|
||||
import computeSlotQuantityFilled from './buildComputation/tests/computeSlotQuantityFilled.testFn.js';
|
||||
import computeToggleDependencies from './buildComputation/tests/computeToggleDependencies.testFn.js';
|
||||
import linkCalculationDependencies from './buildComputation/tests/linkCalculationDependencies.testFn.js';
|
||||
import linkInventory from './buildComputation/tests/linkInventory.testFn.js';
|
||||
import linkTypeDependencies from './buildComputation/tests/linkTypeDependencies.testFn.js';
|
||||
|
||||
describe('buildComputation', function(){
|
||||
it('Builds something at all', function(){
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
assert.exists(computation);
|
||||
});
|
||||
it('Computes inactive status', computeInactiveStatus);
|
||||
it('Computes slot fill quantity', computeSlotQuantityFilled);
|
||||
it('Links toggle dependencies', computeToggleDependencies);
|
||||
it('Links calculation dependencies', linkCalculationDependencies);
|
||||
it('Links inventory stats', linkInventory);
|
||||
it('Links type dependencies', linkTypeDependencies);
|
||||
});
|
||||
|
||||
var testProperties = [
|
||||
|
||||
@@ -6,6 +6,7 @@ export default function computeAction(graph, node, scope){
|
||||
prop.usesLeft = prop.uses.value - (prop.usesUsed || 0);
|
||||
}
|
||||
computeResources(graph, node, scope);
|
||||
if (!prop.resources) return;
|
||||
prop.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId) return;
|
||||
if (itemConsumed.available < itemConsumed.quantity.value){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export default function computeResources(graph, node, scope){
|
||||
const prop = node.data;
|
||||
prop.resources.attributesConsumed.forEach(attConsumed => {
|
||||
const resources = node.data?.resources;
|
||||
if (!resources) return;
|
||||
resources.attributesConsumed.forEach(attConsumed => {
|
||||
if (!attConsumed.variableName) return;
|
||||
const att = scope[attConsumed.variableName];
|
||||
attConsumed.available = att.value;
|
||||
|
||||
@@ -9,14 +9,13 @@ export default function computeVariable(graph, node, scope){
|
||||
if (!node.data) node.data = {};
|
||||
aggregateLinks(graph, node);
|
||||
combineAggregations(node, scope);
|
||||
if (node.definingProp){
|
||||
if (node.data.definingProp){
|
||||
// Add the defining variable to the scope
|
||||
scope[node.id] = node.definingProp
|
||||
scope[node.id] = node.data.definingProp
|
||||
} else {
|
||||
// Otherwise add an implicit variable to the scope
|
||||
scope[node.id] = computeImplicitVariable(node, scope);
|
||||
}
|
||||
console.log('computed variable ', node);
|
||||
}
|
||||
|
||||
function aggregateLinks(graph, node){
|
||||
|
||||
@@ -2,7 +2,6 @@ import getAggregatorResult from './getAggregatorResult.js';
|
||||
|
||||
export default function computeVariableAsAttribute(node, prop, scope){
|
||||
let result = getAggregatorResult(node);
|
||||
console.log('computing variable as attribure ', node);
|
||||
prop.total = result;
|
||||
prop.value = prop.total - (prop.damage || 0);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CompilationContext } from '/imports/parser/parser.js';
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
||||
|
||||
export default function computeCalculations(node, scope){
|
||||
if (!node.data) return;
|
||||
@@ -17,7 +18,12 @@ function evaluateCalculation(calculation, scope){
|
||||
const parseNode = calculation._parsedCalculation;
|
||||
const fn = calculation._parseLevel;
|
||||
const calculationScope = {...calculation._localScope, ...scope};
|
||||
calculation.value = parseNode[fn](calculationScope, context);
|
||||
const resultNode = parseNode[fn](calculationScope, context);
|
||||
if (resultNode instanceof ConstantNode){
|
||||
calculation.value = resultNode.value;
|
||||
} else {
|
||||
calculation.value = NaN;
|
||||
}
|
||||
calculation.errors = context.errors;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { buildComputationFromProps } from '/imports/api/creature/computation/newEngine/buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation.js';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
|
||||
export default function(){
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
|
||||
const prop = computation.propsById['actionId'];
|
||||
assert.equal(prop.summary.value, 'test summary 3 without referencing anything 7');
|
||||
assert.equal(prop.description.value, 'test description 12 with reference 0.25 prop');
|
||||
assert.equal(prop.uses.value, 7);
|
||||
assert.equal(prop.usesLeft, 2);
|
||||
|
||||
const itemConsumed = prop.resources.itemsConsumed[0];
|
||||
assert.equal(itemConsumed.quantity.value, 3);
|
||||
assert.equal(itemConsumed.available, 27);
|
||||
assert.equal(itemConsumed.itemName, 'Arrow');
|
||||
assert.equal(itemConsumed.itemIcon, 'itemIcon');
|
||||
assert.equal(itemConsumed.itemColor, 'itemColor');
|
||||
|
||||
const attConsumed = prop.resources.attributesConsumed[0];
|
||||
assert.equal(attConsumed.quantity.value, 4);
|
||||
assert.equal(attConsumed.available, 9);
|
||||
assert.equal(attConsumed.statId, 'resourceVarId');
|
||||
assert.equal(attConsumed.statName, 'Resource Var');
|
||||
}
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'actionId',
|
||||
type: 'action',
|
||||
ancestors: [{id: 'charId'}],
|
||||
summary: {
|
||||
text: 'test summary {1 + 2} without referencing anything {3 + 4}',
|
||||
},
|
||||
description: {
|
||||
text: 'test description {inlineRef * 2} with reference {1/4} prop',
|
||||
},
|
||||
resources: {
|
||||
itemsConsumed: [{
|
||||
_id: 'itemConsumedId',
|
||||
itemId: 'arrowId',
|
||||
tag: 'arrow',
|
||||
quantity: {
|
||||
calculation: 'itemConsumedQuantity',
|
||||
},
|
||||
}],
|
||||
attributesConsumed: [{
|
||||
_id: 'attConsumedId',
|
||||
variableName: 'resourceVar',
|
||||
quantity: {
|
||||
calculation: 'resourceConsumedQuantity'
|
||||
}
|
||||
}],
|
||||
},
|
||||
uses: {
|
||||
calculation: 'nonExistantProperty + 7',
|
||||
},
|
||||
usesUsed: 5,
|
||||
}),
|
||||
clean({
|
||||
_id: 'numItemsConumedId',
|
||||
type: 'attribute',
|
||||
variableName: 'itemConsumedQuantity',
|
||||
baseValue: {
|
||||
calculation: '3',
|
||||
},
|
||||
}),
|
||||
clean({
|
||||
_id: 'numResourceConumedId',
|
||||
type: 'attribute',
|
||||
variableName: 'resourceConsumedQuantity',
|
||||
baseValue: {
|
||||
calculation: '4',
|
||||
},
|
||||
}),
|
||||
clean({
|
||||
_id: 'resourceVarId',
|
||||
name: 'Resource Var',
|
||||
type: 'attribute',
|
||||
variableName: 'resourceVar',
|
||||
baseValue: {
|
||||
calculation: '9',
|
||||
},
|
||||
}),
|
||||
clean({
|
||||
_id: 'inlineRefResourceId',
|
||||
type: 'attribute',
|
||||
variableName: 'inlineRef',
|
||||
baseValue: {
|
||||
calculation: '1 + 5',
|
||||
},
|
||||
}),
|
||||
clean({
|
||||
_id: 'arrowId',
|
||||
type: 'item',
|
||||
name: 'Arrow',
|
||||
quantity: 27,
|
||||
icon: 'itemIcon',
|
||||
color: 'itemColor',
|
||||
}),
|
||||
];
|
||||
@@ -5,10 +5,14 @@ import computeByType from '/imports/api/creature/computation/newEngine/computeCo
|
||||
export default function computeCreatureComputation(computation){
|
||||
const stack = [];
|
||||
// Computation scope of {variableName: prop}
|
||||
const scope = {};
|
||||
const scope = computation.scope;
|
||||
const graph = computation.dependencyGraph;
|
||||
// Add all nodes to the stack
|
||||
graph.forEachNode(node => stack.push(node));
|
||||
graph.forEachNode(node => {
|
||||
node._visited = false;
|
||||
node._visitedChildren = false;
|
||||
stack.push(node)
|
||||
});
|
||||
// Depth first traversal of nodes
|
||||
while (stack.length){
|
||||
let top = stack[stack.length - 1];
|
||||
|
||||
@@ -2,15 +2,15 @@ import computeCreatureComputation from './computeCreatureComputation.js';
|
||||
import { buildComputationFromProps } from './buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import computeAction from './computeComputation/tests/computeAction.testFn.js';
|
||||
|
||||
describe('Compute compuation', function(){
|
||||
it('Computes something at all', function(){
|
||||
console.time('compute');
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
computeCreatureComputation(computation);
|
||||
console.timeEnd('compute');
|
||||
assert.exists(computation);
|
||||
});
|
||||
it('Computes actions', computeAction);
|
||||
});
|
||||
|
||||
var testProperties = [
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
export default function cleanProp(prop){
|
||||
let schema = CreatureProperties.simpleSchema(prop);
|
||||
return schema.clean(prop);
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import {
|
||||
ResourcesSchema,
|
||||
ResourcesComputedOnlySchema,
|
||||
ResourcesComputedSchema,
|
||||
} from '/imports/api/properties/subSchemas/ResourcesSchema.js';
|
||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
SimpleSchema.extendOptions(['parseLevel']);
|
||||
|
||||
@@ -45,11 +41,6 @@ let ActionSchema = createPropertySchema({
|
||||
'multipleTargets',
|
||||
],
|
||||
},
|
||||
// Resources schema changes for between standard, computed, and computedOnly
|
||||
resources: {
|
||||
type: ResourcesSchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
// Calculation of how many times this action can be used
|
||||
uses: {
|
||||
type: 'fieldToCompute',
|
||||
@@ -66,6 +57,61 @@ let ActionSchema = createPropertySchema({
|
||||
allowedValues: ['longRest', 'shortRest'],
|
||||
optional: true,
|
||||
},
|
||||
// Resources
|
||||
resources: {
|
||||
type: Object,
|
||||
defaultValue: {},
|
||||
},
|
||||
'resources.itemsConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.itemsConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.itemsConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.itemsConsumed.$.tag': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.quantity': {
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemId': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.attributesConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.attributesConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.attributesConsumed.$.variableName': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
},
|
||||
'resources.attributesConsumed.$.quantity': {
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyActionSchema = createPropertySchema({
|
||||
@@ -77,10 +123,6 @@ const ComputedOnlyActionSchema = createPropertySchema({
|
||||
type: 'computedOnlyInlineCalculationField',
|
||||
optional: true,
|
||||
},
|
||||
resources: {
|
||||
type: ResourcesComputedOnlySchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
// True if the uses left is zero, or any item or attribute consumed is
|
||||
// insufficient
|
||||
insufficientResources: {
|
||||
@@ -96,16 +138,70 @@ const ComputedOnlyActionSchema = createPropertySchema({
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
// Resources
|
||||
resources: {
|
||||
type: Object,
|
||||
defaultValue: {},
|
||||
},
|
||||
'resources.itemsConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.itemsConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.itemsConsumed.$.available': {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.quantity': {
|
||||
type: 'computedOnlyField',
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemName': {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.name,
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemIcon': {
|
||||
type: storedIconsSchema,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.icon,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemColor': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.color,
|
||||
},
|
||||
'resources.attributesConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.attributesConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.attributesConsumed.$.quantity': {
|
||||
type: 'computedOnlyField',
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.available': {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.statId': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.statName': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedActionSchema = new SimpleSchema()
|
||||
.extend(ActionSchema)
|
||||
.extend(ComputedOnlyActionSchema)
|
||||
.extend({
|
||||
resources: {
|
||||
type: ResourcesComputedSchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
});
|
||||
.extend(ComputedOnlyActionSchema);
|
||||
|
||||
export { ActionSchema, ComputedOnlyActionSchema, ComputedActionSchema};
|
||||
|
||||
@@ -3,7 +3,7 @@ import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||
|
||||
let ClassLevelSchema = createPropertySchema({
|
||||
const ClassLevelSchema = createPropertySchema({
|
||||
name: {
|
||||
type: String,
|
||||
optional: true,
|
||||
@@ -27,4 +27,6 @@ let ClassLevelSchema = createPropertySchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { ClassLevelSchema };
|
||||
const ComputedOnlyClassLevelSchema = new SimpleSchema({});
|
||||
|
||||
export { ClassLevelSchema, ComputedOnlyClassLevelSchema };
|
||||
|
||||
@@ -85,4 +85,6 @@ function parseString(string, fn = 'compile'){
|
||||
return {result, context}
|
||||
}
|
||||
|
||||
export { ConstantSchema };
|
||||
const ComputedOnlyConstantSchema = new SimpleSchema({});
|
||||
|
||||
export { ConstantSchema, ComputedOnlyConstantSchema };
|
||||
|
||||
@@ -49,4 +49,6 @@ let DamageMultiplierSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { DamageMultiplierSchema };
|
||||
const ComputedOnlyDamageMultiplierSchema = new SimpleSchema({});
|
||||
|
||||
export { DamageMultiplierSchema, ComputedOnlyDamageMultiplierSchema };
|
||||
|
||||
@@ -10,4 +10,6 @@ let FolderSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { FolderSchema };
|
||||
const ComputedOnlyFolderSchema = new SimpleSchema({});
|
||||
|
||||
export { FolderSchema, ComputedOnlyFolderSchema };
|
||||
|
||||
@@ -26,4 +26,6 @@ let ProficiencySchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { ProficiencySchema };
|
||||
const ComputedOnlyProficiencySchema = new SimpleSchema({});
|
||||
|
||||
export { ProficiencySchema, ComputedOnlyProficiencySchema };
|
||||
|
||||
@@ -58,4 +58,6 @@ let ReferenceSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { ReferenceSchema };
|
||||
const ComputedOnlyReferenceSchema = new SimpleSchema({});
|
||||
|
||||
export { ReferenceSchema, ComputedOnlyReferenceSchema };
|
||||
|
||||
@@ -39,4 +39,6 @@ let SlotFillerSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export { SlotFillerSchema };
|
||||
const ComputedOnlySlotFillerSchema = new SimpleSchema({});
|
||||
|
||||
export { SlotFillerSchema, ComputedOnlySlotFillerSchema };
|
||||
|
||||
@@ -5,23 +5,23 @@ import { ComputedOnlyAttackSchema } from '/imports/api/properties/Attacks.js';
|
||||
import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||
import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||
import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes.js';
|
||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { ConstantSchema } from '/imports/api/properties/Constants.js';
|
||||
import { ComputedOnlyClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { ComputedOnlyConstantSchema } from '/imports/api/properties/Constants.js';
|
||||
import { ComputedOnlyContainerSchema } from '/imports/api/properties/Containers.js';
|
||||
import { ComputedOnlyDamageSchema } from '/imports/api/properties/Damages.js';
|
||||
import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js';
|
||||
import { ComputedOnlyDamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js';
|
||||
import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects.js';
|
||||
import { ComputedOnlyFeatureSchema } from '/imports/api/properties/Features.js';
|
||||
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||
import { ComputedOnlyFolderSchema } from '/imports/api/properties/Folders.js';
|
||||
import { ComputedOnlyItemSchema } from '/imports/api/properties/Items.js';
|
||||
import { ComputedOnlyNoteSchema } from '/imports/api/properties/Notes.js';
|
||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
||||
import { ComputedOnlyProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||
import { ComputedOnlyReferenceSchema } from '/imports/api/properties/References.js';
|
||||
import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls.js';
|
||||
import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
|
||||
import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js';
|
||||
import { ComputedOnlySlotSchema } from '/imports/api/properties/Slots.js';
|
||||
import { SlotFillerSchema } from '/imports/api/properties/SlotFillers.js';
|
||||
import { ComputedOnlySlotFillerSchema } from '/imports/api/properties/SlotFillers.js';
|
||||
import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells.js';
|
||||
import { ComputedOnlySpellListSchema } from '/imports/api/properties/SpellLists.js';
|
||||
import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles.js';
|
||||
@@ -33,23 +33,23 @@ const propertySchemasIndex = {
|
||||
attribute: ComputedOnlyAttributeSchema,
|
||||
buff: ComputedOnlyBuffSchema,
|
||||
characterClass: ComputedOnlyClassSchema,
|
||||
classLevel: ClassLevelSchema,
|
||||
constant: ConstantSchema,
|
||||
classLevel: ComputedOnlyClassLevelSchema,
|
||||
constant: ComputedOnlyConstantSchema,
|
||||
container: ComputedOnlyContainerSchema,
|
||||
damage: ComputedOnlyDamageSchema,
|
||||
damageMultiplier: DamageMultiplierSchema,
|
||||
damageMultiplier: ComputedOnlyDamageMultiplierSchema,
|
||||
effect: ComputedOnlyEffectSchema,
|
||||
feature: ComputedOnlyFeatureSchema,
|
||||
folder: FolderSchema,
|
||||
folder: ComputedOnlyFolderSchema,
|
||||
item: ComputedOnlyItemSchema,
|
||||
note: ComputedOnlyNoteSchema,
|
||||
proficiency: ProficiencySchema,
|
||||
proficiency: ComputedOnlyProficiencySchema,
|
||||
propertySlot: ComputedOnlySlotSchema,
|
||||
reference: ReferenceSchema,
|
||||
reference: ComputedOnlyReferenceSchema,
|
||||
roll: ComputedOnlyRollSchema,
|
||||
savingThrow: ComputedOnlySavingThrowSchema,
|
||||
skill: ComputedOnlySkillSchema,
|
||||
slotFiller: SlotFillerSchema,
|
||||
slotFiller: ComputedOnlySlotFillerSchema,
|
||||
spellList: ComputedOnlySpellListSchema,
|
||||
spell: ComputedOnlySpellSchema,
|
||||
toggle: ComputedOnlyToggleSchema,
|
||||
|
||||
@@ -5,27 +5,20 @@ import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
// Get schemas that apply fields directly so they can be gracefully extended
|
||||
// because {type: Schema} fields can't be extended
|
||||
function fieldToCompute(field){
|
||||
return new SimpleSchema({
|
||||
// The object should already be set, but set again just in case
|
||||
[field]: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
const schemaObj = {
|
||||
// This is required, if we don't have a calculation delete the whole object
|
||||
[`${field}.calculation`]: {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
});
|
||||
}
|
||||
// If the field is an array, we need to include those fields as well
|
||||
includeParentFields(field, schemaObj);
|
||||
return new SimpleSchema(schemaObj);
|
||||
}
|
||||
|
||||
function computedOnlyField(field){
|
||||
return new SimpleSchema({
|
||||
// The object should already be set, but set again just in case
|
||||
[field]: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
const schemaObj = {
|
||||
[`${field}.value`]: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
optional: true,
|
||||
@@ -38,6 +31,30 @@ function computedOnlyField(field){
|
||||
[`${field}.errors.$`]:{
|
||||
type: ErrorSchema,
|
||||
},
|
||||
}
|
||||
includeParentFields(field, schemaObj);
|
||||
return new SimpleSchema(schemaObj);
|
||||
}
|
||||
|
||||
// We must include parent array and object fields for the schema to be valid
|
||||
function includeParentFields(field, schemaObj){
|
||||
const splitField = field.split('.');
|
||||
if (splitField.length === 1){
|
||||
schemaObj[field] = {type: Object};
|
||||
return;
|
||||
}
|
||||
let key = '';
|
||||
splitField.push('');
|
||||
splitField.forEach(value => {
|
||||
if (key){
|
||||
if (value === '$'){
|
||||
schemaObj[key] = {type: Array};
|
||||
} else {
|
||||
schemaObj[key] = {type: Object};
|
||||
}
|
||||
key += '.';
|
||||
}
|
||||
key += value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user