From 80d369f0d4705bc5dddd924bce97d79e0b9a7f4e Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 30 Jan 2019 13:34:45 +0200 Subject: [PATCH] Got healthbars persisting data to the database --- app/imports/api/creature/Creatures.js | 23 +--- .../api/creature/creatureComputation.js | 8 +- .../api/creature/properties/Attributes.js | 107 +++++++++++++++++- .../api/creature/subSchemas/ColorSchema.js | 13 +-- app/imports/api/pickKeysAsOptional.js | 9 ++ app/imports/ui/character/StatsTab.vue | 24 +--- app/imports/ui/components/HealthBarCard.vue | 26 +++++ .../ui/components/HealthBarCardContainer.vue | 40 +++++++ 8 files changed, 196 insertions(+), 54 deletions(-) create mode 100644 app/imports/api/pickKeysAsOptional.js create mode 100644 app/imports/ui/components/HealthBarCard.vue create mode 100644 app/imports/ui/components/HealthBarCardContainer.vue diff --git a/app/imports/api/creature/Creatures.js b/app/imports/api/creature/Creatures.js index 4f041104..03f98e22 100644 --- a/app/imports/api/creature/Creatures.js +++ b/app/imports/api/creature/Creatures.js @@ -12,7 +12,11 @@ Creatures = new Mongo.Collection("creatures"); let creatureSchema = new SimpleSchema({ //strings name: {type: String, defaultValue: "", trim: false, optional: true}, - urlName: {type: String, defaultValue: "-", trim: false, optional: true}, + urlName: {type: String, trim: false, optional: true, + autoValue: function() { + return getSlug(this.field("name").value, {maintainCase: true}) || "-"; + }, + }, alignment: {type: String, defaultValue: "", trim: false, optional: true}, gender: {type: String, defaultValue: "", trim: false, optional: true}, race: {type: String, defaultValue: "", trim: false, optional: true}, @@ -62,21 +66,4 @@ let creatureSchema = new SimpleSchema({ Creatures.attachSchema(creatureSchema); Creatures.attachSchema(ColorSchema); -//Keep the urlName up to date -if (Meteor.isServer){ - Creatures.after.update(function(userId, doc, fieldNames, modifier, options) { - if (_.contains(fieldNames, "name")){ - var urlName = getSlug(doc.name, {maintainCase: true}) || "-"; - Creatures.update(doc._id, {$set: {urlName}}); - } - }); - Creatures.before.insert(function(userId, doc) { - doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-"; - // The first creature a user creates should have the new user experience - if (!Creatures.find({owner: userId}).count()){ - doc.settings.newUserExperience = true; - } - }); -} - export default Creatures; diff --git a/app/imports/api/creature/creatureComputation.js b/app/imports/api/creature/creatureComputation.js index 4d0f3978..b93b4eed 100644 --- a/app/imports/api/creature/creatureComputation.js +++ b/app/imports/api/creature/creatureComputation.js @@ -26,8 +26,9 @@ export const recomputeCreature = new ValidatedMethod({ 'You do not have permission to recompute this creature'); } - // Work - computeCreatureById(charId); + // Work, call this direcly if you are already in a method that has checked + // for permission to edit a given character + recomputeCreatureById(charId); }, @@ -73,7 +74,7 @@ export const recomputeCreature = new ValidatedMethod({ * @returns {Object} An in-memory description of the character as * computed and written to the database */ -function computeCreatureById(charId){ +export function recomputeCreatureById(charId){ let char = buildCreature(charId); char = computeCreature(char); writeCreature(char); @@ -82,6 +83,7 @@ function computeCreatureById(charId){ /** * Write the in-memory creature to the database docs + * This could be optimized to only write changed fields to the database * * @param {Object} char in-memory char object * @returns {undefined} diff --git a/app/imports/api/creature/properties/Attributes.js b/app/imports/api/creature/properties/Attributes.js index f803088a..313b7140 100644 --- a/app/imports/api/creature/properties/Attributes.js +++ b/app/imports/api/creature/properties/Attributes.js @@ -1,6 +1,9 @@ import {makeChild} from "/imports/api/parenting.js"; import SimpleSchema from 'simpl-schema'; import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js"; +import { canEditCreature } from '/imports/api/creature/creaturePermission.js'; +import { recomputeCreatureById } from '/imports/api/creature/creatureComputation.js' +import pickKeysAsOptional from '/imports/api/pickKeysAsOptional.js'; let Attributes = new Mongo.Collection("attributes"); @@ -62,10 +65,6 @@ attributeSchema = new SimpleSchema({ type: Boolean, optional: true, }, - enabled: { - type: Boolean, - defaultValue: true, - }, reset: { type: String, optional: true, @@ -76,12 +75,110 @@ attributeSchema = new SimpleSchema({ type: Number, optional: true, }, + color: ColorSchema(), }); Attributes.attachSchema(attributeSchema); -Attributes.attachSchema(ColorSchema); //Attributes.attachBehaviour("softRemovable"); makeChild(Attributes, ["enabled"]); //children of lots of things +let updateAttributeSchema = pickKeysAsOptional(attributeSchema, [ + 'name', + 'variableName', + 'type', + 'baseValue', + 'decimal', + 'reset', + 'resetMultiplier', + 'color', +]); + +const updateAttribute = new ValidatedMethod({ + + name: "Attributes.methods.update", + + validate: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + update: updateAttributeSchema, + }).validator(), + + run({_id, update}) { + let currentAttribute = Attributes.findOne(_id); + if (!currentAttribute){ + throw new Meteor.Error("Attributes.methods.update.denied", + `No attributes exist with the id: ${_id}`); + } + let charId = currentAttribute.charId; + if (canEditCreature(charId, this.userId)){ + Attributes.update(_id, {$set: update}); + recomputeCreatureById(charId); + } + }, + +}); + +const adjustAttribute = new ValidatedMethod({ + + name: "Attributes.methods.adjust", + + validate: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + increment: { + type: Number, + optional: true + }, + set: { + type: Number, + optional: true, + custom() { + if (!this.isSet && !this.field('increment').isSet) { + // either set or increment must exist + return SimpleSchema.ErrorTypes.REQUIRED; + } else if (this.isSet && this.field('increment').isSet){ + return 'Can\'t increment and set an attritbute adjustment in one operation'; + } + }, + }, + }).validator(), + + run({_id, increment, set}) { + let currentAttribute = Attributes.findOne(_id); + if (!currentAttribute){ + throw new Meteor.Error("Attributes.methods.update.denied", + `No attributes exist with the id: ${_id}`); + } + let charId = currentAttribute.charId; + if (canEditCreature(charId, this.userId)){ + if (typeof set === 'number'){ + let val = currentAttribute.value; + // Set represents what we want the value to be after adjustment + // So we need the actual adjustment to get to that value + let adjustment = set - val; + // Ajustment can't exceed total value + if (-adjustment > val) adjustment = -val; + // Adjustment must be negative + if (adjustment > 0) adjustment = 0; + Attributes.update(_id, {$set: {adjustment}}); + } else if (typeof increment === 'number'){ + let remaining = currentAttribute.value + currentAttribute.adjustment; + let adj = currentAttribute.adjustment; + // Can't decrease adjustment below remaining value + if (-increment > remaining) increment = -remaining; + // Can't increase adjustment above zero + if (increment > -adj) increment = -adj; + Attributes.update(_id, {$inc: {adjustment: increment}}); + } + } + }, + +}); + export default Attributes; +export { updateAttribute, adjustAttribute }; diff --git a/app/imports/api/creature/subSchemas/ColorSchema.js b/app/imports/api/creature/subSchemas/ColorSchema.js index 77515bd9..d0ae188a 100644 --- a/app/imports/api/creature/subSchemas/ColorSchema.js +++ b/app/imports/api/creature/subSchemas/ColorSchema.js @@ -1,12 +1,11 @@ import SimpleSchema from 'simpl-schema'; -const ColorSchema = new SimpleSchema({ - color: { - type: String, - defaultValue: "#9E9E9E", - // match hex colors of the form #A23 or #A23f56 - regEx: /^#([a-f0-9]{3}){1,2}\b$/i, - }, +const ColorSchema = ({optional = false} = {}) => ({ + type: String, + defaultValue: "#9E9E9E", + // match hex colors of the form #A23 or #A23f56 + regEx: /^#([a-f0-9]{3}){1,2}\b$/i, + optional }); export default ColorSchema; diff --git a/app/imports/api/pickKeysAsOptional.js b/app/imports/api/pickKeysAsOptional.js new file mode 100644 index 00000000..3de0ba30 --- /dev/null +++ b/app/imports/api/pickKeysAsOptional.js @@ -0,0 +1,9 @@ +export default function pickKeysAsOptional(schema, keys){ + let newSchema = schema.pick(...keys); + let optionalSchema = {}; + for (let i of keys){ + optionalSchema[i] = {optional: true} + }; + newSchema.extend(optionalSchema); + return newSchema; +}; diff --git a/app/imports/ui/character/StatsTab.vue b/app/imports/ui/character/StatsTab.vue index 2c7bc62c..09df9e68 100644 --- a/app/imports/ui/character/StatsTab.vue +++ b/app/imports/ui/character/StatsTab.vue @@ -2,16 +2,7 @@
- - - - - +
@@ -97,7 +88,7 @@ import AttributeCard from '/imports/ui/components/AttributeCard.vue'; import AbilityListTile from '/imports/ui/components/AbilityListTile.vue'; import ColumnLayout from "/imports/ui/components/ColumnLayout.vue"; - import HealthBar from '/imports/ui/components/HealthBar.vue'; + import HealthBarCardContainer from '/imports/ui/components/HealthBarCardContainer.vue'; import HitDiceListTile from '/imports/ui/components/HitDiceListTile.vue'; import SkillListTile from '/imports/ui/components/SkillListTile.vue'; @@ -113,20 +104,11 @@ AbilityListTile, AttributeCard, ColumnLayout, - HealthBar, + HealthBarCardContainer, HitDiceListTile, SkillListTile, }, meteor: { - healthBars(){ - return Attributes.find({ - charId: this.charId, - type: 'healthBar', - value: {$ne: 0}, - }, { - sort: {order: 1}, - }); - }, abilities(){ return getAttributeOfType(this.charId, 'ability'); }, diff --git a/app/imports/ui/components/HealthBarCard.vue b/app/imports/ui/components/HealthBarCard.vue new file mode 100644 index 00000000..26b297c9 --- /dev/null +++ b/app/imports/ui/components/HealthBarCard.vue @@ -0,0 +1,26 @@ + + + diff --git a/app/imports/ui/components/HealthBarCardContainer.vue b/app/imports/ui/components/HealthBarCardContainer.vue new file mode 100644 index 00000000..a6425ba1 --- /dev/null +++ b/app/imports/ui/components/HealthBarCardContainer.vue @@ -0,0 +1,40 @@ + + +