From 3f540d0f142fb4cfb0b8d10dcc61ceedf2e09638 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 15 Jun 2020 22:30:27 +0200 Subject: [PATCH] Overhaul of character action components, actions now consume resources --- .../api/creature/CreatureProperties.js | 41 +++- .../creature/computation/ComputationMemo.js | 10 + .../computation/computeEndStepProperty.js | 18 +- .../creature/computation/recomputeCreature.js | 1 + app/imports/api/properties/Actions.js | 20 ++ app/imports/ui/components/tree/TreeNode.vue | 15 +- app/imports/ui/creature/RestButton.vue | 1 - .../character/characterSheetTabs/StatsTab.vue | 38 +--- .../CreaturePropertyDialog.vue | 1 - .../components/actions/ActionCard.vue | 190 ++++++++++++++---- .../components/actions/ActionListTile.vue | 100 --------- .../actions/AttributeConsumedView.vue | 34 ++++ .../components/actions/ItemConsumedView.vue | 108 ++++++++++ .../actions/SelectItemToConsume.vue | 73 +++++++ .../ui/properties/forms/ResourcesForm.vue | 1 - .../properties/treeNodeViews/TreeNodeView.vue | 30 +++ 16 files changed, 502 insertions(+), 179 deletions(-) delete mode 100644 app/imports/ui/properties/components/actions/ActionListTile.vue create mode 100644 app/imports/ui/properties/components/actions/AttributeConsumedView.vue create mode 100644 app/imports/ui/properties/components/actions/ItemConsumedView.vue create mode 100644 app/imports/ui/properties/components/actions/SelectItemToConsume.vue create mode 100644 app/imports/ui/properties/treeNodeViews/TreeNodeView.vue diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js index c6c6e446..e09c93e4 100644 --- a/app/imports/api/creature/CreatureProperties.js +++ b/app/imports/api/creature/CreatureProperties.js @@ -343,7 +343,45 @@ const adjustQuantity = new ValidatedMethod({ let currentProperty = CreatureProperties.findOne(_id); // Check permissions assertPropertyEditPermission(currentProperty, this.userId); - adjustQuantityWork({property: currentProperty, operation, value}) + adjustQuantityWork({property: currentProperty, operation, value}); + recomputeCreatures(currentProperty); + }, +}); + +const selectAmmoItem = new ValidatedMethod({ + name: 'creatureProperties.selectAmmoItem', + validate: new SimpleSchema({ + actionId: SimpleSchema.RegEx.Id, + itemId: SimpleSchema.RegEx.Id, + itemConsumedIndex: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({actionId, itemId, itemConsumedIndex}) { + let action = CreatureProperties.findOne(actionId); + // Check permissions + assertPropertyEditPermission(action, this.userId); + // Check that this index has a document to edit + let itemConsumed = action.resources.itemsConsumed[itemConsumedIndex]; + if (!itemConsumed){ + throw new Meteor.Error('Resouce not found', + 'Could not set ammo, because the ammo document was not found'); + } + let itemToLink = CreatureProperties.findOne(itemId); + if (!itemToLink){ + throw new Meteor.Error('Item not found', + 'Could not set ammo: the item was not found'); + } + let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`; + CreatureProperties.update(actionId, { + $set: {[path]: itemId} + }, { + selector: action, + }); + recomputeCreatures(action); }, }); @@ -416,6 +454,7 @@ export { updateProperty, damageProperty, adjustQuantity, + selectAmmoItem, pushToProperty, pullFromProperty, softRemoveProperty, diff --git a/app/imports/api/creature/computation/ComputationMemo.js b/app/imports/api/creature/computation/ComputationMemo.js index 3ee18e20..c52539c8 100644 --- a/app/imports/api/creature/computation/ComputationMemo.js +++ b/app/imports/api/creature/computation/ComputationMemo.js @@ -15,6 +15,8 @@ export default class ComputationMemo { this.classes = {}; this.togglesById = {}; this.toggleIds = new Set(); + // Equipped items that might be used as ammo + this.equipmentById = {}; // Properties that have calculations, but don't impact other properties this.endStepPropsById = {}; // First note all the ids of all the toggles @@ -40,6 +42,10 @@ export default class ComputationMemo { ) { // Add all the stats this.addStat(prop); + } else if ( + prop.type === 'item' + ) { + this.addEquipment(prop); } else { return true; } @@ -185,6 +191,10 @@ export default class ComputationMemo { }); return targets; } + addEquipment(prop){ + prop = this.registerProperty(prop); + this.equipmentById[prop._id] = prop; + } addEndStepProp(prop){ prop = this.registerProperty(prop); this.endStepPropsById[prop._id] = prop; diff --git a/app/imports/api/creature/computation/computeEndStepProperty.js b/app/imports/api/creature/computation/computeEndStepProperty.js index 3241815c..a38c3e5a 100644 --- a/app/imports/api/creature/computation/computeEndStepProperty.js +++ b/app/imports/api/creature/computation/computeEndStepProperty.js @@ -37,6 +37,7 @@ function computeAction(prop, memo){ if (attConsumed.variableName){ let stat = memo.statsByVariableName[attConsumed.variableName]; prop.resources.attributesConsumed[i].statId = stat && stat._id; + prop.resources.attributesConsumed[i].statName = stat && stat.name; let available = stat && stat.currentValue || 0; prop.resources.attributesConsumed[i].available = available; if (available < attConsumed.quantity){ @@ -45,7 +46,22 @@ function computeAction(prop, memo){ } }); // Items consumed - // TODO + prop.resources.itemsConsumed.forEach((itemConsumed, i) => { + let item = itemConsumed.itemId && memo.equipmentById[itemConsumed.itemId]; + prop.resources.itemsConsumed[i].itemId = item && item._id; + let available = item && item.quantity || 0; + prop.resources.itemsConsumed[i].available = available; + let name = item && item.name; + if (item && item.quantity !== 1 && item.plural){ + name = item.plural; + } + prop.resources.itemsConsumed[i].itemName = name; + prop.resources.itemsConsumed[i].itemIcon = item && item.icon; + prop.resources.itemsConsumed[i].itemColor = item && item.color; + if (!item || available < itemConsumed.quantity){ + prop.insufficientResources = true; + } + }); } function computeAttack(prop, memo){ diff --git a/app/imports/api/creature/computation/recomputeCreature.js b/app/imports/api/creature/computation/recomputeCreature.js index b2bb007e..ca0323ca 100644 --- a/app/imports/api/creature/computation/recomputeCreature.js +++ b/app/imports/api/creature/computation/recomputeCreature.js @@ -42,6 +42,7 @@ const calculationPropertyTypes = [ 'proficiency', 'classLevel', 'toggle', + 'item', // End step types 'action', 'attack', diff --git a/app/imports/api/properties/Actions.js b/app/imports/api/properties/Actions.js index 7938f22d..38fedd42 100644 --- a/app/imports/api/properties/Actions.js +++ b/app/imports/api/properties/Actions.js @@ -1,5 +1,6 @@ import SimpleSchema from 'simpl-schema'; import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; +import { storedIconsSchema } from '/imports/api/icons/Icons.js' /* * Actions are things a character can do @@ -133,6 +134,19 @@ const ComputedOnlyActionSchema = new SimpleSchema({ // This appears both in the computed and uncomputed schema because it can be // set by both a computation or a form 'resources.itemsConsumed.$.itemId': { + type: String, + regEx: SimpleSchema.RegEx.Id, + optional: true, + }, + 'resources.itemsConsumed.$.itemName': { + type: String, + optional: true, + }, + 'resources.itemsConsumed.$.itemIcon': { + type: storedIconsSchema, + optional: true, + }, + 'resources.itemsConsumed.$.itemColor': { type: String, optional: true, }, @@ -147,6 +161,12 @@ const ComputedOnlyActionSchema = new SimpleSchema({ regEx: SimpleSchema.RegEx.Id, optional: true, }, + 'resources.attributesConsumed.$.statName': { + type: String, + optional: true, + }, + // True if the uses left is zero, or any item or attribute consumed is + // insufficient insufficientResources: { type: Boolean, optional: true, diff --git a/app/imports/ui/components/tree/TreeNode.vue b/app/imports/ui/components/tree/TreeNode.vue index 804376f0..967e5a12 100644 --- a/app/imports/ui/components/tree/TreeNode.vue +++ b/app/imports/ui/components/tree/TreeNode.vue @@ -34,8 +34,7 @@ drag_handle - @@ -80,13 +79,13 @@ **/ import { canBeParent } from '/imports/api/parenting/parenting.js'; import { getPropertyIcon } from '/imports/constants/PROPERTIES.js'; - import treeNodeViewIndex from '/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js'; + import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue'; export default { name: 'TreeNode', - components: { - ...treeNodeViewIndex - }, + components: { + TreeNodeView, + }, props: { node: Object, group: String, @@ -100,10 +99,6 @@ expanded: false, }}, computed: { - treeNodeView(){ - let type = this.node.type; - return treeNodeViewIndex[type] || treeNodeViewIndex.default; - }, hasChildren(){ return this.children && this.children.length || this.lazy && !this.expanded; }, diff --git a/app/imports/ui/creature/RestButton.vue b/app/imports/ui/creature/RestButton.vue index 011fadd2..c137551f 100644 --- a/app/imports/ui/creature/RestButton.vue +++ b/app/imports/ui/creature/RestButton.vue @@ -1,7 +1,6 @@