Completed the stats tab, conditions not added yet
This commit is contained in:
@@ -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": [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
184
app/imports/ui/character/StatsTab.vue
Normal file
184
app/imports/ui/character/StatsTab.vue
Normal 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>
|
||||
24
app/imports/ui/components/ColumnLayout.Story.vue
Normal file
24
app/imports/ui/components/ColumnLayout.Story.vue
Normal 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>
|
||||
29
app/imports/ui/components/ColumnLayout.vue
Normal file
29
app/imports/ui/components/ColumnLayout.vue
Normal 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>
|
||||
@@ -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,
|
||||
}
|
||||
]
|
||||
}},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import "/imports/server/publications/index.js";
|
||||
import "/imports/api/creature/creatureComputation.js";
|
||||
|
||||
Reference in New Issue
Block a user