diff --git a/app/imports/api/creature/creatures/CreatureVariables.js b/app/imports/api/creature/creatures/CreatureVariables.js index 27fe0af7..b52805af 100644 --- a/app/imports/api/creature/creatures/CreatureVariables.js +++ b/app/imports/api/creature/creatures/CreatureVariables.js @@ -1,3 +1,5 @@ +import { getSingleProperty } from '/imports/api/engine/loadCreatures'; + //set up the collection for creature variables let CreatureVariables = new Mongo.Collection('creatureVariables'); @@ -7,15 +9,29 @@ if (Meteor.isServer) { } /** No schema because the structure isn't known until compute time - * Expect documents to looke like: + * Expect documents to look like: * { * _id: "nE8Ngd6K4L4jSxLY2", * _creatureId: "nE8Ngd6K4L4jSxLY2", // indexed reference to the creature - * explicitlyDefinedVariableName: {...some creatureProperty} + * explicitlyDefinedVariableName: {...some creatureProperty}, + * // Must be found in CreatureProperties before using: + * linkedProperty: { _propId: "nE8Ngd6K1234SxLY2" } * implicitVariableName: {value: 10}, * undefinedVariableName: {}, * } * Where top level fields that don't start with `_` are variables on the sheet **/ +/** + * Get the property from the given scope, respecting properties that are just a link to the actual + * property document + */ +export function getFromScope(name, scope) { + let value = scope?.[name]; + if (value?._propId) { + value = getSingleProperty(scope._creatureId, value._propId); + } + return value; +} + export default CreatureVariables; diff --git a/app/imports/api/engine/actions/ActionContext.js b/app/imports/api/engine/actions/ActionContext.js index 9c5e2e50..fb2edaf4 100644 --- a/app/imports/api/engine/actions/ActionContext.js +++ b/app/imports/api/engine/actions/ActionContext.js @@ -1,10 +1,10 @@ import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js'; import { - getCreature, getVariables, getPropertiesOfType + getCreature, getVariables, getPropertiesOfType, replaceLinkedVariablesWithProps } from '/imports/api/engine/loadCreatures.js'; import { groupBy, remove } from 'lodash'; -export default class ActionContext{ +export default class ActionContext { constructor(creatureId, targetIds = [], method) { // Get the creature this.creature = getCreature(creatureId) @@ -20,6 +20,7 @@ export default class ActionContext{ // Get the variables of the acting creature this.creature.variables = getVariables(creatureId); + replaceLinkedVariablesWithProps(this.creature.variables); delete this.creature.variables._id; delete this.creature.variables._creatureId; // Alias as scope @@ -64,7 +65,7 @@ export default class ActionContext{ } } addLog(content) { - if (content.name || content.value){ + if (content.name || content.value) { this.log.content.push(content); } } diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js b/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js index 49adf4eb..57fd8d25 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js @@ -8,7 +8,7 @@ import computedSchemas from '/imports/api/properties/computedPropertySchemasInde import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js'; import { get } from 'lodash'; import resolve, { map, toString } from '/imports/parser/resolve.js'; -import symbol from '/imports/parser/parseTree/symbol.js'; +import accessor from '/imports/parser/parseTree/accessor.js'; import logErrors from './shared/logErrors.js'; import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js'; import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js'; @@ -117,7 +117,7 @@ function crystalizeVariables({ propList, actionContext }) { calcObj.parseNode = map(calcObj.parseNode, node => { // Skip nodes that aren't symbols or accessors if ( - node.parseType !== 'accessor' && node.parseType !== 'symbol' + node.parseType !== 'accessor' ) return node; // Handle variables if (node.name === '~target') { @@ -125,7 +125,7 @@ function crystalizeVariables({ propList, actionContext }) { if (node.parseType === 'accessor') { node.name = node.path.shift(); if (!node.path.length) { - return symbol.create({ name: node.name }) + return accessor.create({ name: node.name }) } } else { // Can't strip symbols diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computePointBuy.js b/app/imports/api/engine/computation/computeComputation/computeByType/computePointBuy.js index 93227e15..65e16aa3 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computePointBuy.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computePointBuy.js @@ -1,5 +1,5 @@ import { has } from 'lodash'; -import resolveCalculationNode from 'imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js'; +import resolveCalculationNode from '/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js'; export default function computePointBuy(computation, node) { const prop = node.data; diff --git a/app/imports/api/engine/computation/writeComputation/writeScope.js b/app/imports/api/engine/computation/writeComputation/writeScope.js index a25e9ba0..7f708dd3 100644 --- a/app/imports/api/engine/computation/writeComputation/writeScope.js +++ b/app/imports/api/engine/computation/writeComputation/writeScope.js @@ -19,6 +19,12 @@ export default function writeScope(creatureId, computation) { // Mongo can't handle keys that start with a dollar sign if (key[0] === '$' || key[0] === '_') continue; + // Remove empty objects + if (Object.keys(scope[key]).length === 0) { + delete scope[key]; + continue; + } + // Remove large properties that aren't likely to be accessed delete scope[key].parent; delete scope[key].ancestors; @@ -30,6 +36,11 @@ export default function writeScope(creatureId, computation) { } } + // If this is a creature property, replace the property with a link + if (scope[key]._id && scope[key].type) { + scope[key] = { _propId: scope[key]._id }; + } + // Only update changed fields if (!EJSON.equals(variables[key], scope[key])) { if (!$set) $set = {}; diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index aa036a37..3979ba24 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -43,7 +43,7 @@ export function getSingleProperty(creatureId, propertyId) { const prop = CreatureProperties.findOne({ _id: propertyId, 'ancestors.id': creatureId, - 'removed': {$ne: true}, + 'removed': { $ne: true }, }, { sort: { order: 1 }, }); @@ -61,7 +61,7 @@ export function getProperties(creatureId) { // console.time(`Cache miss on creature properties: ${creatureId}`) const props = CreatureProperties.find({ 'ancestors.id': creatureId, - 'removed': {$ne: true}, + 'removed': { $ne: true }, }, { sort: { order: 1 }, }).fetch(); @@ -73,7 +73,7 @@ export function getPropertiesOfType(creatureId, propType) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = [] - for (const prop of creature.properties.values()){ + for (const prop of creature.properties.values()) { if (prop.type === propType) { props.push(prop); } @@ -97,7 +97,7 @@ export function getCreature(creatureId) { if (loadedCreatures.has(creatureId)) { const loadedCreature = loadedCreatures.get(creatureId); const creature = loadedCreature.creature; - if (creature) { + if (creature) { const cloneCreature = EJSON.clone(creature); return cloneCreature; } @@ -113,16 +113,24 @@ export function getVariables(creatureId) { const loadedCreature = loadedCreatures.get(creatureId); const variables = loadedCreature.variables; if (variables) { - const cloneVarables = EJSON.clone(variables); - return cloneVarables; + const cloneVariables = EJSON.clone(variables); + return cloneVariables; } } // console.time(`Cache miss on variables: ${creatureId}`); - const variables = CreatureVariables.findOne({_creatureId: creatureId}); + const variables = CreatureVariables.findOne({ _creatureId: creatureId }); // console.timeEnd(`Cache miss on variables: ${creatureId}`); return variables; } +export function replaceLinkedVariablesWithProps(variables) { + for (const key in variables) { + const propId = variables[key]?._propId; + if (!propId) continue; + variables[key] = getSingleProperty(variables._creatureId, propId); + } +} + export function getProperyAncestors(creatureId, propertyId) { const prop = getSingleProperty(creatureId, propertyId); if (!prop) return []; @@ -148,7 +156,7 @@ export function getProperyAncestors(creatureId, propertyId) { // Fetch from database return CreatureProperties.find({ _id: { $in: ancestorIds }, - removed: {$ne: true}, + removed: { $ne: true }, }, { sort: { order: 1 }, }).fetch(); @@ -164,7 +172,7 @@ export function getPropertyDecendants(creatureId, propertyId) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = []; - for(const prop of creature.properties.values()){ + for (const prop of creature.properties.values()) { if (prop.ancestors[expectedAncestorPostition]?.id === propertyId) { props.push(prop); } @@ -216,7 +224,7 @@ class LoadedCreature { compute(); }, }); - + // Observe the creature itself self.creatureObserver = Creatures.find({ _id: creatureId, @@ -239,7 +247,7 @@ class LoadedCreature { self.variablesObserver = CreatureVariables.find({ _creatureId: creatureId, }, { - fields: { _creatureId: 0}, + fields: { _creatureId: 0 }, }).observeChanges({ added(id, fields) { fields._id = id; diff --git a/app/imports/client/ui/properties/forms/PointBuySpendForm.vue b/app/imports/client/ui/properties/forms/PointBuySpendForm.vue index de268236..92abe936 100644 --- a/app/imports/client/ui/properties/forms/PointBuySpendForm.vue +++ b/app/imports/client/ui/properties/forms/PointBuySpendForm.vue @@ -93,7 +93,7 @@