Completed the stats tab, conditions not added yet

This commit is contained in:
Stefan Zermatten
2019-01-21 16:03:05 +02:00
parent e43718f034
commit 60dfba3b46
13 changed files with 310 additions and 49 deletions

View File

@@ -38,6 +38,9 @@ export default () => ({
{"name": "Level 8 Spell Slots", "variableName": "level8SpellSlots", "type": "spellSlot"},
{"name": "Level 9 Spell Slots", "variableName": "level9SpellSlots", "type": "spellSlot"},
{"name": "Proficiency Bonus", "variableName": "proficiencyBonus", "type": "modifier"},
{"name": "initiative", "variableName": "initiative", "type": "modifier"},
{"name": "Carry Capacity Multiplier", "variableName": "carryMultiplier", "type": "utility", "baseValue": 1},
{"name": "Rage Damage", "variableName": "rageDamage", "type": "utility"},
],
@@ -68,9 +71,6 @@ export default () => ({
{"name": "Intelligence Save", "variableName": "intelligenceSave", "ability": "intelligence", "type":"save"},
{"name": "Wisdom Save", "variableName": "wisdomSave", "ability": "wisdom", "type":"save"},
{"name": "Charisma Save", "variableName": "charismaSave", "ability": "charisma", "type":"save"},
{"name": "Proficiency Bonus", "variableName": "proficiencyBonus", "type": "stat"},
{"name": "initiative", "variableName": "initiative", "type": "stat"},
],
"damageMultipliers": [

View File

@@ -2,10 +2,16 @@
// on them disadvantaged as well
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import SimpleSchema from 'simpl-schema';
import Creatures from "/imports/api/creature/Creatures.js";
import Attributes from "/imports/api/creature/Attributes.js";
import Skills from "/imports/api/creature/Skills.js";
import Effects from "/imports/api/creature/Effects.js";
import Attributes from "/imports/api/creature/properties/Attributes.js";
import Skills from "/imports/api/creature/properties/Skills.js";
import Effects from "/imports/api/creature/properties/Effects.js";
import DamageMultipliers from "/imports/api/creature/properties/DamageMultipliers.js";
import Classes from "/imports/api/creature/properties/Classes.js";
// TODO, just checks that a charId is given
const canEditCreature = charId => !!charId;
export const recomputeCreature = new ValidatedMethod({
@@ -70,7 +76,7 @@ export const recomputeCreature = new ValidatedMethod({
* computed and written to the database
*/
function computeCreatureById(charId){
let char = buildCreature();
let char = buildCreature(charId);
char = computeCreature(char);
writeCreature(char);
return char;
@@ -109,19 +115,21 @@ function writeAttributes(char) {
}},
}
}
if (att.mod){
op.updateMany.update.mod = att.mod;
if (typeof att.mod === 'number'){
op.updateMany.update.$set.mod = att.mod;
}
return op;
});
if (Meteor.isServer){
Attributes.rawCollection().bulkWrite( bulkWriteOps, {ordered : false});
Attributes.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2))
});
} else {
_.each(bulkWriteOps, op => {
Attributes.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
};
@@ -149,13 +157,15 @@ function writeSkills(char) {
return op;
});
if (Meteor.isServer){
Skills.rawCollection().bulkWrite( bulkWriteOps, {ordered : false});
Skills.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2))
});
} else {
_.each(bulkWriteOps, op => {
Skills.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
};
/**
* Write all the damange multipliers from the in-memory char object to the docs
@@ -176,13 +186,15 @@ function writeDamageMultipliers(char) {
return op;
});
if (Meteor.isServer){
DamageMultipliers.rawCollection().bulkWrite( bulkWriteOps, {ordered : false});
DamageMultipliers.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2))
});
} else {
_.each(bulkWriteOps, op => {
DamageMultipliers.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
};
/**
@@ -307,7 +319,7 @@ function buildCreature(charId){
}
});
return char;
}
};
/**
@@ -330,7 +342,7 @@ export function computeCreature(char){
computeStat (stat, char);
}
return char;
}
};
/**

View File

@@ -1,4 +1,5 @@
import DEFAULT_CHARACTER_DOCS from '/imports/api/creature/DEFAULT_CHARACTER_DOCS.js';
import { Random } from 'meteor/random';
const setParent = function(charId){
let parent = {
@@ -27,9 +28,12 @@ const getRacialBonusEffect = function(charId, attribute, bonus){
};
};
const giveDocsOrder = function(docArray){
const giveDocsOrderAndIds = function(docArray){
for (i in docArray){
docArray[i].order = +i;
if (!docArray[i]._id){
docArray[i]._id = Random.id();
}
}
};
@@ -164,7 +168,7 @@ const getDefaultCharacterDocs = function(charId, {
// Order the docs
for (collection in docs){
giveDocsOrder(docs[collection]);
giveDocsOrderAndIds(docs[collection]);
}
return docs

View File

@@ -5,7 +5,7 @@ import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
let Attributes = new Mongo.Collection("attributes");
/*
* Attributes are whole numbered stats of a character
* Attributes are numbered stats of a character
*/
attributeSchema = new SimpleSchema({
charId: {
@@ -32,7 +32,8 @@ attributeSchema = new SimpleSchema({
allowedValues: [
"ability", //Strength, Dex, Con, etc.
"stat", // Speed, Armor Class
"hitDice",
"modifier", // Proficiency Bonus, Initiative
"hitDice", // d12 hit dice
"healthBar", // Hitpoints, Temporary Hitpoints
"resource", // Rages, sorcery points
"spellSlot", // Level 1, 2, 3... spell slots

View File

@@ -71,15 +71,11 @@ let skillSchema = new SimpleSchema({
type: SimpleSchema.Integer,
optional: true,
},
enabled: {
type: Boolean,
defaultValue: true,
},
});
Skills.attachSchema(skillSchema);
//Skills.attachBehaviour("softRemovable");
makeChild(Skills, ["enabled"]); //children of lots of things
makeChild(Skills); //children of lots of things
export default Skills;

View File

@@ -40,6 +40,7 @@
import Vue from "vue";
import AbilityListTile from '/imports/ui/components/AbilityListTile.Story.vue';
import AttributeCard from '/imports/ui/components/AttributeCard.Story.vue';
import ColumnLayout from "/imports/ui/components/ColumnLayout.Story.vue";
import HealthBar from '/imports/ui/components/HealthBar.Story.vue';
import HitDiceListTile from '/imports/ui/components/HitDiceListTile.Story.vue';
import SkillListTile from '/imports/ui/components/SkillListTile.Story.vue';
@@ -48,6 +49,7 @@
components: {
AbilityListTile,
AttributeCard,
ColumnLayout,
HealthBar,
HitDiceListTile,
SkillListTile,

View File

@@ -1,13 +1,16 @@
<template>
<div>
<div class="character-sheet">
<v-toolbar app :color="character.color || 'primary'" :dark="isDarkColor(character.color || theme.primary)">
<v-btn v-if="showMenuButton" flat icon @click="toggleDrawer">
<v-icon>menu</v-icon>
</v-btn>
<v-btn v-if="showMenuButton" flat icon @click="recompute(character._id)">
<v-icon>refresh</v-icon>
</v-btn>
<span>{{character.name}}</span>
</v-toolbar>
<v-content v-if="$subReady.singleCharacter">
{{character}}
<stats-tab :char-id="character._id"/>
</v-content>
<v-content v-else>
<v-progress-circular indeterminate />
@@ -20,12 +23,17 @@
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
import { mapMutations } from "vuex";
import theme from '/imports/ui/theme.js';
import StatsTab from '/imports/ui/character/StatsTab.vue';
import { recomputeCreature } from '/imports/api/creature/creatureComputation.js'
export default {
props: {
showMenuButton: Boolean,
charId: String,
},
components: {
StatsTab,
},
data(){return {
theme,
}},
@@ -33,6 +41,9 @@
...mapMutations([
"toggleDrawer",
]),
recompute(charId){
recomputeCreature.call({charId});
},
isDarkColor,
},
meteor: {
@@ -47,5 +58,4 @@
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,184 @@
<template lang="html">
<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>
</div>
<column-layout>
<div class="ability-scores">
<v-card>
<v-list>
<template v-for="(ability, index) in abilities">
<v-divider v-if="index !== 0"/>
<ability-list-tile
:key="ability._id"
:name="ability.name"
:score="ability.value"
:modifier="ability.mod"
/>
</template>
</v-list>
</v-card>
</div>
<div v-for="stat in stats" class="stat">
<attribute-card
:name="stat.name"
:value="stat.value"
/>
</div>
<div v-for="modifier in modifiers" class="modifier">
<attribute-card modifier
:name="modifier.name"
:value="modifier.value"
/>
</div>
<div class="hit-dice">
<v-card>
<v-list>
<v-subheader>Hit Dice</v-subheader>
<template v-for="(hitDie, index) in hitDice">
<v-divider v-if="index !== 0"/>
<hit-dice-list-tile
v-bind="hitDie"
/>
</template>
</v-list>
</v-card>
</div>
<div class="saving-throws">
<v-card>
<v-list>
<v-subheader>Saving Throws</v-subheader>
<skill-list-tile
v-for="save in savingThrows"
v-bind="save"
:key="save._id"
/>
</v-list>
</v-card>
</div>
<div class="skills">
<v-card>
<v-list>
<v-subheader>Skills</v-subheader>
<skill-list-tile
v-for="skill in skills"
v-bind="skill"
:key="skill._id"
/>
</v-list>
</v-card>
</div>
</column-layout>
</div>
</template>
<script>
import Attributes from '/imports/api/creature/properties/Attributes.js';
import Skills from '/imports/api/creature/properties/Skills.js';
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 HitDiceListTile from '/imports/ui/components/HitDiceListTile.vue';
import SkillListTile from '/imports/ui/components/SkillListTile.vue';
const getAttributeOfType = function(charId, type){
return Attributes.find({charId, type}, {sort: {order: 1}});
};
export default {
props: {
charId: String,
},
components: {
AbilityListTile,
AttributeCard,
ColumnLayout,
HealthBar,
HitDiceListTile,
SkillListTile,
},
meteor: {
healthBars(){
return Attributes.find({
charId: this.charId,
type: 'healthBar',
value: {$ne: 0},
}, {
sort: {order: 1},
});
},
abilities(){
return getAttributeOfType(this.charId, 'ability');
},
stats(){
return getAttributeOfType(this.charId, 'stat');
},
modifiers(){
return getAttributeOfType(this.charId, 'modifier');
},
hitDice(){
return Attributes.find({
charId: this.charId,
type: 'hitDice',
value: {$ne: 0},
}, {
sort: {order: 1},
}).map(hd => {
let diceMatch = hd.variableName.match(/d(\d+)/);
let dice = diceMatch && +diceMatch[1];
let con = Attributes.findOne({
charId: this.charId,
variableName: "constitution"
});
let conMod = con && con.mod;
return {
dice,
conMod,
key: hd._id,
maxValue: hd.value,
value: hd.value - (hd.adjustment || 0),
}
});
},
savingThrows(){
return Skills.find({
charId: this.charId,
type: 'save',
}, {
sort: {order: 1},
});
},
skills(){
return Skills.find({
charId: this.charId,
type: 'skill',
}, {
sort: {order: 1},
});
},
},
};
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,24 @@
<template lang="html">
<column-layout>
<div v-for="(height, n) in cardHeights" :key="n">
<v-card :height="height"/>
</div >
</column-layout>
</template>
<script>
import ColumnLayout from "/imports/ui/components/ColumnLayout.vue";
import { _ } from "meteor/underscore";
export default {
dontWrap: true,
data(){return{
cardHeights: _.times(12, n => `${_.random(100, 500)}px`),
}},
components: {
ColumnLayout,
},
};
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,29 @@
<template lang="html" functional>
<div class="column-layout">
<slot/>
</div>
</template>
<script>
export default {
};
</script>
<style lang="css" scoped>
.column-layout {
column-count: 4;
column-fill: balance;
column-gap: 0;
column-width: 240px;
transform: translateZ(0);
padding: 4px;
}
.column-layout >>> > div {
-webkit-backface-visibility: hidden;
-webkit-transform: translateX(0);
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
padding: 4px;
}
</style>

View File

@@ -17,28 +17,27 @@
{
name: 'Animal Handling',
proficiency: 1,
modifier: 4,
value: 4,
}, {
name: 'Deception',
proficiency: 0,
modifier: -0,
advantage: true,
disadvantage: true,
value: -0,
advantage: 0,
}, {
name: 'Intimidation',
proficiency: 2,
modifier: 6,
advantage: true,
value: 6,
advantage: 1,
}, {
name: 'Insight',
proficiency: 0.5,
modifier: -2,
disadvantage: true,
value: -2,
advantage: -1,
}, {
name: 'History',
conditionalBenefit: "Some cool benefit",
fail: true,
disadvantage: true,
conditionalBenefits: 1,
fail: 1,
advantage: -1,
}
]
}},

View File

@@ -10,9 +10,9 @@
<span class="prof-mod">
{{displayedModifier}}
</span>
{{name}}<template v-if="conditionalBenefit">*</template>
<v-icon size="20px" v-if="advantage && !disadvantage">arrow_upward</v-icon>
<v-icon size="20px" v-if="!advantage && disadvantage">arrow_downward</v-icon>
{{name}}<template v-if="conditionalBenefits">*</template>
<v-icon size="20px" v-if="advantage > 0">arrow_upward</v-icon>
<v-icon size="20px" v-if="advantage < 0">arrow_downward</v-icon>
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
@@ -24,12 +24,11 @@ import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
export default {
props: {
proficiency: Number,
advantage: Boolean,
disadvantage: Boolean,
fail: Boolean,
modifier: Number,
advantage: Number,
fail: Number,
value: Number,
name: String,
conditionalBenefit: String,
conditionalBenefits: Number,
},
methods: {
click(e){
@@ -49,7 +48,7 @@ export default {
}
},
displayedModifier(){
let mod = this.modifier;
let mod = this.value;
if (this.fail){
return 'fail';
} else {

View File

@@ -1 +1,2 @@
import "/imports/server/publications/index.js";
import "/imports/api/creature/creatureComputation.js";