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({
//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;

View File

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

View File

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

View File

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

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="px-2 pt-2">
<v-card>
<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>
<health-bar-card-container :charId="charId"/>
</div>
<column-layout>
@@ -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');
},

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>