diff --git a/app/imports/api/creature/Creatures.js b/app/imports/api/creature/Creatures.js
index 212dac2c..aa0fe97c 100644
--- a/app/imports/api/creature/Creatures.js
+++ b/app/imports/api/creature/Creatures.js
@@ -1,9 +1,10 @@
-import { ValidatedMethod } from 'meteor/mdg:validated-method';
import SimpleSchema from 'simpl-schema';
import Effects from "/imports/api/creature/properties/Effects.js"
import deathSaveSchema from "/imports/api/creature/subSchemas/DeathSavesSchema.js"
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
-import getDefaultCreatureDocs from "/imports/api/creature/getDefaultCreatureDocs.js";
+
+//Methods
+import '/imports/api/creature/insertCreature.js';
//set up the collection for creatures
Creatures = new Mongo.Collection("creatures");
@@ -24,20 +25,19 @@ let creatureSchema = new SimpleSchema({
backstory: {type: String, defaultValue: "", trim: false, optional: true},
//mechanics
- deathSave: {type: deathSaveSchema},
+ deathSave: {type: deathSaveSchema, defaultValue: {}},
xp: {type: SimpleSchema.Integer, defaultValue: 0},
weightCarried: {type: Number, defaultValue: 0},
level: {type: SimpleSchema.Integer, defaultValue: 0},
type: {type: String, defaultValue: "pc", allowedValues: ["pc", "npc", "monster"]},
//permissions
- party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
readers: {type: Array, defaultValue: [], index: 1},
"readers.$": {type: String, regEx: SimpleSchema.RegEx.Id},
writers: {type: Array, defaultValue: [], index: 1},
"writers.$": {type: String, regEx: SimpleSchema.RegEx.Id},
- settings: {type: Object},
+ settings: {type: Object, defaultValue: {}},
//how many experiences to load at a time in XP table
"settings.experiencesInc": {type: SimpleSchema.Integer, defaultValue: 20},
//slowed down by carrying too much?
diff --git a/app/imports/api/creature/DEFAULT_CHARACTER_STATS.js b/app/imports/api/creature/DEFAULT_CHARACTER_DOCS.js
similarity index 97%
rename from app/imports/api/creature/DEFAULT_CHARACTER_STATS.js
rename to app/imports/api/creature/DEFAULT_CHARACTER_DOCS.js
index 4e30e0d8..75c68a2d 100644
--- a/app/imports/api/creature/DEFAULT_CHARACTER_STATS.js
+++ b/app/imports/api/creature/DEFAULT_CHARACTER_DOCS.js
@@ -1,4 +1,7 @@
-export default {
+// Don't just export a constant, because deep nested objects could be changed
+// by code that requires it. Exporting a function that returns the newly created
+// object is a little safer.
+export default () => ({
"attributes": [
{"name": "Strength", "variableName": "strength", "baseValue": 10, "type": "ability"},
{"name": "Dexterity", "variableName": "dexterity", "baseValue": 10, "type": "ability"},
@@ -132,4 +135,4 @@ export default {
],
},
],
-}
+});
diff --git a/app/imports/api/creature/getDefaultCharacterDocs.js b/app/imports/api/creature/getDefaultCharacterDocs.js
new file mode 100644
index 00000000..6dfc1cfd
--- /dev/null
+++ b/app/imports/api/creature/getDefaultCharacterDocs.js
@@ -0,0 +1,173 @@
+import DEFAULT_CHARACTER_DOCS from '/imports/api/creature/DEFAULT_CHARACTER_DOCS.js';
+
+const setParent = function(charId){
+ let parent = {
+ collection: "Creatures",
+ id: charId,
+ group: "default",
+ };
+ return doc => {
+ doc.parent = parent;
+ doc.charId = charId;
+ };
+};
+
+const getRacialBonusEffect = function(charId, attribute, bonus){
+ return {
+ name: "Race Bonus",
+ stat: attribute,
+ operation: "add",
+ value: bonus,
+ parent: {
+ collection: "Creatures",
+ id: charId,
+ group: "racial",
+ },
+ charId: charId,
+ };
+};
+
+const giveDocsOrder = function(docArray){
+ for (i in docArray){
+ docArray[i].order = +i;
+ }
+};
+
+const getDefaultCharacterDocs = function(charId, {
+ // Character form data
+ baseStrength = 10,
+ baseDexterity = 10,
+ baseConstitution = 10,
+ baseIntelligence = 10,
+ baseWisdom = 10,
+ baseCharisma = 10,
+ strengthBonus = 0,
+ dexterityBonus = 0,
+ constitutionBonus = 0,
+ intelligenceBonus = 0,
+ wisdomBonus = 0,
+ charismaBonus = 0,
+ hitDice = "d8",
+ cls = "Class",
+ level = 1,
+}){
+ let docs = DEFAULT_CHARACTER_DOCS();
+
+ // Setup the base ability scores
+ docs.attributes[0].baseValue = baseStrength;
+ docs.attributes[1].baseValue = baseDexterity;
+ docs.attributes[2].baseValue = baseConstitution;
+ docs.attributes[3].baseValue = baseIntelligence;
+ docs.attributes[4].baseValue = baseWisdom;
+ docs.attributes[5].baseValue = baseCharisma;
+
+ // Set up racial bonuses
+ if (strengthBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'strength', strengthBonus)
+ );
+ }
+ if (dexterityBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'dexterity', dexterityBonus)
+ );
+ }
+ if (constitutionBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'constitution', constitutionBonus)
+ );
+ }
+ if (intelligenceBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'intelligence', intelligenceBonus)
+ );
+ }
+ if (wisdomBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'wisdom', wisdomBonus)
+ );
+ }
+ if (charismaBonus) {
+ docs.effects.push(
+ getRacialBonusEffect(charId, 'charisma', charismaBonus)
+ );
+ }
+
+ // Set up Class
+ const strippedCls = cls.replace(/\s+/g, '')
+ const classId = Random.id();
+ docs.classes = [{
+ charId,
+ level,
+ name: cls,
+ }];
+
+ // Setup hit dice
+ docs.effects.push({
+ name: cls,
+ stat: `${hitDice}HitDice`,
+ operation: "add",
+ calculation: `${strippedCls}Level`,
+ parent: {
+ collection: "Classes",
+ id: classId,
+ },
+ charId: charId,
+ });
+
+ // Setup health for all class levels
+ let healthPerLevel = 4;
+ if (hitDice == "d6"){
+ healthPerLevel = 4;
+ } else if (hitDice == "d8"){
+ healthPerLevel = 5;
+ } else if (hitDice == "d10"){
+ healthPerLevel = 6;
+ } else if (hitDice == "d12"){
+ healthPerLevel = 7;
+ }
+ docs.effects.push({
+ name: cls,
+ stat: `${hitDice}HitDice`,
+ operation: "add",
+ calculation: `${healthPerLevel - 2} + ${healthPerLevel} * ${strippedCls}Level`,
+ parent: {
+ collection: "Classes",
+ id: classId,
+ },
+ charId: charId,
+ });
+
+ // Set the parents for base items
+ docs.attributes.forEach(setParent(charId));
+ docs.skills.forEach(setParent(charId));
+ docs.damageMultipliers.forEach(setParent(charId));
+ docs.effects.forEach(setParent(charId));
+ docs.containers.forEach(setParent(charId));
+
+ // Set up parenting on items and move them to the top level items object
+ docs.items = [];
+ docs.containers.forEach(container => {
+ container._id = Random.id();
+ const parent = {
+ collection: "Containers",
+ id: container._id,
+ };
+ container.items.forEach(item => {
+ item.parent = parent;
+ item.charId = charId;
+ });
+ // Move the items to the top level array
+ docs.items.push(...container.items);
+ delete container.items;
+ });
+
+ // Order the docs
+ for (collection in docs){
+ giveDocsOrder(docs[collection]);
+ }
+
+ return docs
+};
+
+export default getDefaultCharacterDocs;
diff --git a/app/imports/api/creature/getDefaultCreatureDocs.js b/app/imports/api/creature/getDefaultCreatureDocs.js
deleted file mode 100644
index 2828d938..00000000
--- a/app/imports/api/creature/getDefaultCreatureDocs.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import DEFAULT_CHARACTER_STATS from '/imports/api/creature/DEFAULT_CHARACTER_STATS.js';
-
-getDefaultCreatureDocs = function(charId, creatureType = "pc"){
- // Setup the docs object which will be returned
- let docs = {
- attributes: [],
- skills: [],
- damageMultipliers: [],
- effects: []
- };
-
- // Get the default character stats
- let stats;
- if (creatureType === "pc"){
- stats = DEFAULT_CHARACTER_STATS;
- } else {
- stats = null;
- throw new Meteor.Error("Not implemented",
- "Default stats for non-player characters aren't implemented yet");
- }
-
- // Setup the variables we'll need to share
- let order = 0;
- const baseParent = {
- collection: "Characters",
- id: charId,
- group: "default",
- };
- let name, variableName, parent, attribute, skill, dm, type, baseValue;
-
- // Attributes
- for (type in stats.attributes){
- for (let i in stats.attributes[type]){
- attribute = stats.attributes[type][i];
- if (_.isString(attribute)){
- name = attribute;
- variableName = attribute.toLowerCase();
- } else {
- name = attribute.name;
- variableName = attribute.variableName;
- }
- baseValue = attribute.baseValue;
- parent = _.clone(baseParent);
- docs.attributes.push({
- _id: Random.id,
- charId, name, variableName, order, type, parent, baseValue,
- });
- order++;
- }
- }
-
- // Skills
- order = 0;
- for (type in stats.skills){
- for (let i in stats.skills[type]){
- skill = stats.skills[type][i];
- docs.skills.push({
- _id: Random.id,
- charId,
- type,
- order,
- name: skill.name,
- variableName: skill.variableName,
- ability: skill.ability,
- parent: _.clone(baseParent),
- });
- order++;
- }
- }
-
- // Damage Multipliers
- order = 0;
- for (let i in stats.damageMultipliers){
- dm = stats.damageMultipliers[i];
- docs.damageMultipliers.push({
- _id: Random.id,
- charId,
- order,
- name: dm.name,
- variableName: dm.variableName,
- parent: _.clone(baseParent),
- });
- order++;
- }
-
- // Effects
- order = 0;
- for (let i in stats.effects){
- eff = stats.effects[i];
- docs.effects.push({
- _id: Random.id,
- charId,
- order,
- name: eff.name,
- stat: eff.stat,
- operation: eff.operation,
- calculation:eff.calculation,
- });
- order++;
- }
- return docs;
-}
-
-export default getDefaultCreatureDocs;
diff --git a/app/imports/api/creature/insertCreature.js b/app/imports/api/creature/insertCreature.js
index 0b1be7a1..05f2e9b3 100644
--- a/app/imports/api/creature/insertCreature.js
+++ b/app/imports/api/creature/insertCreature.js
@@ -1,35 +1,47 @@
-import getDefaultCreatureDocs from '/imports/api/creature/getDefaultCreatureDocs.js';
+import getDefaultCharacterDocs from '/imports/api/creature/getDefaultCharacterDocs.js';
+import Attributes from '/imports/api/creature/properties/Attributes.js';
+import Skills from '/imports/api/creature/properties/Skills.js';
+import DamageMultipliers from '/imports/api/creature/properties/DamageMultipliers.js';
+import Effects from '/imports/api/creature/properties/Effects.js';
+import Containers from '/imports/api/inventory/Containers.js';
+import Items from '/imports/api/inventory/Items.js';
+import Classes from '/imports/api/creature/properties/Classes.js';
-const addDefaultStats = function(charId){
- const defaultDocs = getDefaultCreatureDocs(charId);
- Attributes.rawCollection().insert(getDefa.attributes, {ordered: false});
- Skills.rawCollection().insert(getDefa.skills, {ordered: false});
- DamageMultipliers.rawCollection().insert(getDefa.damageMultipliers, {ordered: false});
+const addDefaultDocs = function(docs){
+ Attributes.rawCollection().insert(docs.attributes, {ordered: false});
+ Skills.rawCollection().insert(docs.skills, {ordered: false});
+ DamageMultipliers.rawCollection().insert(docs.damageMultipliers, {ordered: false});
+ Effects.rawCollection().insert(docs.effects, {ordered: false});
+ Containers.rawCollection().insert(docs.containers, {ordered: false});
+ Items.rawCollection().insert(docs.items, {ordered: false});
+ Classes.rawCollection().insert(docs.classes, {ordered: false});
};
const insertCreature = new ValidatedMethod({
name: "Creatures.methods.insertCharacter", // DDP method name
- validate: new SimpleSchema({
- name: {
- type: String,
- optional: true,
- },
- }).validator(),
+ validate: null,
- run({name}) {
+ run(characterFormData) {
if (!this.userId) {
throw new Meteor.Error("Creatures.methods.insert.denied",
"You need to be logged in to insert a creature");
}
// Create the creature document
- let charId = Creatures.insert({name, owner: this.userId});
+ let charId = Creatures.insert({
+ name: characterFormData.name,
+ owner: this.userId,
+ alignment: characterFormData.alignment,
+ gender: characterFormData.gender,
+ race: characterFormData.race,
+ });
this.unblock();
- //Add all the required attributes to it
if (Meteor.isServer){
- addDefaultStats(charId);
+ //Add all the required attributes to it
+ let docs = getDefaultCharacterDocs(charId, characterFormData);
+ addDefaultDocs(docs);
}
return charId;
},
diff --git a/app/imports/api/creature/properties/DamageMultipliers.js b/app/imports/api/creature/properties/DamageMultipliers.js
index bf328d12..98032e00 100644
--- a/app/imports/api/creature/properties/DamageMultipliers.js
+++ b/app/imports/api/creature/properties/DamageMultipliers.js
@@ -1,12 +1,12 @@
import SimpleSchema from 'simpl-schema';
import {makeChild} from "/imports/api/parenting.js";
-DamageMultipliers = new Mongo.Collection("damageMultipliers");
+const DamageMultipliers = new Mongo.Collection("damageMultipliers");
/*
* DamageMultipliers are whole numbered stats of a character
*/
-Schemas.DamageMultiplier = new SimpleSchema({
+const damageMultiplierSchema = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
@@ -30,7 +30,9 @@ Schemas.DamageMultiplier = new SimpleSchema({
},
});
-DamageMultipliers.attachSchema(Schemas.DamageMultiplier);
+DamageMultipliers.attachSchema(damageMultiplierSchema);
// DamageMultipliers.attachBehaviour("softRemovable");
makeChild(DamageMultipliers, ["enabled"]); //children of lots of things
+
+export default DamageMultipliers;
diff --git a/app/imports/api/inventory/Items.js b/app/imports/api/inventory/Items.js
index 41d819a5..ebbd38db 100644
--- a/app/imports/api/inventory/Items.js
+++ b/app/imports/api/inventory/Items.js
@@ -206,3 +206,4 @@ makeChild(Items); //children of containers
makeParent(Items, ["name", "enabled"]); //parents of effects and attacks
//Items.allow(CHARACTER_SUBSCHEMA_ALLOW);
+export default Items;
diff --git a/app/imports/ui/character/CharacterCreationDialog.vue b/app/imports/ui/character/CharacterCreationDialog.vue
index e3084e19..e5539314 100644
--- a/app/imports/ui/character/CharacterCreationDialog.vue
+++ b/app/imports/ui/character/CharacterCreationDialog.vue
@@ -20,7 +20,7 @@
-
+
diff --git a/app/imports/ui/pages/CharacterList.vue b/app/imports/ui/pages/CharacterList.vue
index fdc584c6..7b8d4618 100644
--- a/app/imports/ui/pages/CharacterList.vue
+++ b/app/imports/ui/pages/CharacterList.vue
@@ -78,6 +78,7 @@
import ToolbarLayout from "/imports/ui/layouts/ToolbarLayout.vue";
import LabeledFab from "/imports/ui/components/LabeledFab.vue";
import CharacterCreationDialog from "/imports/ui/character/CharacterCreationDialog.vue";
+ import insertCreature from '/imports/api/creature/insertCreature.js';
const characterTransform = function(char){
char.url = `\/character\/${char._id}\/${char.urlName || "-"}`;
@@ -123,15 +124,14 @@
},
},
methods: {
- insertCharacter(e){
- console.log(e);
+ insertCharacter(){
store.commit("pushDialogStack", {
component: CharacterCreationDialog,
data: {},
element: undefined,
returnElement: undefined,
callback(result){
- console.log({result});
+ insertCreature.call(result);
},
});
},