Characters now insert with intelligent defaults based on the character wizard
This commit is contained in:
@@ -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?
|
||||
|
||||
@@ -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 {
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
});
|
||||
173
app/imports/api/creature/getDefaultCharacterDocs.js
Normal file
173
app/imports/api/creature/getDefaultCharacterDocs.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user