Characters now insert with intelligent defaults based on the character wizard

This commit is contained in:
Stefan Zermatten
2018-12-19 14:15:56 +02:00
parent 13669fdc91
commit 798cf3edd7
9 changed files with 221 additions and 134 deletions

View File

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

View File

@@ -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 {
],
},
],
}
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@
<v-stepper-content step="1">
<v-text-field label="Name" v-model="name"></v-text-field>
<v-text-field label="Gender" v-model="gender"></v-text-field>
<v-text-field label="Alignment" v-model="alignment"></v-text-field>
<v-text-field label="Alignment" v-model="alignment" @keydown.tab="step++"></v-text-field>
</v-stepper-content>
<v-stepper-content step="2">
<v-text-field label="Race" v-model="race"></v-text-field>

View File

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