diff --git a/app/imports/api/creature/creatures/Creatures.js b/app/imports/api/creature/creatures/Creatures.js index 375d29b9..e75eb3c6 100644 --- a/app/imports/api/creature/creatures/Creatures.js +++ b/app/imports/api/creature/creatures/Creatures.js @@ -120,6 +120,20 @@ let CreatureSchema = new SimpleSchema({ blackbox: true, defaultValue: {} }, + computeErrors: { + type: Array, + optional: true, + }, + 'computeErrors.$': { + type: Object, + }, + 'computeErrors.$.type': { + type: String, + }, + 'computeErrors.$.details' : { + type: Object, + blackbox: true, + }, // Tabletop tabletop: { diff --git a/app/imports/api/engine/computation/CreatureComputation.js b/app/imports/api/engine/computation/CreatureComputation.js index a806ddd6..d6c45ebe 100644 --- a/app/imports/api/engine/computation/CreatureComputation.js +++ b/app/imports/api/engine/computation/CreatureComputation.js @@ -10,6 +10,7 @@ export default class CreatureComputation { this.scope = {}; this.props = properties; this.dependencyGraph = createGraph(); + this.errors = []; // Store properties for easy access later properties.forEach(prop => { diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/aggregate/aggregateClassLevel.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/aggregate/aggregateClassLevel.js index 22197dc2..24ece344 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/aggregate/aggregateClassLevel.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/aggregate/aggregateClassLevel.js @@ -1,6 +1,6 @@ export default function aggregateClassLevel({node, linkedNode, link}){ + if (node.data.inactive) return; if (link.data === 'classLevel'){ - if (node.data.inactive) return; if (!node.data.classLevelAggregator) node.data.classLevelAggregator = { levelsFilled: [true], // Level 0 is always filled level: 0, @@ -11,6 +11,6 @@ export default function aggregateClassLevel({node, linkedNode, link}){ aggregator.levelsFilled[linkedProp.level] = true; } else if (link.data === 'level'){ node.data.baseValue = (node.data.baseValue || 0) + - linkedNode.data.classLevelAggregator.level; + (linkedNode.data.classLevelAggregator?.level || 0); } } diff --git a/app/imports/api/engine/computation/computeCreatureComputation.js b/app/imports/api/engine/computation/computeCreatureComputation.js index 5eba016d..fbaafe28 100644 --- a/app/imports/api/engine/computation/computeCreatureComputation.js +++ b/app/imports/api/engine/computation/computeCreatureComputation.js @@ -1,6 +1,7 @@ import computeToggles from '/imports/api/engine/computation/computeComputation/computeToggles.js'; import computeByType from '/imports/api/engine/computation/computeComputation/computeByType.js'; import embedInlineCalculations from './utility/embedInlineCalculations.js'; +import path from 'ngraph.path'; export default function computeCreatureComputation(computation){ const stack = []; @@ -27,7 +28,7 @@ export default function computeCreatureComputation(computation){ } else { top._visitedChildren = true; // Push dependencies to graph to be computed first - pushDependenciesToStack(top.id, graph, stack); + pushDependenciesToStack(top.id, graph, stack, computation); } } @@ -42,8 +43,20 @@ function compute(computation, node){ computeByType[node.data?.type || '_variable']?.(computation, node); } -function pushDependenciesToStack(nodeId, graph, stack){ +function pushDependenciesToStack(nodeId, graph, stack, computation){ graph.forEachLinkedNode(nodeId, linkedNode => { + if (linkedNode._visitedChildren && !linkedNode._visited){ + const pather = path.nba(graph, { + oriented: true + }); + const loop = pather.find(nodeId, nodeId); + computation.errors.push({ + type: 'dependencyLoop', + details: { + nodes: loop.map(node => node.id) + }, + }); + } stack.push(linkedNode); }, true); } diff --git a/app/imports/api/engine/computation/writeComputation/writeErrors.js b/app/imports/api/engine/computation/writeComputation/writeErrors.js new file mode 100644 index 00000000..66002515 --- /dev/null +++ b/app/imports/api/engine/computation/writeComputation/writeErrors.js @@ -0,0 +1,9 @@ +import Creatures from '/imports/api/creature/creatures/Creatures.js'; + +export default function(creatureId, errors = []){ + if (errors.length){ + Creatures.update(creatureId, {$set: {computeErrors: errors}}); + } else { + Creatures.update(creatureId, {$unset: {computeErrors: 1}}); + } +} diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index 58fe1ed6..fc142270 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -2,12 +2,14 @@ import buildCreatureComputation from './computation/buildCreatureComputation.js' import computeCreatureComputation from './computation/computeCreatureComputation.js'; import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js'; import writeScope from './computation/writeComputation/writeScope.js'; +import writeErrors from './computation/writeComputation/writeErrors.js'; export default function computeCreature(creatureId){ const computation = buildCreatureComputation(creatureId); computeCreatureComputation(computation); writeAlteredProperties(computation); writeScope(creatureId, computation.scope); + writeErrors(creatureId, computation.errors); } // For now just recompute the whole creature, TODO only recompute a single diff --git a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue index 67fcd039..d47b5132 100644 --- a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue +++ b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue @@ -4,7 +4,7 @@ v-on="hasClickListener ? {click} : {}" > - +
+ + + {{model.total}} + + {{ model.name }} diff --git a/app/package-lock.json b/app/package-lock.json index 26584fab..359dfda4 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "dicecloud", - "version": "0.10.0", + "version": "2.0.33", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2399,6 +2399,11 @@ "ngraph.events": "^1.2.1" } }, + "ngraph.path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ngraph.path/-/ngraph.path-1.4.0.tgz", + "integrity": "sha512-yJZay4tP0wcjqkkf8zlMQ/T+JOgU+EWfdE4w4TG8OS94B12J/+Z44UOYxVJErE8E6/wFunX1hMZEB1/GHsBYHg==" + }, "node-addon-api": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", @@ -2775,7 +2780,7 @@ }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "simpl-schema": { diff --git a/app/package.json b/app/package.json index 23d4e20a..5d745db7 100644 --- a/app/package.json +++ b/app/package.json @@ -38,6 +38,7 @@ "moo": "^0.5.1", "nearley": "^2.19.1", "ngraph.graph": "^19.1.0", + "ngraph.path": "^1.4.0", "qrcode": "^1.5.0", "request": "^2.88.2", "simpl-schema": "^1.12.0",