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 @@ - - - Attacks - - - + @@ -294,7 +285,6 @@ import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue'; import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue'; import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue'; - import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue'; import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue'; import RestButton from '/imports/ui/creature/RestButton.vue'; import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; @@ -337,7 +327,6 @@ SkillListTile, ResourceCard, SpellSlotListTile, - ActionListTile, ActionCard, }, props: { @@ -390,14 +379,7 @@ return getSkillOfType(this.creature, 'language'); }, actions(){ - let props = getProperties(this.creature, {type: 'action'}).map(action => { - action.children = getActiveProperties({ - ancestorId: action._id, - options: {sort: {order: 1}}, - }); - return action; - }); - return props; + return getProperties(this.creature, {type: 'action'}); }, attacks(){ let props = getProperties(this.creature, {type: 'attack'}).map(attack => { diff --git a/app/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue b/app/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue index 21d1f029..fa69f25f 100644 --- a/app/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue +++ b/app/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue @@ -160,7 +160,6 @@ export default { }); }, push({path, value, ack}){ - console.log({path, value, ack}); pushToProperty.call({_id: this._id, path, value}, (error) =>{ if (error) console.warn(error); ack && ack(error && error.reason || error); diff --git a/app/imports/ui/properties/components/actions/ActionCard.vue b/app/imports/ui/properties/components/actions/ActionCard.vue index 60476b6e..aeb9a60d 100644 --- a/app/imports/ui/properties/components/actions/ActionCard.vue +++ b/app/imports/ui/properties/components/actions/ActionCard.vue @@ -1,58 +1,106 @@ - + - + {{ rollBonus }} - $vuetify.icons.action + {{ actionTypeIcon }} - - + + {{ model.name }} - - action type text + + + {{ model.actionType }} + + + {{ usesLeft }} uses + - - {{ usesLeft }}/{{ totalUses }} - - - + + + + + + + + + diff --git a/app/imports/ui/properties/components/actions/ActionListTile.vue b/app/imports/ui/properties/components/actions/ActionListTile.vue deleted file mode 100644 index 9e978bf4..00000000 --- a/app/imports/ui/properties/components/actions/ActionListTile.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - {{ rollBonus }} - - - - - {{ model.name }} - - - - - - {{ usesLeft }}/{{ totalUses }} - - - - - - - - diff --git a/app/imports/ui/properties/components/actions/AttributeConsumedView.vue b/app/imports/ui/properties/components/actions/AttributeConsumedView.vue new file mode 100644 index 00000000..69485dd0 --- /dev/null +++ b/app/imports/ui/properties/components/actions/AttributeConsumedView.vue @@ -0,0 +1,34 @@ + + + + {{ model.quantity }} + + + {{ model.statName }} + + + + + diff --git a/app/imports/ui/properties/components/actions/ItemConsumedView.vue b/app/imports/ui/properties/components/actions/ItemConsumedView.vue new file mode 100644 index 00000000..da7cedbb --- /dev/null +++ b/app/imports/ui/properties/components/actions/ItemConsumedView.vue @@ -0,0 +1,108 @@ + + + + + + + + {{ model.available }} + + + {{ model.available }} / {{ model.quantity }} + + + + + + {{ model.itemName }} + + + Select ammo + + + + arrow_drop_down + + + + + + + + + + + diff --git a/app/imports/ui/properties/components/actions/SelectItemToConsume.vue b/app/imports/ui/properties/components/actions/SelectItemToConsume.vue new file mode 100644 index 00000000..6eb6e31c --- /dev/null +++ b/app/imports/ui/properties/components/actions/SelectItemToConsume.vue @@ -0,0 +1,73 @@ + + + + + + + + + No equipped items found with the tag "{{ itemConsumed.tag }}" + + + + + + + diff --git a/app/imports/ui/properties/forms/ResourcesForm.vue b/app/imports/ui/properties/forms/ResourcesForm.vue index 17811b83..60337f0e 100644 --- a/app/imports/ui/properties/forms/ResourcesForm.vue +++ b/app/imports/ui/properties/forms/ResourcesForm.vue @@ -83,7 +83,6 @@ this.addResourceLoading = false; }, addAttributesConsumed(){ - console.log(AttributeConsumedSchema.clean({})); this.addResourceLoading = true; this.$emit('push', { path: ['attributesConsumed'], diff --git a/app/imports/ui/properties/treeNodeViews/TreeNodeView.vue b/app/imports/ui/properties/treeNodeViews/TreeNodeView.vue new file mode 100644 index 00000000..6a48a281 --- /dev/null +++ b/app/imports/ui/properties/treeNodeViews/TreeNodeView.vue @@ -0,0 +1,30 @@ + + + + +