From 449a4fba7d20a20763102407aa02cd9425727ab9 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 4 Feb 2021 11:38:29 +0200 Subject: [PATCH] Refactored creature property methods into separate documents, might have broken a lot of things --- .../api/creature/CreatureProperties.js | 609 ------------------ app/imports/api/creature/Creatures.js | 2 +- .../api/creature/actions/applyAdjustment.js | 2 +- app/imports/api/creature/actions/applyBuff.js | 2 +- .../api/creature/actions/applyDamage.js | 2 +- .../api/creature/actions/castSpellWithSlot.js | 3 +- app/imports/api/creature/actions/doAction.js | 8 +- .../api/creature/actions/spendResources.js | 3 +- .../computation/getComputationProperties.js | 2 +- .../creature/computation/recomputeCreature.js | 14 + .../computation/writeAlteredProperties.js | 2 +- .../creatureProperties/CreatureProperties.js | 77 +++ .../assertPropertyEditPermission.js | 7 - .../getClosestPropertyAncestorCreature.js | 7 - .../getClosestPropertyAncestorCreatureId.js | 13 - .../getRootCreatureAncestor.js | 5 + .../methods/adjustQuantity.js | 66 ++ .../methods/damagePropertiesByName.js | 57 ++ .../methods/damageProperty.js | 77 +++ .../creatureProperties/methods/dealDamage.js | 68 ++ .../methods/duplicateProperty.js | 33 + .../equipItem.js} | 10 +- .../creatureProperties/methods/index.js | 14 + .../methods/insertProperty.js | 40 ++ .../methods/insertPropertyFromLibraryNode.js | 103 +++ .../methods/pullFromProperty.js | 36 ++ .../methods/pushToProperty.js | 35 + .../methods/restoreProperty.js | 34 + .../methods/selectAmmoItem.js | 52 ++ .../methods/softRemoveProperty.js | 34 + .../methods/updateCreatureProperty.js | 54 ++ .../recomputeCreaturesByProperty.js | 12 + .../denormalise/recomputeDamageMultipliers.js | 2 +- .../recomputeInactiveProperties.js | 2 +- .../denormalise/recomputeInventory.js | 2 +- .../denormalise/recomputeSlotFullness.js | 2 +- app/imports/api/creature/removeCreature.js | 2 +- app/imports/api/creature/restCreature.js | 2 +- .../server/cron/deleteSoftRemovedDocuments.js | 2 +- .../server/publications/singleCharacter.js | 2 +- .../server/publications/slotFillers.js | 2 +- .../characterSheetTabs/CharacterTab.vue | 2 +- .../characterSheetTabs/FeaturesTab.vue | 2 +- .../characterSheetTabs/InventoryTab.vue | 12 +- .../characterSheetTabs/SpellsTab.vue | 2 +- .../character/characterSheetTabs/StatsTab.vue | 5 +- .../character/characterSheetTabs/TreeTab.vue | 7 +- .../CreaturePropertiesTree.vue | 2 +- .../CreaturePropertyDialog.vue | 10 +- .../ui/creature/slots/SlotFillDialog.vue | 2 +- app/imports/ui/creature/slots/Slots.vue | 6 +- .../components/actions/ActionCard.vue | 2 +- .../actions/SelectItemToConsume.vue | 4 +- .../attributes/HealthBarCardContainer.vue | 5 +- .../components/inventory/ContainerCard.vue | 2 +- .../components/inventory/ItemList.vue | 4 +- .../components/inventory/ItemListTile.vue | 2 +- .../spells/CastSpellWithSlotDialog.vue | 2 +- .../components/spells/SpellListCard.vue | 2 +- .../components/spells/SpellListTile.vue | 4 +- .../shared/lists/createListOfProperties.js | 2 +- .../ui/properties/viewers/ActionViewer.vue | 4 +- .../ui/properties/viewers/AttributeViewer.vue | 2 +- .../ui/properties/viewers/ItemViewer.vue | 2 +- .../ui/properties/viewers/SkillViewer.vue | 2 +- .../ui/tabletop/TabletopActionCards.vue | 2 +- 66 files changed, 882 insertions(+), 710 deletions(-) delete mode 100644 app/imports/api/creature/CreatureProperties.js create mode 100644 app/imports/api/creature/creatureProperties/CreatureProperties.js delete mode 100644 app/imports/api/creature/creatureProperties/assertPropertyEditPermission.js delete mode 100644 app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js delete mode 100644 app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js create mode 100644 app/imports/api/creature/creatureProperties/getRootCreatureAncestor.js create mode 100644 app/imports/api/creature/creatureProperties/methods/adjustQuantity.js create mode 100644 app/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js create mode 100644 app/imports/api/creature/creatureProperties/methods/damageProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/dealDamage.js create mode 100644 app/imports/api/creature/creatureProperties/methods/duplicateProperty.js rename app/imports/api/creature/creatureProperties/{manageEquipment.js => methods/equipItem.js} (83%) create mode 100644 app/imports/api/creature/creatureProperties/methods/index.js create mode 100644 app/imports/api/creature/creatureProperties/methods/insertProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js create mode 100644 app/imports/api/creature/creatureProperties/methods/pullFromProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/pushToProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/restoreProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js create mode 100644 app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js create mode 100644 app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js create mode 100644 app/imports/api/creature/creatureProperties/recomputeCreaturesByProperty.js diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js deleted file mode 100644 index 89ed39bc..00000000 --- a/app/imports/api/creature/CreatureProperties.js +++ /dev/null @@ -1,609 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Mongo } from 'meteor/mongo'; -import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import { ValidatedMethod } from 'meteor/mdg:validated-method'; -import SimpleSchema from 'simpl-schema'; -import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; -import ChildSchema, { RefSchema } from '/imports/api/parenting/ChildSchema.js'; -import { recomputeCreature } from '/imports/api/creature/computation/recomputeCreature.js'; -import LibraryNodes from '/imports/api/library/LibraryNodes.js'; -import Creatures from '/imports/api/creature/Creatures.js'; -import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { softRemove, restore } from '/imports/api/parenting/softRemove.js'; -import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js'; -import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js'; -import { - setLineageOfDocs, - getAncestry, - renewDocIds -} from '/imports/api/parenting/parenting.js'; -import {setDocToLastOrder} from '/imports/api/parenting/order.js'; -import { storedIconsSchema } from '/imports/api/icons/Icons.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; - -import '/imports/api/creature/actions/doAction.js'; -import '/imports/api/creature/actions/castSpellWithSlot.js'; -import '/imports/api/creature/creatureProperties/manageEquipment.js'; - -let CreatureProperties = new Mongo.Collection('creatureProperties'); - -let CreaturePropertySchema = new SimpleSchema({ - type: { - type: String, - allowedValues: Object.keys(propertySchemasIndex), - }, - tags: { - type: Array, - defaultValue: [], - }, - 'tags.$': { - type: String, - }, - disabled: { - type: Boolean, - optional: true, - }, - icon: { - type: storedIconsSchema, - optional: true, - }, - // Denormalised flag if this property is inactive on the sheet for any reason - // Including being disabled, or a decendent of a disabled property - inactive: { - type: Boolean, - optional: true, - index: 1, - }, - // Denormalised flag if this property was made inactive by an inactive - // ancestor. True if this property has an inactive ancestor even if this - // property is itself inactive - deactivatedByAncestor: { - type: Boolean, - optional: true, - index: 1, - }, - // Denormalised list of all properties or creatures this property depends on - dependencies: { - type: Array, - defaultValue: [], - index: 1, - }, - 'dependencies.$': { - type: String, - regEx: SimpleSchema.RegEx.Id, - }, -}); - -for (let key in propertySchemasIndex){ - let schema = new SimpleSchema({}); - schema.extend(propertySchemasIndex[key]); - schema.extend(CreaturePropertySchema); - schema.extend(ColorSchema); - schema.extend(ChildSchema); - schema.extend(SoftRemovableSchema); - CreatureProperties.attachSchema(schema, { - selector: {type: key} - }); -} - -export function getCreature(property){ - if (!property) throw new Meteor.Error('No property provided'); - let creature = Creatures.findOne(property.ancestors[0].id); - if (!creature) throw new Meteor.Error('Creature does not exist'); - return creature; -} - -function assertPropertyEditPermission(property, userId){ - let creature = getCreature(property); - return assertEditPermission(creature, userId); -} - -function recomputeCreatures(property){ - for (let ref of property.ancestors){ - if (ref.collection === 'creatures') { - reorderDocs({collection: CreatureProperties, ancestorId: ref.id}); - recomputeCreature.call({charId: ref.id}); - } - } -} - -const insertProperty = new ValidatedMethod({ - name: 'creatureProperties.insert', - validate: null, - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({creatureProperty}) { - delete creatureProperty._id; - assertPropertyEditPermission(creatureProperty, this.userId); - let _id = CreatureProperties.insert(creatureProperty); - let property = CreatureProperties.findOne(_id); - recomputeCreatures(property); - }, -}); - -const duplicateProperty = new ValidatedMethod({ - name: 'creatureProperties.duplicate', - validate: new SimpleSchema({ - _id: { - type: String, - regEx: SimpleSchema.RegEx.Id, - } - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id}) { - let creatureProperty = CreatureProperties.findOne(_id); - assertPropertyEditPermission(creatureProperty, this.userId); - delete creatureProperty._id; - CreatureProperties.insert(creatureProperty); - recomputeCreatures(creatureProperty); - }, -}); - -const insertPropertyFromLibraryNode = new ValidatedMethod({ - name: 'creatureProperties.insertPropertyFromLibraryNode', - validate: new SimpleSchema({ - nodeId: { - type: String, - regEx: SimpleSchema.RegEx.Id, - }, - parentRef: { - type: RefSchema, - }, - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({nodeId, parentRef}) { - // get the new ancestry for the properties - let {parentDoc, ancestors} = getAncestry({parentRef}); - - // Check permission to edit - if (parentRef.collection === 'creatures'){ - assertEditPermission(parentDoc, this.userId); - } else if (parentRef.collection === 'creatureProperties'){ - assertPropertyEditPermission(parentDoc, this.userId); - } else { - throw `${parentRef.collection} is not a valid parent collection` - } - - // Fetch the library node and its decendents, provided they have not been - // removed - let node = LibraryNodes.findOne({ - _id: nodeId, - removed: {$ne: true}, - }); - if (!node) throw `Node not found for nodeId: ${nodeId}`; - let oldParent = node.parent; - let nodes = LibraryNodes.find({ - 'ancestors.id': nodeId, - removed: {$ne: true}, - }).fetch(); - // The root node is last in the array of nodes - nodes.push(node); - - // re-map all the ancestors - setLineageOfDocs({ - docArray: nodes, - newAncestry: ancestors, - oldParent, - }); - - // Give the docs new IDs without breaking internal references - renewDocIds({ - docArray: nodes, - collectionMap: {'libraryNodes': 'creatureProperties'} - }); - - // Order the root node - setDocToLastOrder({ - collection: CreatureProperties, - doc: node, - }); - - // Insert the creature properties - let insertedDocIds = CreatureProperties.batchInsert(nodes); - - // get the root inserted doc - let rootId = insertedDocIds[insertedDocIds.length - 1]; - - // Recompute the creatures doc was attached to - recomputeCreatures(node); - - // Return the docId of the last property, the inserted root property - return rootId; - }, -}) - -const updateProperty = new ValidatedMethod({ - name: 'creatureProperties.update', - validate({_id, path}){ - if (!_id) throw new Meteor.Error('No _id', '_id is required'); - // We cannot change these fields with a simple update - switch (path[0]){ - case 'type': - case 'order': - case 'parent': - case 'ancestors': - case 'damage': - throw new Meteor.Error('Permission denied', - 'This property can\'t be updated directly'); - } - }, - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id, path, value}) { - let property = CreatureProperties.findOne(_id); - assertPropertyEditPermission(property, this.userId); - let pathString = path.join('.'); - let modifier; - // unset empty values - if (value === null || value === undefined){ - modifier = {$unset: {[pathString]: 1}}; - } else { - modifier = {$set: {[pathString]: value}}; - } - CreatureProperties.update(_id, modifier, { - selector: {type: property.type}, - }); - recomputeCreatures(property); - }, -}); - -export function damagePropertyWork({property, operation, value}){ - if (operation === 'set'){ - let currentValue = property.value; - // Set represents what we want the value to be after damage - // So we need the actual damage to get to that value - let damage = currentValue - value; - // Damage can't exceed total value - if (damage > currentValue) damage = currentValue; - // Damage must be positive - if (damage < 0) damage = 0; - CreatureProperties.update(property._id, { - $set: {damage} - }, { - selector: property - }); - return currentValue - damage; - } else if (operation === 'increment'){ - let currentValue = property.value - (property.damage || 0); - let currentDamage = property.damage; - let increment = value; - // Can't increase damage above the remaining value - if (increment > currentValue) increment = currentValue; - // Can't decrease damage below zero - if (-increment > currentDamage) increment = -currentDamage; - CreatureProperties.update(property._id, { - $inc: {damage: increment} - }, { - selector: property - }); - return increment; - } -} - -const damagePropertiesByName = new ValidatedMethod({ - name: 'CreatureProperties.damagePropertiesByName', - validate: new SimpleSchema({ - creatureId: SimpleSchema.RegEx.Id, - variableName: { - type: String, - }, - operation: { - type: String, - allowedValues: ['set', 'increment'] - }, - value: Number, - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 20, - timeInterval: 5000, - }, - run({creatureId, variableName, operation, value}) { - // Check permissions - let creature = Creatures.findOne(creatureId, { - fields: { - damageMultipliers: 1, - owner: 1, - readers: 1, - writers: 1, - }, - }); - assertEditPermission(creature, this.userId); - CreatureProperties.find({ - 'ancestors.id': creatureId, - variableName, - removed: {$ne: false}, - inactive: {$ne: true}, - }).forEach(property => { - // Check if property can take damage - let schema = CreatureProperties.simpleSchema(property); - if (!schema.allowsKey('damage')) return; - // Damage the property - damagePropertyWork({property: property, operation, value}) - }); - recomputeCreature.call({charId: creatureId}); - } -}) - -const damageProperty = new ValidatedMethod({ - name: 'creatureProperties.damage', - validate: new SimpleSchema({ - _id: SimpleSchema.RegEx.Id, - operation: { - type: String, - allowedValues: ['set', 'increment'] - }, - value: Number, - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 20, - timeInterval: 5000, - }, - run({_id, operation, value}) { - let currentProperty = CreatureProperties.findOne(_id); - // Check permissions - assertPropertyEditPermission(currentProperty, this.userId); - // Check if property can take damage - let schema = CreatureProperties.simpleSchema(currentProperty); - if (!schema.allowsKey('damage')){ - throw new Meteor.Error( - 'Damage property failed', - `Property of type "${currentProperty.type}" can't be damaged` - ); - } - damagePropertyWork({property: currentProperty, operation, value}) - recomputeCreatures(currentProperty); - }, -}); - -const dealDamage = new ValidatedMethod({ - name: 'creatureProperties.dealDamage', - validate: new SimpleSchema({ - creatureId: SimpleSchema.RegEx.Id, - damageType: { - type: String, - }, - amount: Number, - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 20, - timeInterval: 5000, - }, - run({creatureId, damageType, amount}) { - let creature = Creatures.findOne(creatureId, { - fields: { - damageMultipliers: 1, - owner: 1, - readers: 1, - writers: 1, - }, - }); - // Check permissions - assertEditPermission(creature, this.userId); - let healthBars = CreatureProperties.find({ - 'ancestors.id': creatureId, - type: 'attribute', - attributeType:'healthBar', - removed: {$ne: true}, - inactive: {$ne: true}, - }, { - sort: {order: -1}, - }); - let multiplier = creature.damageMultipliers[damageType]; - if (multiplier === undefined) multiplier = 1; - let totalDamage = Math.floor(amount * multiplier); - let damageLeft = totalDamage; - if (damageType === 'healing') damageLeft = -totalDamage; - healthBars.forEach(healthBar => { - if (damageLeft === 0) return; - let damageAdded = damagePropertyWork({ - property: healthBar, - operation: 'increment', - value: damageLeft, - }); - damageLeft -= damageAdded; - }); - recomputeCreature.call({charId: creatureId}); - return totalDamage; - }, -}); - -export function adjustQuantityWork({property, operation, value}){ - // Check if property has quantity - let schema = CreatureProperties.simpleSchema(property); - if (!schema.allowsKey('quantity')){ - throw new Meteor.Error( - 'Adjust quantity failed', - `Property of type "${property.type}" doesn't have a quantity` - ); - } - if (operation === 'set'){ - CreatureProperties.update(property._id, { - $set: {quantity: value} - }, { - selector: property - }); - } else if (operation === 'increment'){ - // value here is 'damage' - value = -value; - let currentQuantity = property.quantity; - if (currentQuantity + value < 0) value = -currentQuantity; - CreatureProperties.update(property._id, { - $inc: {quantity: value} - }, { - selector: property - }); - } -} - -const adjustQuantity = new ValidatedMethod({ - name: 'creatureProperties.adjustQuantity', - validate: new SimpleSchema({ - _id: SimpleSchema.RegEx.Id, - operation: { - type: String, - allowedValues: ['set', 'increment'] - }, - value: Number, - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id, operation, value}) { - let currentProperty = CreatureProperties.findOne(_id); - // Check permissions - assertPropertyEditPermission(currentProperty, this.userId); - 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); - }, -}); - -const pushToProperty = new ValidatedMethod({ - name: 'creatureProperties.push', - validate: null, - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id, path, value}){ - let property = CreatureProperties.findOne(_id); - assertPropertyEditPermission(property, this.userId); - CreatureProperties.update(_id, { - $push: {[path.join('.')]: value}, - }, { - selector: {type: property.type}, - }); - recomputeCreatures(property); - } -}); - -const pullFromProperty = new ValidatedMethod({ - name: 'creatureProperties.pull', - validate: null, - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id, path, itemId}){ - let property = CreatureProperties.findOne(_id); - assertPropertyEditPermission(property, this.userId); - CreatureProperties.update(_id, { - $pull: {[path.join('.')]: {_id: itemId}}, - }, { - selector: {type: property.type}, - getAutoValues: false, - }); - recomputeCreatures(property); - } -}); - -const softRemoveProperty = new ValidatedMethod({ - name: 'creatureProperties.softRemove', - validate: new SimpleSchema({ - _id: SimpleSchema.RegEx.Id - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id}){ - let property = CreatureProperties.findOne(_id); - assertPropertyEditPermission(property, this.userId); - softRemove({_id, collection: CreatureProperties}); - recomputeCreatures(property); - } -}); - -const restoreProperty = new ValidatedMethod({ - name: 'creatureProperties.restore', - validate: new SimpleSchema({ - _id: SimpleSchema.RegEx.Id - }).validator(), - mixins: [RateLimiterMixin], - rateLimit: { - numRequests: 5, - timeInterval: 5000, - }, - run({_id}){ - let property = CreatureProperties.findOne(_id); - assertPropertyEditPermission(property, this.userId); - restore({_id, collection: CreatureProperties}); - recomputeCreatures(property); - } -}); - -export default CreatureProperties; -export { - CreaturePropertySchema, - insertProperty, - duplicateProperty, - insertPropertyFromLibraryNode, - updateProperty, - dealDamage, - damagePropertiesByName, - damageProperty, - adjustQuantity, - selectAmmoItem, - pushToProperty, - pullFromProperty, - softRemoveProperty, - restoreProperty, -}; diff --git a/app/imports/api/creature/Creatures.js b/app/imports/api/creature/Creatures.js index c8154b84..633e9be9 100644 --- a/app/imports/api/creature/Creatures.js +++ b/app/imports/api/creature/Creatures.js @@ -4,7 +4,7 @@ import SimpleSchema from 'simpl-schema'; import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js' import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; import SharingSchema from '/imports/api/sharing/SharingSchema.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js'; import { assertUserHasPaidBenefits } from '/imports/api/users/patreon/tiers.js'; diff --git a/app/imports/api/creature/actions/applyAdjustment.js b/app/imports/api/creature/actions/applyAdjustment.js index d63020bb..bdd8d21c 100644 --- a/app/imports/api/creature/actions/applyAdjustment.js +++ b/app/imports/api/creature/actions/applyAdjustment.js @@ -1,6 +1,6 @@ import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js'; import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js'; -import { damagePropertiesByName } from '/imports/api/creature/CreatureProperties.js'; +import damagePropertiesByName from '/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js'; export default function applyAdjustment({ prop, diff --git a/app/imports/api/creature/actions/applyBuff.js b/app/imports/api/creature/actions/applyBuff.js index 6b55f617..2bde6c85 100644 --- a/app/imports/api/creature/actions/applyBuff.js +++ b/app/imports/api/creature/actions/applyBuff.js @@ -3,7 +3,7 @@ import { renewDocIds } from '/imports/api/parenting/parenting.js'; import {setDocToLastOrder} from '/imports/api/parenting/order.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; export default function applyBuff({ prop, diff --git a/app/imports/api/creature/actions/applyDamage.js b/app/imports/api/creature/actions/applyDamage.js index 47a0648c..5c3fc3a1 100644 --- a/app/imports/api/creature/actions/applyDamage.js +++ b/app/imports/api/creature/actions/applyDamage.js @@ -1,6 +1,6 @@ import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js'; import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js'; -import { dealDamage } from '/imports/api/creature/CreatureProperties.js'; +import dealDamage from '/imports/api/creature/creatureProperties/methods/dealDamage.js'; export default function applyDamage({ prop, diff --git a/app/imports/api/creature/actions/castSpellWithSlot.js b/app/imports/api/creature/actions/castSpellWithSlot.js index c8b53864..9b97b8e2 100644 --- a/app/imports/api/creature/actions/castSpellWithSlot.js +++ b/app/imports/api/creature/actions/castSpellWithSlot.js @@ -1,7 +1,8 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties, { getCreature, damagePropertyWork } from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties, { getCreature } from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; import { doActionWork } from '/imports/api/creature/actions/doAction.js'; diff --git a/app/imports/api/creature/actions/doAction.js b/app/imports/api/creature/actions/doAction.js index 1353631b..d118d9f6 100644 --- a/app/imports/api/creature/actions/doAction.js +++ b/app/imports/api/creature/actions/doAction.js @@ -1,7 +1,9 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties, { getCreature } from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import Creatures from '/imports/api/creature/Creatures.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; import { nodesToTree } from '/imports/api/parenting/parenting.js'; @@ -25,11 +27,11 @@ const doAction = new ValidatedMethod({ run({actionId, targetId}) { let action = CreatureProperties.findOne(actionId); // Check permissions - let creature = getCreature(action); + let creature = getRootCreatureAncestor(action); assertEditPermission(creature, this.userId); let target = undefined; if (targetId) { - target = getCreature(targetId); + target = Creatures.findOne(targetId); assertEditPermission(target, this.userId); } doActionWork({action, creature, target}); diff --git a/app/imports/api/creature/actions/spendResources.js b/app/imports/api/creature/actions/spendResources.js index b4409866..1de6bc61 100644 --- a/app/imports/api/creature/actions/spendResources.js +++ b/app/imports/api/creature/actions/spendResources.js @@ -1,4 +1,5 @@ -import CreatureProperties, { damagePropertyWork, adjustQuantityWork } from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties, { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js'; +import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; export default function spendResources(action){ // Check Uses diff --git a/app/imports/api/creature/computation/getComputationProperties.js b/app/imports/api/creature/computation/getComputationProperties.js index bbfc5606..96a75b76 100644 --- a/app/imports/api/creature/computation/getComputationProperties.js +++ b/app/imports/api/creature/computation/getComputationProperties.js @@ -1,4 +1,4 @@ -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; export default function getComputationProperties(creatureId){ // find ids of all toggles that have conditions, even if they are inactive diff --git a/app/imports/api/creature/computation/recomputeCreature.js b/app/imports/api/creature/computation/recomputeCreature.js index 9618fe38..af4487b5 100644 --- a/app/imports/api/creature/computation/recomputeCreature.js +++ b/app/imports/api/creature/computation/recomputeCreature.js @@ -94,3 +94,17 @@ export function recomputeCreatureByDoc(creature){ recomputeSlotFullness(creatureId); return computationMemo; } + +// TODO +export function recomputePropertyDependencies(property){ + // Placeholder functionality, just recompute the whole creature + let creature = Creatures.findOne(property.ancestors[0].id); + recomputeCreatureByDoc(creature); +} + +// TODO +export function recomputeCreatureByIdAndDependencies({creatureId, dependencies}){ + // Placeholder functionality, just recompute the whole creature + let creature = Creatures.findOne(creatureId); + recomputeCreatureByDoc(creature); +} diff --git a/app/imports/api/creature/computation/writeAlteredProperties.js b/app/imports/api/creature/computation/writeAlteredProperties.js index f2dcdaf0..b8e15cbc 100644 --- a/app/imports/api/creature/computation/writeAlteredProperties.js +++ b/app/imports/api/creature/computation/writeAlteredProperties.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor' import { isEqual, forOwn } from 'lodash'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import propertySchemasIndex from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; export default function writeAlteredProperties(memo){ diff --git a/app/imports/api/creature/creatureProperties/CreatureProperties.js b/app/imports/api/creature/creatureProperties/CreatureProperties.js new file mode 100644 index 00000000..aa20f7df --- /dev/null +++ b/app/imports/api/creature/creatureProperties/CreatureProperties.js @@ -0,0 +1,77 @@ +import { Mongo } from 'meteor/mongo'; +import SimpleSchema from 'simpl-schema'; +import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; +import ChildSchema from '/imports/api/parenting/ChildSchema.js'; +import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js'; +import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js'; +import { storedIconsSchema } from '/imports/api/icons/Icons.js'; + +let CreatureProperties = new Mongo.Collection('creatureProperties'); + +let CreaturePropertySchema = new SimpleSchema({ + type: { + type: String, + allowedValues: Object.keys(propertySchemasIndex), + }, + tags: { + type: Array, + defaultValue: [], + }, + 'tags.$': { + type: String, + }, + disabled: { + type: Boolean, + optional: true, + }, + icon: { + type: storedIconsSchema, + optional: true, + }, + // Denormalised flag if this property is inactive on the sheet for any reason + // Including being disabled, or a decendent of a disabled property + inactive: { + type: Boolean, + optional: true, + index: 1, + }, + // Denormalised flag if this property was made inactive by an inactive + // ancestor. True if this property has an inactive ancestor even if this + // property is itself inactive + deactivatedByAncestor: { + type: Boolean, + optional: true, + index: 1, + }, + // Denormalised list of all properties or creatures this property depends on + dependencies: { + type: Array, + defaultValue: [], + index: 1, + }, + 'dependencies.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, +}); + +for (let key in propertySchemasIndex){ + let schema = new SimpleSchema({}); + schema.extend(propertySchemasIndex[key]); + schema.extend(CreaturePropertySchema); + schema.extend(ColorSchema); + schema.extend(ChildSchema); + schema.extend(SoftRemovableSchema); + CreatureProperties.attachSchema(schema, { + selector: {type: key} + }); +} + +import '/imports/api/creature/creatureProperties/methods/index.js'; +import '/imports/api/creature/actions/doAction.js'; +import '/imports/api/creature/actions/castSpellWithSlot.js'; + +export default CreatureProperties; +export { + CreaturePropertySchema, +}; diff --git a/app/imports/api/creature/creatureProperties/assertPropertyEditPermission.js b/app/imports/api/creature/creatureProperties/assertPropertyEditPermission.js deleted file mode 100644 index 5f91cf21..00000000 --- a/app/imports/api/creature/creatureProperties/assertPropertyEditPermission.js +++ /dev/null @@ -1,7 +0,0 @@ -import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import getClosestPropertyAncestorCreature from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js'; - -export default function assertPropertyEditPermission(prop, userId){ - let creature = getClosestPropertyAncestorCreature(prop); - assertEditPermission(creature, userId); -} diff --git a/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js b/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js deleted file mode 100644 index 9e8e3145..00000000 --- a/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js +++ /dev/null @@ -1,7 +0,0 @@ -import Creatures from '/imports/api/creature/Creatures.js'; -import getClosestPropertyAncestorCreatureId from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js'; - -export default function getClosestPropertyAncestorCreature(prop){ - let creatureId = getClosestPropertyAncestorCreatureId(prop); - return Creatures.findOne(creatureId); -} diff --git a/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js b/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js deleted file mode 100644 index 1a6b8836..00000000 --- a/app/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function getClosestPropertyAncestorCreatureId(prop){ - if (!prop.ancestors) throw 'Property has no ancestors'; - let creatureId; - // Find the last ancestor in the creature collection - for (let i = prop.ancestors.length - 1; i >= 0; i--){ - if (prop.ancestors[i].collection === 'creatures'){ - creatureId = prop.ancestors[i].id; - break; - } - } - if (!creatureId) throw 'This property has no creature ancestors'; - return creatureId; -} diff --git a/app/imports/api/creature/creatureProperties/getRootCreatureAncestor.js b/app/imports/api/creature/creatureProperties/getRootCreatureAncestor.js new file mode 100644 index 00000000..d1768254 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/getRootCreatureAncestor.js @@ -0,0 +1,5 @@ +import Creatures from '/imports/api/creature/Creatures.js'; + +export default function getRootCreatureAncestor(property){ + return Creatures.findOne(property.ancestors[0].id); +} diff --git a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js new file mode 100644 index 00000000..e2e3f1a4 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js @@ -0,0 +1,66 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +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 { recomputePropertyDependencies } from '/imports/api/creature/computation/recomputeCreature.js'; + +const adjustQuantity = new ValidatedMethod({ + name: 'creatureProperties.adjustQuantity', + validate: new SimpleSchema({ + _id: SimpleSchema.RegEx.Id, + operation: { + type: String, + allowedValues: ['set', 'increment'] + }, + value: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id, operation, value}) { + // Permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Do work + adjustQuantityWork({property, operation, value}); + + // Changing quantity does not change dependencies, recompute deps + recomputePropertyDependencies(property); + }, +}); + +export function adjustQuantityWork({property, operation, value}){ + // Check if property has quantity + let schema = CreatureProperties.simpleSchema(property); + if (!schema.allowsKey('quantity')){ + throw new Meteor.Error( + 'Adjust quantity failed', + `Property of type "${property.type}" doesn't have a quantity` + ); + } + if (operation === 'set'){ + CreatureProperties.update(property._id, { + $set: {quantity: value} + }, { + selector: property + }); + } else if (operation === 'increment'){ + // value here is 'damage' + value = -value; + let currentQuantity = property.quantity; + if (currentQuantity + value < 0) value = -currentQuantity; + CreatureProperties.update(property._id, { + $inc: {quantity: value} + }, { + selector: property + }); + } +} + +export default adjustQuantity; diff --git a/app/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js b/app/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js new file mode 100644 index 00000000..f20a39cb --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js @@ -0,0 +1,57 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import SimpleSchema from 'simpl-schema'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import Creatures from '/imports/api/creature/Creatures.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; +import { recomputePropertyDependencies } from '/imports/api/creature/computation/recomputeCreature.js'; + +const damagePropertiesByName = new ValidatedMethod({ + name: 'CreatureProperties.damagePropertiesByName', + validate: new SimpleSchema({ + creatureId: SimpleSchema.RegEx.Id, + variableName: { + type: String, + }, + operation: { + type: String, + allowedValues: ['set', 'increment'] + }, + value: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 20, + timeInterval: 5000, + }, + run({creatureId, variableName, operation, value}) { + // Check permissions + let creature = Creatures.findOne(creatureId, { + fields: { + damageMultipliers: 1, + owner: 1, + readers: 1, + writers: 1, + }, + }); + assertEditPermission(creature, this.userId); + let lastProperty; + CreatureProperties.find({ + 'ancestors.id': creatureId, + variableName, + removed: {$ne: false}, + inactive: {$ne: true}, + }).forEach(property => { + // Check if property can take damage + let schema = CreatureProperties.simpleSchema(property); + if (!schema.allowsKey('damage')) return; + // Damage the property + damagePropertyWork({property, operation, value}); + lastProperty = property; + }); + recomputePropertyDependencies(lastProperty); + } +}); + +export default damagePropertiesByName; diff --git a/app/imports/api/creature/creatureProperties/methods/damageProperty.js b/app/imports/api/creature/creatureProperties/methods/damageProperty.js new file mode 100644 index 00000000..9c10ab99 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/damageProperty.js @@ -0,0 +1,77 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +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 { recomputePropertyDependencies } from '/imports/api/creature/computation/recomputeCreature.js'; + +const damageProperty = new ValidatedMethod({ + name: 'creatureProperties.damage', + validate: new SimpleSchema({ + _id: SimpleSchema.RegEx.Id, + operation: { + type: String, + allowedValues: ['set', 'increment'] + }, + value: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 20, + timeInterval: 5000, + }, + run({_id, operation, value}) { + // Check permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + // Check if property can take damage + let schema = CreatureProperties.simpleSchema(property); + if (!schema.allowsKey('damage')){ + throw new Meteor.Error( + 'Damage property failed', + `Property of type "${property.type}" can't be damaged` + ); + } + damagePropertyWork({property, operation, value}); + // Dependencies can't be changed through damage, only recompute deps + recomputePropertyDependencies(property); + + }, +}); + +export function damagePropertyWork({property, operation, value}){ + if (operation === 'set'){ + let currentValue = property.value; + // Set represents what we want the value to be after damage + // So we need the actual damage to get to that value + let damage = currentValue - value; + // Damage can't exceed total value + if (damage > currentValue) damage = currentValue; + // Damage must be positive + if (damage < 0) damage = 0; + CreatureProperties.update(property._id, { + $set: {damage} + }, { + selector: property + }); + return currentValue - damage; + } else if (operation === 'increment'){ + let currentValue = property.value - (property.damage || 0); + let currentDamage = property.damage; + let increment = value; + // Can't increase damage above the remaining value + if (increment > currentValue) increment = currentValue; + // Can't decrease damage below zero + if (-increment > currentDamage) increment = -currentDamage; + CreatureProperties.update(property._id, { + $inc: {damage: increment} + }, { + selector: property + }); + return increment; + } +} + +export default damageProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/dealDamage.js b/app/imports/api/creature/creatureProperties/methods/dealDamage.js new file mode 100644 index 00000000..12f049ea --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/dealDamage.js @@ -0,0 +1,68 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import SimpleSchema from 'simpl-schema'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import Creatures from '/imports/api/creature/Creatures.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; +import { recomputeCreatureByIdAndDependencies } from '/imports/api/creature/computation/recomputeCreature.js'; + +const dealDamage = new ValidatedMethod({ + name: 'creatureProperties.dealDamage', + validate: new SimpleSchema({ + creatureId: SimpleSchema.RegEx.Id, + damageType: { + type: String, + }, + amount: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 20, + timeInterval: 5000, + }, + run({creatureId, damageType, amount}) { + // permissions + let creature = Creatures.findOne(creatureId, { + fields: { + damageMultipliers: 1, + owner: 1, + readers: 1, + writers: 1, + }, + }); + assertEditPermission(creature, this.userId); + + // Get all the health bars and do damage to them + let healthBars = CreatureProperties.find({ + 'ancestors.id': creatureId, + type: 'attribute', + attributeType:'healthBar', + removed: {$ne: true}, + inactive: {$ne: true}, + }, { + sort: {order: -1}, + }); + let multiplier = creature.damageMultipliers[damageType]; + if (multiplier === undefined) multiplier = 1; + let totalDamage = Math.floor(amount * multiplier); + let damageLeft = totalDamage; + if (damageType === 'healing') damageLeft = -totalDamage; + let dependencies = []; + healthBars.forEach(healthBar => { + if (damageLeft === 0) return; + let damageAdded = damagePropertyWork({ + property: healthBar, + operation: 'increment', + value: damageLeft, + }); + damageLeft -= damageAdded; + dependencies.push(healthBar.variableName); + dependencies.push(...healthBar.dependencies); + }); + recomputeCreatureByIdAndDependencies({creatureId, dependencies}); + return totalDamage; + }, +}); + +export default dealDamage; diff --git a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js new file mode 100644 index 00000000..650b9681 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js @@ -0,0 +1,33 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +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 { insertPropertyWork } from '/imports/api/creature/creatureProperties/methods/insertProperty.js'; + +const duplicateProperty = new ValidatedMethod({ + name: 'creatureProperties.duplicate', + validate: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + } + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id}) { + let creatureProperty = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(creatureProperty); + assertEditPermission(rootCreature, this.userId); + insertPropertyWork({ + property: creatureProperty, + creature: rootCreature, + }); + }, +}); + +export default duplicateProperty; diff --git a/app/imports/api/creature/creatureProperties/manageEquipment.js b/app/imports/api/creature/creatureProperties/methods/equipItem.js similarity index 83% rename from app/imports/api/creature/creatureProperties/manageEquipment.js rename to app/imports/api/creature/creatureProperties/methods/equipItem.js index a5f88759..b14ca3d8 100644 --- a/app/imports/api/creature/creatureProperties/manageEquipment.js +++ b/app/imports/api/creature/creatureProperties/methods/equipItem.js @@ -1,12 +1,12 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; -import getClosestPropertyAncestorCreature from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js'; -function getParentRefByTag(creatureId, tag){ +export function getParentRefByTag(creatureId, tag){ let prop = CreatureProperties.findOne({ 'ancestors.id': creatureId, removed: {$ne: true}, @@ -40,7 +40,7 @@ const equipItem = new ValidatedMethod({ let item = CreatureProperties.findOne(_id); if (item.type !== 'item') throw new Meteor.Error('wrong type', 'Equip and unequip can only be performed on items'); - let creature = getClosestPropertyAncestorCreature(item); + let creature = getRootCreatureAncestor(item); assertEditPermission(creature, this.userId); CreatureProperties.update(_id, { $set: {equipped}, @@ -61,4 +61,4 @@ const equipItem = new ValidatedMethod({ }, }); -export { equipItem, getParentRefByTag } +export default equipItem; diff --git a/app/imports/api/creature/creatureProperties/methods/index.js b/app/imports/api/creature/creatureProperties/methods/index.js new file mode 100644 index 00000000..63e26999 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/index.js @@ -0,0 +1,14 @@ +import '//imports/api/creature/creatureProperties/methods/adjustQuantity.js'; +import '/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js'; +import '/imports/api/creature/creatureProperties/methods/damageProperty.js'; +import '/imports/api/creature/creatureProperties/methods/dealDamage.js'; +import '/imports/api/creature/creatureProperties/methods/duplicateProperty.js'; +import '/imports/api/creature/creatureProperties/methods/equipItem.js'; +import '/imports/api/creature/creatureProperties/methods/insertProperty.js'; +import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js'; +import '/imports/api/creature/creatureProperties/methods/pullFromProperty.js'; +import '/imports/api/creature/creatureProperties/methods/pushToProperty.js'; +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'; diff --git a/app/imports/api/creature/creatureProperties/methods/insertProperty.js b/app/imports/api/creature/creatureProperties/methods/insertProperty.js new file mode 100644 index 00000000..188e86c7 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/insertProperty.js @@ -0,0 +1,40 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +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 { reorderDocs } from '/imports/api/parenting/order.js'; +import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const insertProperty = new ValidatedMethod({ + name: 'creatureProperties.insert', + validate: null, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({creatureProperty}) { + let rootCreature = getRootCreatureAncestor(creatureProperty); + assertEditPermission(rootCreature, this.userId); + insertPropertyWork({ + property: creatureProperty, + creature: rootCreature, + }); + }, +}); + +export function insertPropertyWork({property, creature}){ + delete property._id; + let _id = CreatureProperties.insert(property); + // Tree structure changed by insert, reorder the tree + reorderDocs({ + collection: CreatureProperties, + ancestorId: creature._id, + }); + // Inserting a creature property invalidates dependencies: full recompute + recomputeCreatureByDoc(creature); + return _id; +} + +export default insertProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js new file mode 100644 index 00000000..4589eadd --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -0,0 +1,103 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import LibraryNodes from '/imports/api/library/LibraryNodes.js'; +import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; +import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { + setLineageOfDocs, + getAncestry, + renewDocIds +} from '/imports/api/parenting/parenting.js'; +import { reorderDocs } from '/imports/api/parenting/order.js'; +import { setDocToLastOrder } from '/imports/api/parenting/order.js'; + +const insertPropertyFromLibraryNode = new ValidatedMethod({ + name: 'creatureProperties.insertPropertyFromLibraryNode', + validate: new SimpleSchema({ + nodeId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + parentRef: { + type: RefSchema, + }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({nodeId, parentRef}) { + // get the new ancestry for the properties + let {parentDoc, ancestors} = getAncestry({parentRef}); + + // Check permission to edit + let rootCreature; + if (parentRef.collection === 'creatures'){ + rootCreature = parentDoc; + } else if (parentRef.collection === 'creatureProperties'){ + rootCreature = getRootCreatureAncestor(parentDoc); + } else { + throw `${parentRef.collection} is not a valid parent collection` + } + assertEditPermission(rootCreature, this.userId); + + // Fetch the library node and its decendents, provided they have not been + // removed + let node = LibraryNodes.findOne({ + _id: nodeId, + removed: {$ne: true}, + }); + if (!node) throw `Node not found for nodeId: ${nodeId}`; + let oldParent = node.parent; + let nodes = LibraryNodes.find({ + 'ancestors.id': nodeId, + removed: {$ne: true}, + }).fetch(); + // The root node is last in the array of nodes + nodes.push(node); + + // re-map all the ancestors + setLineageOfDocs({ + docArray: nodes, + newAncestry: ancestors, + oldParent, + }); + + // Give the docs new IDs without breaking internal references + renewDocIds({ + docArray: nodes, + collectionMap: {'libraryNodes': 'creatureProperties'} + }); + + // Order the root node + setDocToLastOrder({ + collection: CreatureProperties, + doc: node, + }); + + // Insert the creature properties + let insertedDocIds = CreatureProperties.batchInsert(nodes); + + // get the root inserted doc + let rootId = insertedDocIds[insertedDocIds.length - 1]; + + // Tree structure changed by inserts, reorder the tree + reorderDocs({ + collection: CreatureProperties, + ancestorId: rootCreature._id, + }); + + // Inserting a creature property invalidates dependencies: full recompute + recomputeCreatureByDoc(rootCreature); + + // Return the docId of the last property, the inserted root property + return rootId; + }, +}); + +export default insertPropertyFromLibraryNode; diff --git a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js new file mode 100644 index 00000000..dfcec70a --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js @@ -0,0 +1,36 @@ +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 { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const pullFromProperty = new ValidatedMethod({ + name: 'creatureProperties.pull', + validate: null, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id, path, itemId}){ + // Permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Do work + CreatureProperties.update(_id, { + $pull: {[path.join('.')]: {_id: itemId}}, + }, { + selector: {type: property.type}, + getAutoValues: false, + }); + + // TODO figure out if this method can change deps or not + recomputeCreatureByDoc(rootCreature); + // recomputePropertyDependencies(property); + } +}); + +export default pullFromProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js new file mode 100644 index 00000000..d8a0dac6 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js @@ -0,0 +1,35 @@ +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 { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const pushToProperty = new ValidatedMethod({ + name: 'creatureProperties.push', + validate: null, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id, path, value}){ + // Permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Do work + CreatureProperties.update(_id, { + $push: {[path.join('.')]: value}, + }, { + selector: {type: property.type}, + }); + + // TODO figure out if this method can change deps or not + recomputeCreatureByDoc(rootCreature); + // recomputePropertyDependencies(property); + } +}); + +export default pushToProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js new file mode 100644 index 00000000..62c29eea --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js @@ -0,0 +1,34 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import SimpleSchema from 'simpl-schema'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { restore } from '/imports/api/parenting/softRemove.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; +import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const restoreProperty = new ValidatedMethod({ + name: 'creatureProperties.restore', + validate: new SimpleSchema({ + _id: SimpleSchema.RegEx.Id + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id}){ + // Permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Do work + restore({_id, collection: CreatureProperties}); + + // Changes dependency tree by removing children + recomputeCreatureByDoc(rootCreature); + } +}); + +export default restoreProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js new file mode 100644 index 00000000..73aff869 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js @@ -0,0 +1,52 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +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 { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +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}) { + // Permissions + let action = CreatureProperties.findOne(actionId); + let rootCreature = getRootCreatureAncestor(action); + assertEditPermission(rootCreature, 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, + }); + + // Changing the linked item does change the dependency tree + // TODO: We can predict exactly which deps will be affected instead of + // recomputing the entire creature + recomputeCreatureByDoc(rootCreature); + }, +}); + +export default selectAmmoItem; diff --git a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js new file mode 100644 index 00000000..f44f1166 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js @@ -0,0 +1,34 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import SimpleSchema from 'simpl-schema'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { softRemove } from '/imports/api/parenting/softRemove.js'; +import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; +import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const softRemoveProperty = new ValidatedMethod({ + name: 'creatureProperties.softRemove', + validate: new SimpleSchema({ + _id: SimpleSchema.RegEx.Id + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id}){ + // Permissions + let property = CreatureProperties.findOne(_id); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + // Do work + softRemove({_id, collection: CreatureProperties}); + + // Changes dependency tree by removing children + recomputeCreatureByDoc(rootCreature); + } +}); + +export default softRemoveProperty; diff --git a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js new file mode 100644 index 00000000..0cfab37b --- /dev/null +++ b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js @@ -0,0 +1,54 @@ +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 { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js'; + +const updateCreatureProperty = new ValidatedMethod({ + name: 'creatureProperties.update', + validate({_id, path}){ + if (!_id) throw new Meteor.Error('No _id', '_id is required'); + // We cannot change these fields with a simple update + switch (path[0]){ + case 'type': + case 'order': + case 'parent': + case 'ancestors': + case 'damage': + throw new Meteor.Error('Permission denied', + 'This property can\'t be updated directly'); + } + }, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({_id, path, value}) { + // Permission + let property = CreatureProperties.findOne(_id, { + fields: {type: 1, ancestors: 1} + }); + let rootCreature = getRootCreatureAncestor(property); + assertEditPermission(rootCreature, this.userId); + + let pathString = path.join('.'); + let modifier; + // unset empty values + if (value === null || value === undefined){ + modifier = {$unset: {[pathString]: 1}}; + } else { + modifier = {$set: {[pathString]: value}}; + } + CreatureProperties.update(_id, modifier, { + selector: {type: property.type}, + }); + + // Updating a property might change dependencies, unless we are certain + // it did not, a full recompute is required + recomputeCreatureByDoc(rootCreature); + }, +}); + +export default updateCreatureProperty; diff --git a/app/imports/api/creature/creatureProperties/recomputeCreaturesByProperty.js b/app/imports/api/creature/creatureProperties/recomputeCreaturesByProperty.js new file mode 100644 index 00000000..6dd5b2d3 --- /dev/null +++ b/app/imports/api/creature/creatureProperties/recomputeCreaturesByProperty.js @@ -0,0 +1,12 @@ +import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js'; + +/** + * Recomputes all ancestor creatures of this property + */ +export default function recomputeCreaturesByProperty(property){ + for (let ref of property.ancestors){ + if (ref.collection === 'creatures') { + recomputeCreatureById.call(ref.id); + } + } +} diff --git a/app/imports/api/creature/denormalise/recomputeDamageMultipliers.js b/app/imports/api/creature/denormalise/recomputeDamageMultipliers.js index ca722736..ccaed171 100644 --- a/app/imports/api/creature/denormalise/recomputeDamageMultipliers.js +++ b/app/imports/api/creature/denormalise/recomputeDamageMultipliers.js @@ -3,7 +3,7 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import Creatures from '/imports/api/creature/Creatures.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; export const recomputeDamageMultipliers = new ValidatedMethod({ diff --git a/app/imports/api/creature/denormalise/recomputeInactiveProperties.js b/app/imports/api/creature/denormalise/recomputeInactiveProperties.js index dbdb976c..e53f05ee 100644 --- a/app/imports/api/creature/denormalise/recomputeInactiveProperties.js +++ b/app/imports/api/creature/denormalise/recomputeInactiveProperties.js @@ -1,4 +1,4 @@ -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; export default function recomputeInactiveProperties(ancestorId){ let disabledFilter = { diff --git a/app/imports/api/creature/denormalise/recomputeInventory.js b/app/imports/api/creature/denormalise/recomputeInventory.js index 6cac3d69..ceb4824e 100644 --- a/app/imports/api/creature/denormalise/recomputeInventory.js +++ b/app/imports/api/creature/denormalise/recomputeInventory.js @@ -1,4 +1,4 @@ -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import nodesToTree from '/imports/api/parenting/parenting.js'; export default function recomputeInventory(creatureId){ diff --git a/app/imports/api/creature/denormalise/recomputeSlotFullness.js b/app/imports/api/creature/denormalise/recomputeSlotFullness.js index 06a058a3..2b4e60ba 100644 --- a/app/imports/api/creature/denormalise/recomputeSlotFullness.js +++ b/app/imports/api/creature/denormalise/recomputeSlotFullness.js @@ -1,4 +1,4 @@ -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; // n + 1 database queries + n potential updates for n slots. Could be sped up. export default function recomputeSlotFullness(ancestorId){ CreatureProperties.find({ diff --git a/app/imports/api/creature/removeCreature.js b/app/imports/api/creature/removeCreature.js index 41d3dd43..899db6b3 100644 --- a/app/imports/api/creature/removeCreature.js +++ b/app/imports/api/creature/removeCreature.js @@ -3,7 +3,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertOwnership } from '/imports/api/creature/creaturePermissions.js'; import Creatures from '/imports/api/creature/Creatures.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; diff --git a/app/imports/api/creature/restCreature.js b/app/imports/api/creature/restCreature.js index f47e182c..753298ae 100644 --- a/app/imports/api/creature/restCreature.js +++ b/app/imports/api/creature/restCreature.js @@ -2,7 +2,7 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import Creatures from '/imports/api/creature/Creatures.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js'; diff --git a/app/imports/server/cron/deleteSoftRemovedDocuments.js b/app/imports/server/cron/deleteSoftRemovedDocuments.js index 26baae62..779fbed4 100644 --- a/app/imports/server/cron/deleteSoftRemovedDocuments.js +++ b/app/imports/server/cron/deleteSoftRemovedDocuments.js @@ -1,4 +1,4 @@ -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js'; import { SyncedCron } from 'meteor/percolate:synced-cron'; diff --git a/app/imports/server/publications/singleCharacter.js b/app/imports/server/publications/singleCharacter.js index 8720767b..59becda1 100644 --- a/app/imports/server/publications/singleCharacter.js +++ b/app/imports/server/publications/singleCharacter.js @@ -1,6 +1,6 @@ import SimpleSchema from 'simpl-schema'; import Creatures from '/imports/api/creature/Creatures.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import { assertViewPermission } from '/imports/api/creature/creaturePermissions.js'; import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js'; diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index 9f3293c2..bb91d2f3 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -1,6 +1,6 @@ import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; Meteor.publish('slotFillers', function(slotId){ this.autorun(function (){ diff --git a/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue b/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue index 97be825b..0bb474aa 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue @@ -111,7 +111,7 @@ diff --git a/app/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue index d788ddc7..4cff71b5 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue @@ -24,7 +24,7 @@