Got healthbars persisting data to the database

This commit is contained in:
Stefan Zermatten
2019-01-30 13:34:45 +02:00
parent f6b0c746cc
commit 80d369f0d4
8 changed files with 196 additions and 54 deletions

View File

@@ -12,7 +12,11 @@ Creatures = new Mongo.Collection("creatures");
let creatureSchema = new SimpleSchema({ let creatureSchema = new SimpleSchema({
//strings //strings
name: {type: String, defaultValue: "", trim: false, optional: true}, 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}, alignment: {type: String, defaultValue: "", trim: false, optional: true},
gender: {type: String, defaultValue: "", trim: false, optional: true}, gender: {type: String, defaultValue: "", trim: false, optional: true},
race: {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(creatureSchema);
Creatures.attachSchema(ColorSchema); 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; export default Creatures;

View File

@@ -26,8 +26,9 @@ export const recomputeCreature = new ValidatedMethod({
'You do not have permission to recompute this creature'); 'You do not have permission to recompute this creature');
} }
// Work // Work, call this direcly if you are already in a method that has checked
computeCreatureById(charId); // 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 * @returns {Object} An in-memory description of the character as
* computed and written to the database * computed and written to the database
*/ */
function computeCreatureById(charId){ export function recomputeCreatureById(charId){
let char = buildCreature(charId); let char = buildCreature(charId);
char = computeCreature(char); char = computeCreature(char);
writeCreature(char); writeCreature(char);
@@ -82,6 +83,7 @@ function computeCreatureById(charId){
/** /**
* Write the in-memory creature to the database docs * 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 * @param {Object} char in-memory char object
* @returns {undefined} * @returns {undefined}

View File

@@ -1,6 +1,9 @@
import {makeChild} from "/imports/api/parenting.js"; import {makeChild} from "/imports/api/parenting.js";
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js"; 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"); let Attributes = new Mongo.Collection("attributes");
@@ -62,10 +65,6 @@ attributeSchema = new SimpleSchema({
type: Boolean, type: Boolean,
optional: true, optional: true,
}, },
enabled: {
type: Boolean,
defaultValue: true,
},
reset: { reset: {
type: String, type: String,
optional: true, optional: true,
@@ -76,12 +75,110 @@ attributeSchema = new SimpleSchema({
type: Number, type: Number,
optional: true, optional: true,
}, },
color: ColorSchema(),
}); });
Attributes.attachSchema(attributeSchema); Attributes.attachSchema(attributeSchema);
Attributes.attachSchema(ColorSchema);
//Attributes.attachBehaviour("softRemovable"); //Attributes.attachBehaviour("softRemovable");
makeChild(Attributes, ["enabled"]); //children of lots of things 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 default Attributes;
export { updateAttribute, adjustAttribute };

View File

@@ -1,12 +1,11 @@
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
const ColorSchema = new SimpleSchema({ const ColorSchema = ({optional = false} = {}) => ({
color: { type: String,
type: String, defaultValue: "#9E9E9E",
defaultValue: "#9E9E9E", // match hex colors of the form #A23 or #A23f56
// match hex colors of the form #A23 or #A23f56 regEx: /^#([a-f0-9]{3}){1,2}\b$/i,
regEx: /^#([a-f0-9]{3}){1,2}\b$/i, optional
},
}); });
export default ColorSchema; export default ColorSchema;

View File

@@ -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;
};

View File

@@ -2,16 +2,7 @@
<div class="stats-tab ma-2"> <div class="stats-tab ma-2">
<div class="px-2 pt-2"> <div class="px-2 pt-2">
<v-card> <health-bar-card-container :charId="charId"/>
<v-card-text>
<health-bar
v-for="healthBar in healthBars"
:key="healthBar._id"
:value="healthBar.value + (healthBar.adjustment || 0)"
:maxValue="healthBar.value"
/>
</v-card-text>
</v-card>
</div> </div>
<column-layout> <column-layout>
@@ -97,7 +88,7 @@
import AttributeCard from '/imports/ui/components/AttributeCard.vue'; import AttributeCard from '/imports/ui/components/AttributeCard.vue';
import AbilityListTile from '/imports/ui/components/AbilityListTile.vue'; import AbilityListTile from '/imports/ui/components/AbilityListTile.vue';
import ColumnLayout from "/imports/ui/components/ColumnLayout.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 HitDiceListTile from '/imports/ui/components/HitDiceListTile.vue';
import SkillListTile from '/imports/ui/components/SkillListTile.vue'; import SkillListTile from '/imports/ui/components/SkillListTile.vue';
@@ -113,20 +104,11 @@
AbilityListTile, AbilityListTile,
AttributeCard, AttributeCard,
ColumnLayout, ColumnLayout,
HealthBar, HealthBarCardContainer,
HitDiceListTile, HitDiceListTile,
SkillListTile, SkillListTile,
}, },
meteor: { meteor: {
healthBars(){
return Attributes.find({
charId: this.charId,
type: 'healthBar',
value: {$ne: 0},
}, {
sort: {order: 1},
});
},
abilities(){ abilities(){
return getAttributeOfType(this.charId, 'ability'); return getAttributeOfType(this.charId, 'ability');
}, },

View File

@@ -0,0 +1,26 @@
<template lang="html">
<v-card>
<v-card-text>
<health-bar
v-for="attribute in attributes"
:key="attribute._id"
:value="attribute.value + (attribute.adjustment || 0)"
:maxValue="attribute.value"
@change="e => $emit('change', {_id: attribute._id, change: e})"
/>
</v-card-text>
</v-card>
</template>
<script>
import HealthBar from '/imports/ui/components/HealthBar.vue';
export default {
props: {
attributes: Array,
},
components: {
HealthBar,
},
}
</script>

View File

@@ -0,0 +1,40 @@
<template lang="html">
<health-bar-card
:attributes="attributes"
@change="healthBarChanged"
/>
</template>
<script>
import Attributes from '/imports/api/creature/properties/Attributes.js';
import { adjustAttribute } from '/imports/api/creature/properties/Attributes.js';
import HealthBarCard from '/imports/ui/components/HealthBarCard.vue';
export default {
components: {
HealthBarCard,
},
props: {
charId: String,
},
meteor: {
attributes(){
return Attributes.find({
charId: this.charId,
type: 'healthBar',
value: {$ne: 0},
}, {
sort: {order: 1},
});
},
},
methods: {
healthBarChanged({_id, change}){
adjustAttribute.call({
_id,
[change.type]: change.value
});
},
},
};
</script>