Got tests running on single property character
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import computeStat from '/imports/api/creature/computation/engine/computeStat.js';
|
||||
import computeProficiency from '/imports/api/creature/computation/engine/computeProficiency.js';
|
||||
import evaluateCalculation from '/imports/api/creature/computation/engine/evaluateCalculation.js';
|
||||
import stripFloatingPointOddities from '/imports/ui/utility/stripFloatingPointOddities.js';
|
||||
import stripFloatingPointOddities from '/imports/api/creature/computation/newEngine/utility/stripFloatingPointOddities.js';
|
||||
import { union } from 'lodash';
|
||||
|
||||
export default function combineStat(stat, aggregator, memo){
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
import createGraph from 'ngraph.graph';
|
||||
|
||||
export default class CreatureComputation {
|
||||
constructor(properties){
|
||||
// Set up fields
|
||||
this.originalPropsById = {};
|
||||
this.propsById = {};
|
||||
this.propsByType = {};
|
||||
this.propsByVariableName = {};
|
||||
this.props = properties;
|
||||
this.dependencyGraph = createGraph();
|
||||
|
||||
// Store properties for easy access later
|
||||
properties.forEach(prop => {
|
||||
// Store a copy of the unmodified prop
|
||||
this.originalPropsById[prop._id] = cloneDeep(prop);
|
||||
|
||||
// Store by id
|
||||
this.propsById[prop._id] = prop;
|
||||
|
||||
// Store by type
|
||||
this.propsByType[prop.type] ?
|
||||
this.propsByType[prop.type].push(prop) :
|
||||
this.propsByType[prop.type] = [prop];
|
||||
|
||||
// Store by variableName
|
||||
this.propsByVariableName[prop.variableName] ?
|
||||
this.propsByVariableName[prop.variableName].push(prop) :
|
||||
this.propsByVariableName[prop.variableName]= [prop];
|
||||
|
||||
// Store the prop in the dependency graph
|
||||
this.dependencyGraph.addNode(prop._id, prop);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default function linkCalculationDependencies(dependencyGraph, prop, {prop
|
||||
// ancestors: {} //this gets added if there are resolved ancestors
|
||||
};
|
||||
// Traverse the parsed calculation looking for variable names
|
||||
calcObj._parsedCalculation.travese(node => {
|
||||
calcObj._parsedCalculation.traverse(node => {
|
||||
// Skip nodes that aren't symbols or accessors
|
||||
if (!(node instanceof SymbolNode || node instanceof AccessorNode)) return;
|
||||
// Link ancestor references as direct property dependencies
|
||||
|
||||
@@ -27,6 +27,8 @@ 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;
|
||||
// Determine if this property is carried, items are carried by default
|
||||
let carried = prop.type === 'container' ? prop.carried : true;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const linkDependenciesByType = {
|
||||
}
|
||||
|
||||
export default function linkTypeDependencies(dependencyGraph, prop){
|
||||
linkDependenciesByType[prop.type]?.(prop);
|
||||
linkDependenciesByType[prop.type]?.(dependencyGraph, prop);
|
||||
}
|
||||
|
||||
function linkClassLevel(dependencyGraph, prop){
|
||||
|
||||
@@ -1,20 +1,54 @@
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||
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';
|
||||
import { get } from 'lodash';
|
||||
|
||||
export default function parseCalculationFields(prop, schemas){
|
||||
parseInlineCalculationFields(prop, schemas);
|
||||
parseDirectCalculationFields(prop, schemas);
|
||||
}
|
||||
|
||||
function parseInlineCalculationFields(prop, schemas){
|
||||
// For each key in the schema
|
||||
schemas[prop.type]._schemaKeys.forEach( key => {
|
||||
// That ends in .inlineCalculations
|
||||
if (key.slice(-19) === '.inlineCalculations'){
|
||||
const inlineCalcKey = key.slice(0, -19);
|
||||
applyFnToKey(prop, inlineCalcKey, (prop, key) => {
|
||||
const inlineCalcObj = get(prop, key);
|
||||
if (!inlineCalcObj) return;
|
||||
// Store a reference to all the inline calculations
|
||||
prop._computationDetails.inlineCalculations.push(inlineCalcObj);
|
||||
// Extract the calculations and store them on the property
|
||||
let string = inlineCalcObj.text;
|
||||
inlineCalcObj.inlineCalculations = [];
|
||||
let matches = string.matchAll(INLINE_CALCULATION_REGEX);
|
||||
for (let match of matches){
|
||||
let calculation = match[1];
|
||||
inlineCalcObj.inlineCalculations.push({
|
||||
calculation,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseDirectCalculationFields(prop, schemas){
|
||||
// For each key in the schema
|
||||
schemas[prop.type]._schemaKeys.forEach( key => {
|
||||
// that ends in '.calculation'
|
||||
if (key.slice(-12) === '.calculation'){
|
||||
const calcKey = key.sclice(0, -12);
|
||||
|
||||
const calcKey = key.slice(0, -12);
|
||||
// Determine the level the calculation should compute down to
|
||||
let parseLevel = schemas[prop.type].getDefinition(calcKey).parseLevel;
|
||||
let parseLevel = schemas[prop.type].getDefinition(calcKey).parseLevel || 'reduce';
|
||||
|
||||
// For all fields matching they keys
|
||||
// supports `keys.$.with.$.arrays`
|
||||
applyFnToKey(prop, calcKey, calcObj => {
|
||||
applyFnToKey(prop, calcKey, (prop, key) => {
|
||||
const calcObj = get(prop, key);
|
||||
if (!calcObj) return;
|
||||
// Store a reference to all the calculations
|
||||
prop._computationDetails.calculations.push(calcObj);
|
||||
// Store the level to compute down to later
|
||||
@@ -23,14 +57,7 @@ export default function parseCalculationFields(prop, schemas){
|
||||
parseCalculation(calcObj);
|
||||
});
|
||||
// Or that ends in .inlineCalculations
|
||||
} else if (key.slice(-19) === '.inlineCalculations'){
|
||||
const inlineCalcKey = key.sclice(0, -19);
|
||||
applyFnToKey(prop, inlineCalcKey, inlineCalcObj => {
|
||||
// Store a reference to all the inline calculations
|
||||
prop._computationDetails.inlineCalculations.push(inlineCalcObj);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import applyFnToKey from '../utility/applyFnToKey.js';
|
||||
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;
|
||||
// Unset other computed only keys
|
||||
applyFnToKey(prop, key, unset)
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -4,17 +4,16 @@ import CreatureProperties,
|
||||
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 linkInventory from '/imports/api/creature/computation/newEngine/buildComputation/linkInventory.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';
|
||||
import linkInventory from './buildComputation/linkInventory.js';
|
||||
import walkDown from './utility/walkdown.js';
|
||||
import parseCalculationFields from './buildComputation/parseCalculationFields.js';
|
||||
import computeInactiveStatus from './buildComputation/computeInactiveStatus.js';
|
||||
import computeToggleDependencies from './buildComputation/computeToggleDependencies.js';
|
||||
import linkCalculationDependencies from './buildComputation/linkCalculationDependencies.js';
|
||||
import linkTypeDependencies from './buildComputation/linkTypeDependencies.js';
|
||||
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
||||
import CreatureComputation from './buildComputation/CreatureComputation.js';
|
||||
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
||||
|
||||
/**
|
||||
* Store index of properties
|
||||
@@ -50,50 +49,32 @@ function getProperties(creatureId){
|
||||
}
|
||||
|
||||
export function buildComputationFromProps(properties){
|
||||
|
||||
const computation = new CreatureComputation(properties);
|
||||
// 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
|
||||
// Each node's data represents a prop or a virtual prop like a variable
|
||||
// Each link's data is a string representing the link type
|
||||
const dependencyGraph = createGraph();
|
||||
|
||||
const computation = {
|
||||
originalPropsById: {},
|
||||
propsById: {},
|
||||
propsByType: {},
|
||||
propsByVariableName: {},
|
||||
props: properties,
|
||||
dependencyGraph,
|
||||
};
|
||||
const dependencyGraph = computation.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)
|
||||
);
|
||||
let computedSchema = computedOnlySchemas[prop.type];
|
||||
removeSchemaFields([computedSchema, denormSchema], prop);
|
||||
|
||||
// Add a place to store all the computation details
|
||||
prop._computationDetails = {
|
||||
calculations: [],
|
||||
inlineCalculations: [],
|
||||
toggleAncestors: [],
|
||||
};
|
||||
|
||||
// Parse all the calculations
|
||||
parseCalculationFields(prop, computedSchemas);
|
||||
|
||||
});
|
||||
|
||||
// Get all the properties as trees based on their ancestors
|
||||
@@ -108,7 +89,7 @@ export function buildComputationFromProps(properties){
|
||||
// Link the inventory dependencies
|
||||
linkInventory(forest, dependencyGraph);
|
||||
|
||||
// Graph functions that rely on the props being stored first
|
||||
// Link functions that require the above to be complete
|
||||
properties.forEach(prop => {
|
||||
linkTypeDependencies(dependencyGraph, prop, computation);
|
||||
linkCalculationDependencies(dependencyGraph, prop, computation);
|
||||
@@ -116,19 +97,3 @@ export function buildComputationFromProps(properties){
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { assert } from 'chai';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
describe('buildComputation', function(){
|
||||
it('Builds something', function(){
|
||||
it('Builds something at all', function(){
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
console.log(computation);
|
||||
assert.exists(computation);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,14 @@ var testProperties = [
|
||||
clean({
|
||||
_id: 'attributeId123',
|
||||
type: 'attribute',
|
||||
variableName: 'strength',
|
||||
attributeType: 'ability',
|
||||
baseValue: {
|
||||
calculation: '1 + 2 + 3',
|
||||
},
|
||||
description: {
|
||||
text: 'strength is {strength}'
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import aggregate from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/aggregate/index.js';
|
||||
import computeVariableAsAttribute from '/imports/api/creature/computation/newEngine/computeComputation/computeVariableAsType/computeVariableAsAttribute.js';
|
||||
import computeVariableAsSkill from '/imports/api/creature/computation/newEngine/computeComputation/computeVariableAsType/computeVariableAsSkill.js';
|
||||
import computeVariableAsConstant from '/imports/api/creature/computation/newEngine/computeComputation/computeByType/computeVariable/computeVariableAsConstant.js';
|
||||
import computeVariableAsClass from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/computeVariableAsClass.js';
|
||||
import computeImplicitVariable from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/computeImplicitVariable.js';
|
||||
import aggregate from './computeVariable/aggregate/index.js';
|
||||
import computeVariableAsAttribute from './computeVariable/computeVariableAsAttribute.js';
|
||||
import computeVariableAsSkill from './computeVariable/computeVariableAsSkill.js';
|
||||
import computeVariableAsConstant from './computeVariable/computeVariableAsConstant.js';
|
||||
import computeVariableAsClass from './computeVariable/computeVariableAsClass.js';
|
||||
import computeImplicitVariable from './computeVariable/computeImplicitVariable.js';
|
||||
|
||||
export default function computeVariable(graph, node, scope){
|
||||
if (!node.data) node.data = {};
|
||||
@@ -16,6 +16,7 @@ export default function computeVariable(graph, node, scope){
|
||||
// Otherwise add an implicit variable to the scope
|
||||
scope[node.id] = computeImplicitVariable(node, scope);
|
||||
}
|
||||
console.log('computed variable ', node);
|
||||
}
|
||||
|
||||
function aggregateLinks(graph, node){
|
||||
@@ -40,13 +41,14 @@ function aggregateLinks(graph, node){
|
||||
|
||||
function combineAggregations(node, scope){
|
||||
combineMultiplierAggregator(node);
|
||||
node.overridenProps.forEach(prop => {
|
||||
node.data.overridenProps?.forEach(prop => {
|
||||
computeVariableProp(node, prop, scope);
|
||||
});
|
||||
computeVariableProp(node, node.definingProp, scope);
|
||||
computeVariableProp(node, node.data.definingProp, scope);
|
||||
}
|
||||
|
||||
function computeVariableProp(node, prop, scope){
|
||||
if (!prop) return;
|
||||
if (prop.type === 'attribute'){
|
||||
computeVariableAsAttribute(node, prop, scope)
|
||||
} else if (prop.type === 'skill'){
|
||||
@@ -61,6 +63,7 @@ function computeVariableProp(node, prop, scope){
|
||||
function combineMultiplierAggregator(node){
|
||||
// get a reference to the aggregator
|
||||
const aggregator = node.data.multiplierAggregator;
|
||||
if (!aggregator) return;
|
||||
|
||||
// Combine
|
||||
let value;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import getAggregatorResult from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/getAggregatorResult.js';
|
||||
import getAggregatorResult from './getAggregatorResult.js';
|
||||
|
||||
/*
|
||||
* Variables with effects, proficiencies, or damage multipliers but no defining
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import getAggregatorResult from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/getAggregatorResult.js';
|
||||
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);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CompilationContext } from '/imports/parser/parser.js';
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||
|
||||
export default function computeCalculations(node, scope){
|
||||
if (!node.data) return;
|
||||
// evaluate all the calculations
|
||||
node.data._computationDetails?.calculations?.forEach(calcObj => {
|
||||
evaluateCalculation(calcObj, scope)
|
||||
@@ -14,7 +15,7 @@ export default function computeCalculations(node, scope){
|
||||
function evaluateCalculation(calculation, scope){
|
||||
const context = new CompilationContext();
|
||||
const parseNode = calculation._parsedCalculation;
|
||||
const fn = calculation._parseLevel || 'reduce';
|
||||
const fn = calculation._parseLevel;
|
||||
const calculationScope = {...calculation._localScope, ...scope};
|
||||
calculation.value = parseNode[fn](calculationScope, context);
|
||||
calculation.errors = context.errors;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default function evaluateToggles(node){
|
||||
let prop = node.data;
|
||||
if (!prop) return;
|
||||
let toggles = prop._computationDetails?.toggleAncestors;
|
||||
if (!toggles) return;
|
||||
toggles.forEach(toggle => {
|
||||
|
||||
@@ -8,27 +8,23 @@ export default function computeCreatureComputation(computation){
|
||||
const scope = {};
|
||||
const graph = computation.dependencyGraph;
|
||||
// Add all nodes to the stack
|
||||
graph.forEachNode(node => stack.push({
|
||||
node,
|
||||
visited: false,
|
||||
visitedChildren: false,
|
||||
}));
|
||||
graph.forEachNode(node => stack.push(node));
|
||||
// Depth first traversal of nodes
|
||||
while (stack.length){
|
||||
let top = stack[stack.length - 1];
|
||||
if (top.visited){
|
||||
if (top._visited){
|
||||
// The object has already been computed, skip
|
||||
stack.pop();
|
||||
} else if (top.visitedChildren){
|
||||
// Compute the top object of the stack
|
||||
compute(graph, top.node, scope);
|
||||
} else if (top._visitedChildren){
|
||||
// Mark the object as visited and remove from stack
|
||||
top.visited = true;
|
||||
top._visited = true;
|
||||
stack.pop();
|
||||
// Compute the top object of the stack
|
||||
compute(graph, top, scope);
|
||||
} else {
|
||||
top._visitedChildren = true;
|
||||
// Push dependencies to graph to be computed first
|
||||
pushDependenciesToStack(top.node.id, graph, stack);
|
||||
top.visitedChildren = true;
|
||||
pushDependenciesToStack(top.id, graph, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,15 +38,5 @@ function compute(graph, node, scope){
|
||||
}
|
||||
|
||||
function pushDependenciesToStack(nodeId, graph, stack){
|
||||
graph.forEachLinkedNode(
|
||||
nodeId,
|
||||
(linkedNode) => {
|
||||
stack.push({
|
||||
node: linkedNode,
|
||||
visited: false,
|
||||
visitedChildren: false,
|
||||
});
|
||||
},
|
||||
true // enumerate only outbound links
|
||||
);
|
||||
graph.forEachLinkedNode(nodeId, linkedNode => stack.push(linkedNode), true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import computeCreatureComputation from './computeCreatureComputation.js';
|
||||
import { buildComputationFromProps } from './buildCreatureComputation.js';
|
||||
import { assert } from 'chai';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.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);
|
||||
});
|
||||
});
|
||||
|
||||
var testProperties = [
|
||||
clean({
|
||||
_id: 'attributeId123',
|
||||
type: 'attribute',
|
||||
variableName: 'strength',
|
||||
attributeType: 'ability',
|
||||
baseValue: {
|
||||
calculation: '1 + 2 + 3',
|
||||
},
|
||||
description: {
|
||||
text: 'strength is {strength}'
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
function clean(prop){
|
||||
let schema = CreatureProperties.simpleSchema(prop);
|
||||
return schema.clean(prop);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { get } from 'lodash';
|
||||
|
||||
export function applyFnToKey(doc, key, fn){
|
||||
if (key.includes('$.')){
|
||||
export default function applyFnToKey(doc, key, fn){
|
||||
if (key.includes('.$')){
|
||||
applyToArrayKey(doc, key, fn);
|
||||
} else {
|
||||
applyToSingleKey(doc, key, fn);
|
||||
@@ -16,26 +16,31 @@ function applyToSingleKey(doc, key, fn){
|
||||
/**
|
||||
* Applies the given function to all instances in a document key
|
||||
* key.$.with.$.subdocs will apply to all key[i...n].with[j...m].subdocs
|
||||
* Warning: Order might be confusing, it will traverse the deepest array in order
|
||||
* but the shallower arrays in reverse order
|
||||
*/
|
||||
function applyToArrayKey(doc, key, fn){
|
||||
const keySplit = key.split('.$');
|
||||
|
||||
// Stack based depth first traversal of arrays
|
||||
const array = get(doc, keySplit[0]);
|
||||
if (!array) return;
|
||||
const stack = [{
|
||||
array: get(doc, keySplit[0]),
|
||||
array,
|
||||
paths: keySplit.slice(1),
|
||||
currentPath: keySplit[0],
|
||||
indices: [],
|
||||
}];
|
||||
while(stack.length){
|
||||
const state = stack.pop();
|
||||
for (let index in state.array.length){
|
||||
for (let index in state.array){
|
||||
const currentPath = `${state.currentPath}[${index}]${state.paths[0]}`
|
||||
if (state.paths.length == 1){
|
||||
applyToSingleKey(doc, currentPath, fn);
|
||||
} else {
|
||||
const array = get(doc, currentPath);
|
||||
if (!array) return;
|
||||
stack.push({
|
||||
array: get(doc, currentPath),
|
||||
array,
|
||||
paths: state.paths.slice(1),
|
||||
currentPath,
|
||||
indices: [...state.indices, index],
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import applyFnToKey from './applyFnToKey.js';
|
||||
import { assert } from 'chai';
|
||||
import { get } from 'lodash';
|
||||
|
||||
describe('apply function to key', function(){
|
||||
it('uses a basic key correctly', function(){
|
||||
let obj = getStartingObject();
|
||||
applyFnToKey(obj, 'fox.name', (doc, key) => {
|
||||
assert.equal(obj, doc);
|
||||
assert.equal(key, 'fox.name');
|
||||
assert.equal(get(doc, key), 'foxy');
|
||||
});
|
||||
});
|
||||
it('uses a single nested key correctly', function(){
|
||||
let obj = getStartingObject();
|
||||
let foxSounds = [];
|
||||
applyFnToKey(obj, 'fox.sound.$', (doc, key) => {
|
||||
foxSounds.push(get(doc, key));
|
||||
});
|
||||
assert.include(foxSounds, 'wah');
|
||||
assert.include(foxSounds, 'tjoef');
|
||||
assert.include(foxSounds, 'kek');
|
||||
});
|
||||
it('uses a double nested key correctly', function(){
|
||||
let obj = getStartingObject();
|
||||
let birdSounds = [];
|
||||
applyFnToKey(obj, 'birds.$.sound.$', (doc, key) => {
|
||||
birdSounds.push(get(doc, key));
|
||||
});
|
||||
assert.include(birdSounds, 'koer');
|
||||
assert.include(birdSounds, 'hello');
|
||||
assert.include(birdSounds, 'squawk');
|
||||
});
|
||||
});
|
||||
|
||||
function getStartingObject(){
|
||||
return {
|
||||
fox: {
|
||||
name: 'foxy',
|
||||
sound: [
|
||||
'tjoef',
|
||||
'kek',
|
||||
'wah'
|
||||
]
|
||||
},
|
||||
birds: [{
|
||||
name: 'pigeon',
|
||||
sound: [
|
||||
'koer',
|
||||
]
|
||||
},{
|
||||
name: 'parrot',
|
||||
sound: [
|
||||
'hello',
|
||||
'cracker?',
|
||||
'squawk',
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '/imports/api/properties/subSchemas/ResourcesSchema.js';
|
||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
SimpleSchema.extendOptions(['parseLevel']);
|
||||
|
||||
/*
|
||||
* Actions are things a character can do
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
// Get schemas that apply fields directly so they can be gracefully extended
|
||||
@@ -26,18 +26,36 @@ function computedOnlyInlineCalculationField(field){
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
[`${field}.value`]: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.inlineCalculationField,
|
||||
},
|
||||
[`${field}.inlineCalculations`]: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||
},
|
||||
[`${field}.inlineCalculations.$`]: {
|
||||
type: InlineComputationSchema,
|
||||
type: Object,
|
||||
},
|
||||
[`${field}.value`]: {
|
||||
// The part between bracers {}
|
||||
[`${field}.inlineCalculations.$.calculation`]: {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
[`${field}.inlineCalculations.$.value`]: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.inlineCalculationField,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
[`${field}.inlineCalculations.$.errors`]: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
maxCount: STORAGE_LIMITS.errorCount,
|
||||
},
|
||||
[`${field}.inlineCalculations.$.errors.$`]: {
|
||||
type: ErrorSchema,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,9 +38,6 @@ const exampleAction = {
|
||||
'usesUsed':0,
|
||||
'description':'Starting at 1st level, you gain the ability to place a baleful curse on someone. As a bonus action, choose one creature you can see within 30 feet of you. The target is cursed for 1 minute. The curse ends early if the target dies, you die, or you are incapacitated. Until the curse ends, you gain the following benefits:\n\n- You gain a bonus to damage rolls against the cursed target. The bonus equals your proficiency bonus.\n- Any attack roll you make against the cursed target is a critical hit on a roll of 19 or 20 on the d20.\n- If the cursed target dies, you regain hit points equal to your warlock level + your Charisma modifier (minimum of 1 hit point). \n{warlock.level <10 ? "" :"- If you are hit with an attack by your cursed target, use your reaction to roll a d6. On a 4 or higher, the attack instead misses."} \nYou can\\\'t use this feature again until you finish a short or long rest.',
|
||||
'color':'#8e24aa',
|
||||
'dependencies':[
|
||||
'4eM4YkgAaoCJfCfQ8',
|
||||
],
|
||||
'descriptionCalculations':[
|
||||
{
|
||||
'calculation':'warlock.level <10 ? "" :"- If you are hit with an attack by your cursed target, use your reaction to roll a d6. On a 4 or higher, the attack instead misses."',
|
||||
|
||||
@@ -107,7 +107,7 @@ import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
import stripFloatingPointOddities from '/imports/ui/utility/stripFloatingPointOddities.js';
|
||||
import stripFloatingPointOddities from '/imports/api/creature/computation/newEngine/utility/stripFloatingPointOddities.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -49,7 +49,7 @@ import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
import stripFloatingPointOddities from '/imports/ui/utility/stripFloatingPointOddities.js';
|
||||
import stripFloatingPointOddities from '/imports/api/creature/computation/newEngine/utility/stripFloatingPointOddities.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -134,7 +134,7 @@ import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyV
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
import IncrementButton from '/imports/ui/components/IncrementButton.vue';
|
||||
import adjustQuantity from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
|
||||
import stripFloatingPointOddities from '/imports/ui/utility/stripFloatingPointOddities.js';
|
||||
import stripFloatingPointOddities from '/imports/api/creature/computation/newEngine/utility/stripFloatingPointOddities.js';
|
||||
|
||||
export default {
|
||||
components:{
|
||||
|
||||
Reference in New Issue
Block a user