Denormalised inline calculations to property documents, needs to be referenced by UI still

This commit is contained in:
Stefan Zermatten
2021-01-29 12:29:01 +02:00
parent 9c799e3dc9
commit 1167538977
25 changed files with 360 additions and 118 deletions

View File

@@ -2,6 +2,8 @@ import evaluateString from '/imports/api/creature/computation/afterComputation/e
// Strings can have computations in bracers like so: {computation}
export default function evalutateStringWithEmbeddedCalculations(string, scope){
console.warn('evalutateStringWithEmbeddedCalculations should be replaced with ' +
'fetching the result from the compuations on the property doc');
if (!string) return string;
// Compute everything inside bracers
return string.replace(/\{([^{}]*)\}/g, function(match, p1){

View File

@@ -18,6 +18,7 @@ export default function computeEndStepProperty(prop, memo){
break;
case 'propertySlot':
computeSlot(prop, memo);
break;
}
}

View File

@@ -0,0 +1,34 @@
import evaluateCalculation from '/imports/api/creature/computation/evaluateCalculation.js';
export default function computeInlineCalculations(prop, memo){
if (prop.summary){
computeInlineCalcsForField(prop, memo, 'summary');
}
if (prop.description){
computeInlineCalcsForField(prop, memo, 'description');
}
}
function computeInlineCalcsForField(prop, memo, field){
let string = prop[field];
let inlineComputations = [];
let matches = string.matchAll(/\{([^{}]*)\}/g);
for (let match of matches){
let calculation = match[1];
let {
result,
context,
dependencies,
} = evaluateCalculation(calculation, memo, 'compile');
let computation = {
calculation,
result: result.value,
};
if (context.errors.length){
computation.errors = context.errors;
}
inlineComputations.push(computation);
prop.dependencies.push(...dependencies);
}
prop[`${field}Calculations`] = inlineComputations;
}

View File

@@ -4,6 +4,7 @@ import computeStat from '/imports/api/creature/computation/computeStat.js';
import computeEffect from '/imports/api/creature/computation/computeEffect.js';
import computeToggle from '/imports/api/creature/computation/computeToggle.js';
import computeEndStepProperty from '/imports/api/creature/computation/computeEndStepProperty.js';
import computeInlineCalculations from '/imports/api/creature/computation/computeInlineCalculations.js';
export default function computeMemo(memo){
// Compute level
@@ -24,4 +25,8 @@ export default function computeMemo(memo){
forOwn(memo.endStepPropsById, prop => {
computeEndStepProperty(prop, memo);
});
// Compute inline calculations
forOwn(memo.propsById, prop => {
computeInlineCalculations(prop, memo);
});
}

View File

@@ -28,6 +28,13 @@ export default function evaluateCalculation(string, memo, fn = 'reduce'){
dependencies,
};
}
if (!calc){
return {
context: {errors},
result: new ConstantNode({value: calc, type: 'string'}),
dependencies,
};
}
// Ensure all symbol nodes are defined and computed
calc.traverse(node => {
if (node instanceof SymbolNode || node instanceof AccessorNode){

View File

@@ -0,0 +1,35 @@
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
export default function getComputationProperties(creatureId){
// find ids of all toggles that have conditions, even if they are inactive
let toggleIds = CreatureProperties.find({
'ancestors.id': creatureId,
type: 'toggle',
removed: {$ne: true},
condition: { $exists: true },
}, {
fields: {_id: 1},
}).map(t => t._id);
// Find all the relevant properties
return CreatureProperties.find({
'ancestors.id': creatureId,
removed: {$ne: true},
$or: [
// All active properties
{inactive: {$ne: true}},
// All active and inactive toggles with conditions
// Same as {$in: toggleIds}, but should be slightly faster
{type: 'toggle', condition: { $exists: true }},
// All decendents of the above toggles
{'ancestors.id': {$in: toggleIds}},
]
}, {
// Filter out fields never used by calculations
fields: {
icon: 0,
},
sort: {
order: 1,
}
}).fetch();
}

View File

@@ -3,7 +3,7 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import ComputationMemo from '/imports/api/creature/computation/ComputationMemo.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import getComputationProperties from '/imports/api/creature/computation/getComputationProperties.js';
import computeMemo from '/imports/api/creature/computation/computeMemo.js';
import writeAlteredProperties from '/imports/api/creature/computation/writeAlteredProperties.js';
import writeCreatureVariables from '/imports/api/creature/computation/writeCreatureVariables.js';
@@ -37,23 +37,6 @@ export const recomputeCreature = new ValidatedMethod({
});
const calculationPropertyTypes = [
'attribute',
'skill',
'effect',
'proficiency',
'classLevel',
'toggle',
'item',
// End step types
'action',
'attack',
'savingThrow',
'spellList',
'spell',
'propertySlot',
];
export function recomputeCreatureById(creatureId){
let creature = Creatures.findOne(creatureId);
recomputeCreatureByDoc(creature);
@@ -97,38 +80,13 @@ export function recomputeCreatureById(creatureId){
*/
export function recomputeCreatureByDoc(creature){
const creatureId = creature._id;
// find all toggles that have conditions, even if they are inactive
let toggleIds = CreatureProperties.find({
'ancestors.id': creatureId,
type: 'toggle',
removed: {$ne: true},
condition: { $exists: true },
}, {
fields: {_id: 1},
}).map(t => t._id);
// Find all the active properties
let props = CreatureProperties.find({
'ancestors.id': creatureId,
removed: {$ne: true},
type: {$in: calculationPropertyTypes},
$or: [
{inactive: {$ne: true}},
// But also the inactive computed toggles and their decendants
{'ancestors.id': {$in: toggleIds}},
{_id: {$in: toggleIds}},
]
}, {
fields: { // Filter out potentially large fields
icon: 0,
summary: 0,
description: 0,
},
sort: {
order: 1,
}
}).fetch();
let computationMemo = new ComputationMemo(props, creature);
// Make sure the active state of all properties is correct before doing work
// TODO: Separate this into it's own recompute function and only call it
// when things change activation state
recomputeInactiveProperties(creatureId);
let props = getComputationProperties(creatureId);
let computationMemo = new ComputationMemo(props, creature);
computeMemo(computationMemo);
writeAlteredProperties(computationMemo);
writeCreatureVariables(computationMemo, creatureId);

View File

@@ -1,38 +1,13 @@
import { Meteor } from 'meteor/meteor'
import { isEqual, forOwn } from 'lodash';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
// Schemas
// Calculated props
import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js';
import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js';
import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects.js';
import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles.js';
// End step props
import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
import { ComputedOnlyAttackSchema } from '/imports/api/properties/Attacks.js';
import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
import { ComputedOnlySpellListSchema } from '/imports/api/properties/SpellLists.js';
import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells.js';
import { ComputedOnlySlotSchema } from '/imports/api/properties/Slots.js';
const schemasByType = {
'skill': ComputedOnlySkillSchema,
'attribute': ComputedOnlyAttributeSchema,
'effect': ComputedOnlyEffectSchema,
'toggle': ComputedOnlyToggleSchema,
'action': ComputedOnlyActionSchema,
'attack': ComputedOnlyAttackSchema,
'savingThrow': ComputedOnlySavingThrowSchema,
'spellList': ComputedOnlySpellListSchema,
'spell': ComputedOnlySpellSchema,
'propertySlot': ComputedOnlySlotSchema,
};
import propertySchemasIndex from '/imports/api/properties/computedOnlyPropertySchemasIndex.js';
export default function writeAlteredProperties(memo){
let bulkWriteOperations = [];
// Loop through all properties on the memo
forOwn(memo.propsById, changed => {
let schema = schemasByType[changed.type];
let schema = propertySchemasIndex[changed.type];
if (!schema) return;
let extraIds = changed.computationDetails.idsOfSameName;
let ids;