From 55bca633fc575050e7b54758c02bd5b9c7253298 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 9 Sep 2021 13:47:41 +0200 Subject: [PATCH] Substantial progress on rebuilding computation engine --- .../buildComputation/computeInactiveStatus.js | 24 ++ .../buildComputation/computeInventory.js | 98 +++++++ .../computeSlotQuantityFilled.js | 17 ++ .../computeToggleDependencies.js | 16 ++ .../linkCalculationDependencies.js | 24 ++ .../buildComputation/linkTypeDependencies.js | 40 +++ .../parseCalculationFields.js | 35 +++ .../newEngine/buildCreatureComputation.js | 126 +++++++++ .../computation/newEngine/computeCreature.js | 239 ------------------ .../newEngine/{ => utility}/applyFnToKey.js | 0 .../{ => utility}/findAncestorByType.js | 0 .../computation/newEngine/utility/walkdown.js | 8 + app/imports/api/properties/Containers.js | 10 + .../api/properties/DamageMultipliers.js | 3 +- app/imports/api/properties/Damages.js | 4 +- app/imports/api/properties/SlotFillers.js | 1 - 16 files changed, 401 insertions(+), 244 deletions(-) create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/computeSlotQuantityFilled.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/computeToggleDependencies.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/linkCalculationDependencies.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/linkTypeDependencies.js create mode 100644 app/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js create mode 100644 app/imports/api/creature/computation/newEngine/buildCreatureComputation.js delete mode 100644 app/imports/api/creature/computation/newEngine/computeCreature.js rename app/imports/api/creature/computation/newEngine/{ => utility}/applyFnToKey.js (100%) rename app/imports/api/creature/computation/newEngine/{ => utility}/findAncestorByType.js (100%) create mode 100644 app/imports/api/creature/computation/newEngine/utility/walkdown.js diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js b/app/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js new file mode 100644 index 00000000..fdf651d8 --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js @@ -0,0 +1,24 @@ +import walkDown from '/imports/api/creature/computation/newEngine/utility/walkdown.js'; + +export default function computeInactiveStatus(node){ + const prop = node.node; + if (isActive(prop)) return; + prop.inactive = true; + prop.deactivatedBySelf = true; + // Mark children as inactive due to ancestor + walkDown(node.children, child => { + child.node.inactive = true; + child.node.deactivatedByAncestor = true; + }); +} + +function isActive(prop){ + if (prop.disabled) return false; + switch (prop.type){ + case 'buff': return !!prop.applied; + case 'item': return !!prop.equipped; + case 'spell': return !!prop.prepared || !!prop.alwaysPrepared; + case 'note': return false; + default: return true; + } +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js b/app/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js new file mode 100644 index 00000000..9e96c70e --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js @@ -0,0 +1,98 @@ +/** + * Performs a depth first traversal of the character tree, summing the container + * and inventory contents on the way up the tree + */ +export default function computeInventory(forest, dependencyGraph){ + const data = { + weightTotal: 0, + weightEquipment: 0, + weightCarried: 0, + valueTotal: 0, + valueEquipment: 0, + valueCarried: 0, + itemsAttuned: 0, + }; + // The stack of properties to still navigate + const stack = [...forest]; + // The current containers we are inside of + const containerStack = []; + + while(stack.length){ + const top = stack[stack.length - 1]; + const prop = top.node; + if (prop._computationDetails.inventoryChildrenVisited){ + stack.pop(); + handleProp(prop, containerStack, data, dependencyGraph); + } else { + // Add all containers to the stack when we first visit them + if (prop.type === 'container'){ + containerStack.push(top.node); + setDefaultContainerData(prop); + } + // Push children onto the stack and mark this as children are visited + stack.push(...top.children); + prop._computationDetails.inventoryChildrenVisited = true; + } + } + // Store all the computed values on the dependency graph variables + for (let key in data){ + dependencyGraph.addNode(key, {engineValue: data[key]}); + } +} + +function setDefaultContainerData(container){ + container.contentsWeight = 0; + container.carriedWeight = 0; + container.contentsValue = 0; + container.carriedValue = 0; +} + +function handleProp(prop, containerStack, data, dependencyGraph){ + // Determine if this property is carried, items are carried by default + let carried = prop.type === 'container' ? prop.carried : true; + + // Weight and value for this property + const weight = (prop.weight || 0) + (prop.contentsWeight || 0); + const carriedWeight = (prop.weight || 0) + (prop.carriedWeight || 0); + const value = (prop.value || 0) + (prop.value || 0); + const carriedValue = (prop.value || 0) + (prop.carriedValue || 0); + + // Sum the item-specific data + if (prop.type === 'item'){ + dependencyGraph.addLink('itemsAttuned', prop._id); + if (prop.attuned) data.itemsAttuned += 1; + if (prop.equipped){ + dependencyGraph.addLink('weightEquipment', prop._id); + data.weightEquipment += weight; + dependencyGraph.addLink('valueEquipment', prop._id); + data.valueEquipment += value; + } + } + + // Get the parent container + const container = containerStack[containerStack.length - 1]; + + if (container){ + // The container depends on this prop for its contents data + dependencyGraph.addLink(container._id, prop._id); + // Add this property's weights and values to the container + if (!container.weightless){ + container.contentsWeight += weight; + if (carried) container.carriedWeight += carriedWeight; + } + container.contentsValue += value; + if (carried) container.carriedValue += carriedValue; + } else { + // There is no parent container, add weights/value to the character data + dependencyGraph.addLink('weightTotal', prop._id); + data.weightTotal += weight; + dependencyGraph.addLink('valueTotal', prop._id); + data.valueTotal += value; + if (carried){ + dependencyGraph.addLink('weightCarried', prop._id); + data.weightCarried += carriedWeight; + dependencyGraph.addLink('valueCarried', prop._id); + data.valueCarried += carriedValue; + } + } +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/computeSlotQuantityFilled.js b/app/imports/api/creature/computation/newEngine/buildComputation/computeSlotQuantityFilled.js new file mode 100644 index 00000000..dbaa9fda --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/computeSlotQuantityFilled.js @@ -0,0 +1,17 @@ +/** + * Only computes `totalFilled`, need to compute `quantityExpected.value` + * before `spacesLeft` can be computed + */ +export default function computeSlotQuantityFilled(node, dependencyGraph){ + let slot = node.node; + slot.totalFilled = 0; + node.children.forEach(child => { + let childProp = child.node; + dependencyGraph.addLink(slot._id, childProp._id) + if (childProp.type === 'slotFiller'){ + slot.totalFilled += child.slotQuantityFilled; + } else { + slot.totalFilled++; + } + }); +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/computeToggleDependencies.js b/app/imports/api/creature/computation/newEngine/buildComputation/computeToggleDependencies.js new file mode 100644 index 00000000..882b28c7 --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/computeToggleDependencies.js @@ -0,0 +1,16 @@ +import walkDown from '/imports/api/creature/computation/newEngine/utility/walkdown.js'; + +export default function computeToggleDependencies(node, dependencyGraph){ + const prop = node.node; + // Only for toggles that aren't inactive and aren't set to enabled or disabled + if ( + prop.inactive || + prop.type !== 'toggle' || + prop.disabled || + prop.enabled + ) return; + walkDown(node.children, child => { + child.node._computationDetails.toggleAncestors.push(prop._id); + dependencyGraph.addLink(child.node._id, prop._id, prop.condition); + }); +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/linkCalculationDependencies.js b/app/imports/api/creature/computation/newEngine/buildComputation/linkCalculationDependencies.js new file mode 100644 index 00000000..075fdb2e --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/linkCalculationDependencies.js @@ -0,0 +1,24 @@ +import SymbolNode from '/imports/parser/parseTree/SymbolNode.js'; +import AccessorNode from '/imports/parser/parseTree/AccessorNode.js'; +import findAncestorByType from 'imports/api/creature/computation/newEngine/utility/findAncestorByType.js'; + +export default function linkCalculationDependencies(dependencyGraph, prop, propsById){ + prop._computationDetails.calculations.forEach(calcObj => { + // Traverse the parsed calculation looking for variable names + calcObj._parsedCalculation.travese(node => { + if (node instanceof SymbolNode || node instanceof AccessorNode){ + // Link ancestor references as direct property dependencies + if (node.name[0] === '#'){ + let ancestorProp = findAncestorByType( + prop, node.name.slice(1), propsById + ); + if (!ancestorProp) return; + dependencyGraph.addLink(prop._id, ancestorProp._id, calcObj); + } else { + // Link variable name references as variable dependencies + dependencyGraph.addLink(prop._id, node.name, calcObj); + } + } + }); + }); +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/linkTypeDependencies.js b/app/imports/api/creature/computation/newEngine/buildComputation/linkTypeDependencies.js new file mode 100644 index 00000000..38e640ea --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/linkTypeDependencies.js @@ -0,0 +1,40 @@ +const linkDependenciesByType = { + attribute: linkVariableName, + classLevel: linkVariableName, + constant: linkVariableName, + damageMultiplier: linkDamageMultiplier, + proficiency: linkStats, + effect: linkStats, + skill: linkSkill, +} + +export default function linkTypeDependencies(dependencyGraph, prop){ + linkDependenciesByType[prop.type]?.(prop); +} + +function linkVariableName(dependencyGraph, prop){ + // The variableName of the prop depends on the prop + if (prop.variableName){ + dependencyGraph.addLink(prop.variableName, prop._id); + } +} + +function linkDamageMultiplier(dependencyGraph, prop){ + prop.damageTypes.forEach(damageType => { + dependencyGraph.addLink(`${damageType}Multiplier`, prop._id); + }); +} + +function linkStats(dependencyGraph, prop){ + // The stats a prop references depend on that prop + prop.stats.forEach(variableName => { + if (!variableName) return; + dependencyGraph.addLink(variableName, prop._id); + }); +} + +function linkSkill(dependencyGraph, prop){ + linkVariableName(dependencyGraph, prop); + // The prop depends on the variable references as the ability + if (prop.ability) dependencyGraph.addLink(prop._id, prop.ability); +} diff --git a/app/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js b/app/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js new file mode 100644 index 00000000..e9584d3d --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js @@ -0,0 +1,35 @@ +import { prettifyParseError, parse } from '/imports/parser/parser.js'; +import ErrorNode from '/imports/parser/parseTree/ErrorNode.js'; +import applyFnToKey from '/imports/api/creature/computation/newEngine/utility/applyFnToKey.js'; + +export default function parseCalculationFields(prop, schemas){ + // For each key in the schema + schemas[prop.type]._schemaKeys.forEach( key => { + // that ends in '.calculation' + if (key.slice(-12) !== '.calculation') return; + const calcKey = key.sclice(0, -12); + + // For all fields matching they keys + // supports `keys.$.with.$.arrays` + applyFnToKey(prop, calcKey, calcObj => { + // Store a reference to all the calculations + prop._computationDetails.calculations.push(calcObj); + // Parse the calculation + parseCalculation(calcObj); + }); + + }); +} + +function parseCalculation(calcObj){ + let calculation = calcObj.calculation || ''; + try { + calcObj._parsedCalculation = parse(calculation); + } catch (e) { + let error = prettifyParseError(e); + calcObj.errors ? + calcObj.errors.push(error) : + calcObj.errors = [error]; + calcObj._parsedCalculation = new ErrorNode({error}); + } +} diff --git a/app/imports/api/creature/computation/newEngine/buildCreatureComputation.js b/app/imports/api/creature/computation/newEngine/buildCreatureComputation.js new file mode 100644 index 00000000..e99df529 --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/buildCreatureComputation.js @@ -0,0 +1,126 @@ +import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; +import CreatureProperties, + { DenormalisedOnlyCreaturePropertySchema as denormSchema } + from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; +import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js'; +import applyFnToKey from '/imports/api/creature/computation/newEngine/utility/applyFnToKey.js'; +import { cloneDeep, unset } from 'lodash'; +import createGraph from 'ngraph.graph'; +import computeInventory from '/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js'; +import walkDown from '/imports/api/creature/computation/newEngine/utility/walkdown.js'; +import parseCalculationFields from '/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js'; +import computeInactiveStatus from '/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js'; +import computeToggleDependencies from '/imports/api/creature/computation/newEngine/buildComputation/computeToggleDependencies.js'; +import linkCalculationDependencies from '/imports/api/creature/computation/newEngine/buildComputation/linkCalculationDependencies.js'; +import linkTypeDependencies from '/imports/api/creature/computation/newEngine/buildComputation/linkTypeDependencies.js'; +import computeSlotQuantityFilled from '/imports/api/creature/computation/newEngine/buildComputation/computeSlotQuantityFilled.js'; + +/** + * Store index of properties + * recompute static tree-based enabled/disabled status + * Build a dependency graph + * id -> id dependencies for docs that rely on other docs directly + * id -> variable deps for docs that rely on a variable's value + * variable -> id deps for variables that are impacted by docs + * TODO: + * Depth first traversal or dependency graph to: + * Find loops in the dependency graph + * resolve variables in dependency order + */ + +/** + * TODO + * compute slots spaces left (after computed field of quantityExpected) + * compute damage multipliers + * compute dependencyGraph variables and properties + */ + +export default function buildCreatureComputation(creatureId){ + let properties = CreatureProperties.find({ + 'ancestors.id': creatureId, + 'removed': {$ne: true}, + }, { + sort: {order: 1} + }); + + // Dependency graph where edge(a, b) means a depends on b + // The graph includes all dependencies even of inactive properties + // such that any properties changing without changing their dependencies + // can limit the recompute to connected parts of the graph + const dependencyGraph = createGraph(); + + const computation = { + originalPropsById: {}, + propsById: {}, + propsByType: {}, + propsByVariableName: {}, + props: properties, + dependencyGraph, + }; + + // Process the properties one by one + properties.forEach(prop => { + + // Store the prop in the memo by type, variableName and id + storePropInMemo(prop, computation) + + // Store the prop in the dependency graph + dependencyGraph.addNode(prop._id, prop); + + // Remove old computed only fields + computedOnlySchemas[prop.type]._schemaKeys.forEach(key => + applyFnToKey(prop, key, unset) + ); + + // Remove old denormalised fields + denormSchema._schemaKeys.forEach(key => + applyFnToKey(prop, key, unset) + ); + + // Add a place to store all the computation details + prop._computationDetails = { + calculations: [], + toggleAncestors: [], + }; + + // Parse all the calculations + parseCalculationFields(prop, computedSchemas) + }); + + // Get all the properties as trees based on their ancestors + let forest = nodeArrayToTree(properties); + // Walk the property trees computing things that need to be inherited + walkDown(forest, node => { + computeInactiveStatus(node); + computeToggleDependencies(node); + computeSlotQuantityFilled(node); + }); + + // Compute the inventory + computeInventory(forest, dependencyGraph); + + // Graph functions that rely on the props being stored first + properties.forEach(prop => { + linkTypeDependencies(dependencyGraph, prop, computation.propsById); + linkCalculationDependencies(dependencyGraph, prop, computation.propsById); + }); + + return computation; +} + +function storePropInMemo(prop, memo){ + // Store dicts for easy access later + // Store a copy of the unmodified prop + memo.originalPropsById[prop._id] = cloneDeep(prop); + // Store by id + memo.propsById[prop._id] = prop; + // Store by type + memo.propsByType[prop.type] ? + memo.propsByType[prop.type].push(prop) : + memo.propsByType[prop.type] = [prop]; + // Store by variableName + memo.propsByVariableName[prop.variableName] ? + memo.propsByVariableName[prop.variableName].push(prop) : + memo.propsByVariableName[prop.variableName]= [prop]; +} diff --git a/app/imports/api/creature/computation/newEngine/computeCreature.js b/app/imports/api/creature/computation/newEngine/computeCreature.js deleted file mode 100644 index ffd2d80a..00000000 --- a/app/imports/api/creature/computation/newEngine/computeCreature.js +++ /dev/null @@ -1,239 +0,0 @@ -import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; -import CreatureProperties, - { DenormalisedOnlyCreaturePropertySchema as denormSchema } - from '/imports/api/creature/creatureProperties/CreatureProperties.js'; -import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; -import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js'; -import applyFnToKey from '/imports/api/creature/computation/newEngine/applyFnToKey.js'; -import { cloneDeep, unset } from 'lodash'; -import { prettifyParseError, parse } from '/imports/parser/parser.js'; -import ErrorNode from '/imports/parser/parseTree/ErrorNode.js'; -import SymbolNode from '/imports/parser/parseTree/SymbolNode.js'; -import AccessorNode from '/imports/parser/parseTree/AccessorNode.js'; -import createGraph from 'ngraph.graph'; -import findAncestorByType from 'imports/api/creature/computation/newEngine/findAncestorByType.js'; - -/** - * Store index of properties - * recompute static tree-based enabled/disabled status - * Build a dependency graph - * id -> id dependencies for docs that rely on other docs directly - * id -> variable deps for docs that rely on a variable's value - * TODO: - * variable -> id deps for variables that are impacted by docs - * Depth first traversal or dependency graph to: - * Find loops in the dependency graph - * resolve variables in dependency order - */ - -export default function computeCreature(creatureId){ - let properties = CreatureProperties.find({ - 'ancestors.id': creatureId, - 'removed': {$ne: true}, - }, { - sort: {order: 1} - }); - - const originalPropsById = {}; - const propsById = {}; - const propsByType = {}; - - // Process the properties one by one - properties.forEach(prop => { - // Store the prop by Id and Type - originalPropsById[prop._id] = cloneDeep(prop); - propsById[prop._id] = prop; - if (!propsByType[prop.type]) propsByType[prop.type] = []; - propsByType[prop.type].push(prop); - - // Store the prop in the dependency graph - dependencyGraph.addNode(prop._id, prop); - - // Remove all computed only fields - computedOnlySchemas[prop.type]._schemaKeys.forEach(key => - applyFnToKey(prop, key, unset) - ); - - // Remove all denormalised fields - denormSchema._schemaKeys.forEach(key => - applyFnToKey(prop, key, unset) - ); - - // Add a place to store all the computation details - prop._computationDetails = { - calculations: [], - toggleAncestors: [], - }; - - // parse every calculation field - computedSchemas[prop.type]._schemaKeys.forEach( key => { - if (key.slice(-11) !== 'calculation') return; - const calcKey = key.sclice(0, -11); - applyFnToKey(prop, calcKey, calcObj => { - // Store a reference to all the calculations - prop._computationDetails.calculations.push(calcObj); - // Parse the calculation - parseCalculation(calcObj); - return calcObj; - }); - }); - }); - - - // Process the properties in tree format - let creatureTree = nodeArrayToTree(properties); - walkDown(creatureTree, node => { - denormaliseInactiveStatus(node); - inheritToggleDependencies(node); - computeInventory(node); - }); - - // Dependency graph where edge(a, b) means a depends on b - const dependencyGraph = createGraph(); - // Build graph now that all props are stored - properties.forEach(prop => { - linkTypeDependencies(dependencyGraph, prop, propsById); - if (prop.inactive) return; - linkCalculationDependencies(dependencyGraph, prop, propsById); - }); -} - -function walkDown(tree, callback){ - let stack = [...tree]; - while(stack.length){ - let node = stack.pop(); - callback(node); - stack.push(...node.children); - } -} - -function denormaliseInactiveStatus(node){ - const prop = node.node; - if (isActive(prop)) return; - prop.inactive = true; - prop.deactivatedBySelf = true; - // Mark children as inactive due to ancestor - walkDown(node.children, child => { - child.node.inactive = true; - child.node.deactivatedByAncestor = true; - }); -} - -function isActive(prop){ - if (prop.disabled) return false; - switch (prop.type){ - case 'buff': return !!prop.applied; - case 'item': return !!prop.equipped; - case 'spell': return !!prop.prepared || !!prop.alwaysPrepared; - default: return true; - } -} - -function inheritToggleDependencies(node, dependencyGraph){ - const prop = node.node; - // Only for toggles that aren't inactive and aren't set to enabled or disabled - if ( - prop.inactive || - prop.type !== 'toggle' || - prop.disabled || - prop.enabled - ) return; - walkDown(node.children, child => { - child.node._computationDetails.toggleAncestors.push(prop._id); - dependencyGraph.addLink(child.node._id, prop._id, prop.condition); - }); -} - -function computeInventory(forest){ - const data = { - weightTotal: 0, - weightEquipment: 0, - weightCarried: 0, - valueTotal: 0, - valueEquipment: 0, - valueCarried: 0, - itemsAttuned: 0, - } - // The stack of properties to still navigate - const stack = [...forest]; - // The current containers we are inside of - const containerStack = []; - const visitedNodes = new Set(); - - while(stack.length){ - const top = stack[stack.length - 1]; - // Leaf node - if (top.children.length === 0){ - - } else { - - } - } -} - -function parseCalculation(calcObj){ - let calculation = calcObj.calculation || ''; - try { - calcObj._parsedCalculation = parse(calculation); - } catch (e) { - let error = prettifyParseError(e); - calcObj.errors ? - calcObj.errors.push(error) : - calcObj.errors = [error]; - calcObj._parsedCalculation = new ErrorNode({error}); - } -} - -function linkCalculationDependencies(dependencyGraph, prop, propsById){ - let variableNames = []; - prop._computationDetails.calculations.forEach(calcObj => { - calcObj._parsedCalculation.travese(node => { - if (node instanceof SymbolNode || node instanceof AccessorNode){ - if (node.name[0] !== '#'){ - dependencyGraph.addLink(prop._id, node.name, calcObj); - } else { - let ancestorProp = findAncestorByType( - prop, node.name.slice(1), propsById - ); - if (!ancestorProp) return; - dependencyGraph.addLink(prop._id, ancestorProp._id, calcObj); - } - } - }); - }); - return variableNames; -} - -const inventoryVariables = [ - 'weightTotal', - 'weightEquipment', - 'weightCarried', - 'valueTotal', - 'valueEquipment', - 'valueCarried', - 'itemsAttuned', -]; - -const linkDependenciesByType = { - attribute: linkVariableName, - classLevel: linkVariableName, - constant: linkVariableName, - container: linkInventoryVariables, -} - -function linkVariableName(dependencyGraph, prop){ - if (prop.inactive) return; - if (prop.variableName){ - dependencyGraph.addLink(prop.variableName, prop._id); - } -} - -function linkInventoryVariables(dependencyGraph, prop){ - inventoryVariables.forEach(variableName => { - dependencyGraph.addLink(variableName, prop._id); - }); -} - -function linkTypeDependencies(dependencyGraph, prop){ - linkDependenciesByType[prop.type]?.(prop); -} diff --git a/app/imports/api/creature/computation/newEngine/applyFnToKey.js b/app/imports/api/creature/computation/newEngine/utility/applyFnToKey.js similarity index 100% rename from app/imports/api/creature/computation/newEngine/applyFnToKey.js rename to app/imports/api/creature/computation/newEngine/utility/applyFnToKey.js diff --git a/app/imports/api/creature/computation/newEngine/findAncestorByType.js b/app/imports/api/creature/computation/newEngine/utility/findAncestorByType.js similarity index 100% rename from app/imports/api/creature/computation/newEngine/findAncestorByType.js rename to app/imports/api/creature/computation/newEngine/utility/findAncestorByType.js diff --git a/app/imports/api/creature/computation/newEngine/utility/walkdown.js b/app/imports/api/creature/computation/newEngine/utility/walkdown.js new file mode 100644 index 00000000..e0c4728a --- /dev/null +++ b/app/imports/api/creature/computation/newEngine/utility/walkdown.js @@ -0,0 +1,8 @@ +export default function walkDown(tree, callback){ + let stack = [...tree]; + while(stack.length){ + let node = stack.pop(); + callback(node); + stack.push(...node.children); + } +} diff --git a/app/imports/api/properties/Containers.js b/app/imports/api/properties/Containers.js index c48b521d..ca911e67 100644 --- a/app/imports/api/properties/Containers.js +++ b/app/imports/api/properties/Containers.js @@ -44,10 +44,20 @@ const ComputedOnlyContainerSchema = createPropertySchema({ type: Number, optional: true, }, + // Weight of all the carried contents (some sub-containers might not be carried) + // zero if `contentsWeightless` is true + carriedWeight:{ + type: Number, + optional: true, + }, contentsValue:{ type: Number, optional: true, }, + carriedValue:{ + type: Number, + optional: true, + }, }); const ComputedContainerSchema = new SimpleSchema() diff --git a/app/imports/api/properties/DamageMultipliers.js b/app/imports/api/properties/DamageMultipliers.js index 34c3d55f..4ed9c52f 100644 --- a/app/imports/api/properties/DamageMultipliers.js +++ b/app/imports/api/properties/DamageMultipliers.js @@ -1,5 +1,4 @@ import SimpleSchema from 'simpl-schema'; -import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; /* @@ -20,7 +19,7 @@ let DamageMultiplierSchema = new SimpleSchema({ // The technical, lowercase, single-word name used in formulae 'damageTypes.$': { type: String, - allowedValues: DAMAGE_TYPES, + max: STORAGE_LIMITS.calculation, }, // The value of the damage multiplier value: { diff --git a/app/imports/api/properties/Damages.js b/app/imports/api/properties/Damages.js index 007d97f5..8b4881f9 100644 --- a/app/imports/api/properties/Damages.js +++ b/app/imports/api/properties/Damages.js @@ -1,6 +1,6 @@ import SimpleSchema from 'simpl-schema'; -import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js'; +import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; const DamageSchema = createPropertySchema({ // The roll that determines how much to damage the attribute @@ -22,7 +22,7 @@ const DamageSchema = createPropertySchema({ }, damageType: { type: String, - allowedValues: DAMAGE_TYPES, + max: STORAGE_LIMITS.calculation, defaultValue: 'slashing', }, }); diff --git a/app/imports/api/properties/SlotFillers.js b/app/imports/api/properties/SlotFillers.js index b485a5c6..c3ac0d3b 100644 --- a/app/imports/api/properties/SlotFillers.js +++ b/app/imports/api/properties/SlotFillers.js @@ -30,7 +30,6 @@ let SlotFillerSchema = new SimpleSchema({ slotQuantityFilled: { type: SimpleSchema.Integer, defaultValue: 1, - min: 0, }, // Filters out of UI if condition isn't met, but isn't otherwise enforced slotFillerCondition: {