Files
DiceCloud/app/imports/api/creature/computation/newEngine/buildCreatureComputation.js
2021-09-09 13:47:41 +02:00

127 lines
4.8 KiB
JavaScript

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