diff --git a/app/imports/api/creature/creatureProperties/CreatureProperties.js b/app/imports/api/creature/creatureProperties/CreatureProperties.js index 653f7346..cccf715c 100644 --- a/app/imports/api/creature/creatureProperties/CreatureProperties.js +++ b/app/imports/api/creature/creatureProperties/CreatureProperties.js @@ -84,12 +84,12 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({ }, // Dependency tree, the ID of the lowest ordered doc connected to this doc // via dependencies - /*depGroupId: { + depGroupId: { type: String, regEx: SimpleSchema.RegEx.Id, index: 1, removeBeforeCompute: true, - }*/ + }, }); CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema); diff --git a/app/imports/api/creature/creatureProperties/methods/damageProperty.js b/app/imports/api/creature/creatureProperties/methods/damageProperty.js index 573e004a..2c648c9c 100644 --- a/app/imports/api/creature/creatureProperties/methods/damageProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/damageProperty.js @@ -4,7 +4,7 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { computeCreatureDependencyGroup } from '/imports/api/engine/computeCreature.js'; +import computeCreature, { computeCreatureDependencyGroup } from '/imports/api/engine/computeCreature.js'; const damageProperty = new ValidatedMethod({ name: 'creatureProperties.damage', @@ -37,9 +37,13 @@ const damageProperty = new ValidatedMethod({ `Property of type "${property.type}" can't be damaged` ); } - let result = damagePropertyWork({property, operation, value}); - // Dependencies can't be changed through damage, only recompute deps - computeCreatureDependencyGroup(property); + let result = damagePropertyWork({ property, operation, value }); + if (property.depGroupId) { + // Dependencies can't be changed through damage, only recompute deps + computeCreatureDependencyGroup([property.depGroupId], rootCreature._id); + } else { + computeCreature(rootCreature._id); + } return result; }, }); diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index 8350051c..f86e610b 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -15,7 +15,7 @@ import linkTypeDependencies from './buildComputation/linkTypeDependencies.js'; import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js'; import CreatureComputation from './CreatureComputation.js'; import removeSchemaFields from './buildComputation/removeSchemaFields.js'; -// import assignDependencyGroups from '/imports/api/engine/computation/utility/assignDependencyGroups.js'; +import assignDependencyGroups from '/imports/api/engine/computation/utility/assignDependencyGroups.js'; /** * Store index of properties @@ -56,6 +56,10 @@ function getProperties(creatureId) { } function getGroupProperties(depGroupIds) { + console.log({ depGroupIds }); + if (!depGroupIds || depGroupIds.includes(undefined)) { + throw `Expected array full of ids, got ${depGroupIds}` + } return CreatureProperties.find({ depGroupId: { $in: depGroupIds }, 'removed': { $ne: true }, @@ -135,7 +139,7 @@ export function buildComputationFromProps(properties, creature){ }); // Store the connected groups of the dependency graph - // assignDependencyGroups(dependencyGraph); + assignDependencyGroups(dependencyGraph); return computation; } diff --git a/app/imports/api/engine/computation/utility/assignDependencyGroups.js b/app/imports/api/engine/computation/utility/assignDependencyGroups.js index a93c2a9e..7a660adf 100644 --- a/app/imports/api/engine/computation/utility/assignDependencyGroups.js +++ b/app/imports/api/engine/computation/utility/assignDependencyGroups.js @@ -1,4 +1,10 @@ -export default function assignDependencyGroups(graph) { +import { union } from "lodash"; + +export function assignDependencyGroups(graph) { + console.log('assigning dep group ids'); + graph.forEachLink(function (link) { + console.dir(link); + }); // Iterate through all the nodes graph.forEachNode(node => { if (node._depGroupVisited) { @@ -13,22 +19,20 @@ export default function assignDependencyGroups(graph) { while (stack.length) { top = stack.pop(); if (top._depGroupVisited) continue; - if ( - (lowestOrderId === undefined && top.data?._id) || - (top.data?._id && top.data?.order < lowestOrder) - ) { - lowestOrderId = top.data?._id; - lowestOrder = top.data?.order; - } - if (top.data?._id) { - group.push(top) + if (top.data?._id && ( + lowestOrderId === undefined || + top.data?.order < lowestOrder + )) { + lowestOrderId = top.data._id; + lowestOrder = top.data.order; } + group.push(top) top._depGroupVisited = true; graph.forEachLinkedNode(top.id, linkedNode => stack.push(linkedNode)); } // Assign group id group.forEach(node => { - if (!lowestOrderId) return; + if (!node.data?._id) return; if (group.length > 1) { node.data.depGroupId = lowestOrderId; } else { @@ -36,4 +40,34 @@ export default function assignDependencyGroups(graph) { } }); }); -} \ No newline at end of file +} + + +export default function assignDependencyGroups2(graph) { + const groups = new Set(); + graph.forEachLink(function (link) { + const from = graph.getNode(link.fromId); + const to = graph.getNode(link.toId); + let depGroup; + if (from._depGroup) { + depGroup = from._depGroup; + groups.delete(to._depGroup); + } else if (to._depGroup) { + depGroup = to._depGroup; + } else { + depGroup = {}; + groups.add(depGroup); + } + depGroup.nodes = union(from._depGroup?.nodes, to._depGroup?.nodes, [from, to]) + from._depGroup = depGroup; + to._depGroup = depGroup; + }); + groups.forEach(g => { + if (!g.nodes.length) return; + const rootId = g.nodes[0].id; + g.nodes.forEach(n => { + if (!n.data?._id) return; + n.data.depGroupId = rootId; + }); + }); +} diff --git a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js index 82029cfc..20703856 100644 --- a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js +++ b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js @@ -36,7 +36,7 @@ export default function writeAlteredProperties(computation){ function addChangedKeysToOp(op, keys, original, changed) { // Loop through all keys that can be changed by computation // and compile an operation that sets all those keys - for (let key of keys){ + for (let key of keys) { if (!EJSON.equals(original[key], changed[key])){ if (!op) op = newOperation(original._id, changed.type); let value = changed[key]; diff --git a/app/imports/api/properties/PointBuys.js b/app/imports/api/properties/PointBuys.js new file mode 100644 index 00000000..95cc1634 --- /dev/null +++ b/app/imports/api/properties/PointBuys.js @@ -0,0 +1,96 @@ +import SimpleSchema from 'simpl-schema'; +import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; +import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js'; +import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js'; + +/* + * PointBuys are reason-value attached to skills and abilities + * that modify their final value or presentation in some way + */ +let PointBuySchema = createPropertySchema({ + name: { + type: String, + optional: true, + max: STORAGE_LIMITS.name, + }, + variableName: { + type: String, + optional: true, + regEx: VARIABLE_NAME_REGEX, + min: 2, + max: STORAGE_LIMITS.variableName, + }, + ignored: { + type: Boolean, + optional: true, + }, + 'values': { + type: Array, + defaultValue: [], + }, + 'values.$': { + type: Object, + }, + 'values.$.name': { + type: String, + optional: true, + max: STORAGE_LIMITS.name, + }, + 'values.$.variableName': { + type: String, + optional: true, + regEx: VARIABLE_NAME_REGEX, + min: 2, + max: STORAGE_LIMITS.variableName, + }, + 'values.$.value': { + type: Number, + optional: true, + }, + min: { + type: 'fieldToCompute', + optional: true, + }, + max: { + type: 'fieldToCompute', + optional: true, + }, + total: { + type: 'fieldToCompute', + optional: true, + }, + cost: { + type: 'fieldToCompute', + optional: true, + }, +}); + +const ComputedOnlyPointBuySchema = createPropertySchema({ + min: { + type: 'computedOnlyField', + optional: true, + }, + max: { + type: 'computedOnlyField', + optional: true, + }, + total: { + type: 'computedOnlyField', + optional: true, + }, + cost: { + type: 'computedOnlyField', + optional: true, + }, + spent: { + type: Number, + optional: true, + removeBeforeCompute: true, + }, +}); + +const ComputedPointBuySchema = new SimpleSchema() + .extend(ComputedOnlyPointBuySchema) + .extend(PointBuySchema); + +export { PointBuySchema, ComputedPointBuySchema, ComputedOnlyPointBuySchema };