diff --git a/app/imports/api/creature/creatureProperties/methods/flipToggle.js b/app/imports/api/creature/creatureProperties/methods/flipToggle.js new file mode 100644 index 00000000..e39d0c75 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/flipToggle.js @@ -0,0 +1,48 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; +import computeCreature from '/imports/api/engine/computeCreature.js'; + +const flipToggle = new ValidatedMethod({ + name: 'creatureProperties.flipToggle', + validate({_id}){ + if (!_id) throw new Meteor.Error('No _id', '_id is required'); + }, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id}) { + // Permission + let property = CreatureProperties.findOne(_id, { + fields: {type: 1, ancestors: 1, enabled: 1, disabled: 1} + }); + if (property.type !== 'toggle'){ + throw new Meteor.Error('wrong property', + 'This method can only be applied to toggles'); + } + if (!property.enabled && !property.disabled){ + throw new Meteor.Error('Computed toggle', + 'Can\'t flip a toggle that is computed') + } + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Invert the current value, disabled is the canonical store of value + const currentValue = !property.disabled; + CreatureProperties.update(_id, {$set: { + enabled: !currentValue, + disabled: currentValue, + }}, { + selector: {type: 'toggle'}, + }); + + // Updating a toggle is likely to change the whole tree, do a full recompute + computeCreature(rootCreature._id); + }, +}); + +export default flipToggle; diff --git a/app/imports/api/creature/creatureProperties/methods/index.js b/app/imports/api/creature/creatureProperties/methods/index.js index 72fe3da3..45a0e323 100644 --- a/app/imports/api/creature/creatureProperties/methods/index.js +++ b/app/imports/api/creature/creatureProperties/methods/index.js @@ -12,3 +12,4 @@ import '/imports/api/creature/creatureProperties/methods/restoreProperty.js'; import '/imports/api/creature/creatureProperties/methods/selectAmmoItem.js'; import '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js'; import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js'; +import '/imports/api/creature/creatureProperties/methods/flipToggle.js'; diff --git a/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js b/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js index 2eb41428..b289e1c6 100644 --- a/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js +++ b/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js @@ -3,9 +3,14 @@ import walkDown from '/imports/api/engine/computation/utility/walkdown.js'; export default function computeInactiveStatus(node){ const prop = node.node; if (isActive(prop)) return; - // Unequipped items, notes, and actions disable their children, + // Unequipped items, notes, spells, and actions disable their children, // but are not disabled themselves - if (prop.type !== 'item' && prop.type !== 'note' && prop.type !== 'action' ){ + if ( + prop.type !== 'item' && + prop.type !== 'note' && + prop.type !== 'action' && + prop.type !== 'spell' + ){ prop.inactive = true; prop.deactivatedBySelf = true; } @@ -20,7 +25,7 @@ function isActive(prop){ if (prop.disabled) return false; switch (prop.type){ case 'item': return !!prop.equipped; - case 'spell': return !!prop.prepared || !!prop.alwaysPrepared; + case 'spell': return false; case 'note': return false; case 'action': return false; default: return true; diff --git a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js index d6e30222..af007302 100644 --- a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js +++ b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js @@ -9,6 +9,7 @@ const linkDependenciesByType = { effect: linkStats, skill: linkSkill, spell: linkResources, + toggle: linkVariableName, } export default function linkTypeDependencies(dependencyGraph, prop, computation){ diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable.js index ee9685db..0f9bdab1 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable.js @@ -3,6 +3,7 @@ import computeVariableAsAttribute from './computeVariable/computeVariableAsAttri import computeVariableAsSkill from './computeVariable/computeVariableAsSkill.js'; import computeVariableAsConstant from './computeVariable/computeVariableAsConstant.js'; import computeVariableAsClass from './computeVariable/computeVariableAsClass.js'; +import computeVariableAsToggle from './computeVariable/computeVariableAsToggle.js'; import computeImplicitVariable from './computeVariable/computeImplicitVariable.js'; export default function computeVariable(computation, node){ @@ -50,13 +51,15 @@ function combineAggregations(computation, node){ function computeVariableProp(computation, node, prop){ if (!prop) return; if (prop.type === 'attribute'){ - computeVariableAsAttribute(computation, node, prop) + computeVariableAsAttribute(computation, node, prop); } else if (prop.type === 'skill'){ - computeVariableAsSkill(computation, node, prop) + computeVariableAsSkill(computation, node, prop); } else if (prop.type === 'constant'){ - computeVariableAsConstant(computation, node, prop) + computeVariableAsConstant(computation, node, prop); } else if (prop.type === 'class'){ - computeVariableAsClass(computation, node, prop) + computeVariableAsClass(computation, node, prop); + } else if (prop.type === 'toggle'){ + computeVariableAsToggle(computation, node, prop); } } diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/computeVariableAsToggle.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/computeVariableAsToggle.js new file mode 100644 index 00000000..0c3c41a9 --- /dev/null +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeVariable/computeVariableAsToggle.js @@ -0,0 +1,7 @@ +import getAggregatorResult from './getAggregatorResult.js'; + +export default function computeVariableAsToggle(computation, node, prop){ + let result = getAggregatorResult(node, prop) || 0; + + prop.value = !!result || !!prop.enabled || !!prop.condition?.value; +} diff --git a/app/imports/api/engine/computation/computeCreatureComputation.js b/app/imports/api/engine/computation/computeCreatureComputation.js index 2c542c92..d91c8dfc 100644 --- a/app/imports/api/engine/computation/computeCreatureComputation.js +++ b/app/imports/api/engine/computation/computeCreatureComputation.js @@ -36,6 +36,7 @@ function compute(computation, node){ // Determine the prop's active status by its toggles computeToggles(computation, node); computeCalculations(computation, node); + if (node.data) delete node.data._computationDetails; // Compute the property by type computeByType[node.data?.type || '_variable']?.(computation, node); } diff --git a/app/imports/api/properties/SavingThrows.js b/app/imports/api/properties/SavingThrows.js index 70fc8485..f5cfbf70 100644 --- a/app/imports/api/properties/SavingThrows.js +++ b/app/imports/api/properties/SavingThrows.js @@ -18,7 +18,7 @@ let SavingThrowSchema = createPropertySchema({ // Who this saving throw applies to target: { type: String, - defaultValue: 'every', + defaultValue: 'target', allowedValues: [ 'self', 'target', diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index e36938e8..e9fa76f6 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -75,6 +75,18 @@ +