Began abstracting default abilities, damage multipliers and skills into rulesets.
This commit is contained in:
@@ -17,6 +17,10 @@ Schemas.Attribute = new SimpleSchema({
|
|||||||
variableName: {
|
variableName: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
// Attributes need to store their order to keep the sheet consistent
|
||||||
|
order: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
allowedValues: [
|
allowedValues: [
|
||||||
@@ -26,7 +30,7 @@ Schemas.Attribute = new SimpleSchema({
|
|||||||
"healthBar", // Hitpoints, Temporary Hitpoints
|
"healthBar", // Hitpoints, Temporary Hitpoints
|
||||||
"resource", // Rages, sorcery points
|
"resource", // Rages, sorcery points
|
||||||
"spellSlot", // Level 1, 2, 3... spell slots
|
"spellSlot", // Level 1, 2, 3... spell slots
|
||||||
"attribute", // Aren't displayed, Jump height, Carry capacity
|
"utility", // Aren't displayed, Jump height, Carry capacity
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
// TODO recalculate carried weight method
|
||||||
|
// TODO Add attribute, skill, damageMultiplier and bundle library models
|
||||||
|
// Bundle libraries should support races, classes, and rulesets
|
||||||
|
// TODO Add monster library
|
||||||
|
|
||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
|
|
||||||
const recomputeCharacter = new ValidatedMethod({
|
const recomputeCharacter = new ValidatedMethod({
|
||||||
@@ -8,7 +13,7 @@ const recomputeCharacter = new ValidatedMethod({
|
|||||||
charId: { type: String }
|
charId: { type: String }
|
||||||
}).validator(),
|
}).validator(),
|
||||||
|
|
||||||
run({ charId }) {
|
run({charId}) {
|
||||||
if (!canEditCharacter(charId, this.userId)) {
|
if (!canEditCharacter(charId, this.userId)) {
|
||||||
// Throw errors with a specific error code
|
// Throw errors with a specific error code
|
||||||
throw new Meteor.Error('Characters.methods.recomputeCharacter.denied',
|
throw new Meteor.Error('Characters.methods.recomputeCharacter.denied',
|
||||||
@@ -21,6 +26,31 @@ const recomputeCharacter = new ValidatedMethod({
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const recomputeCharacterXP = new ValidatedMethod({
|
||||||
|
name: "Characters.methods.recomputeCharacterXP",
|
||||||
|
|
||||||
|
validate: new SimpleSchema({
|
||||||
|
charId: { type: String }
|
||||||
|
}).validator(),
|
||||||
|
|
||||||
|
run({charId}) {
|
||||||
|
if (!canEditCharacter(charId, this.userId)) {
|
||||||
|
// Throw errors with a specific error code
|
||||||
|
throw new Meteor.Error("Characters.methods.recomputeCharacterXP.denied",
|
||||||
|
"You do not have permission to recompute this character's XP");
|
||||||
|
}
|
||||||
|
var xp = 0;
|
||||||
|
Experiences.find(
|
||||||
|
{charId: charId},
|
||||||
|
{fields: {value: 1}}
|
||||||
|
).forEach(function(e){
|
||||||
|
xp += e.value;
|
||||||
|
});
|
||||||
|
//TODO write the XP to the database, don't just return it
|
||||||
|
return xp;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is the heart of DiceCloud. It recomputes a character's stats,
|
* This function is the heart of DiceCloud. It recomputes a character's stats,
|
||||||
* distilling down effects and proficiencies into the final stats that make up
|
* distilling down effects and proficiencies into the final stats that make up
|
||||||
@@ -342,11 +372,11 @@ const combineSkill = function(stat, char){
|
|||||||
if (prof.value > stat.proficiency) stat.proficiency = prof.value;
|
if (prof.value > stat.proficiency) stat.proficiency = prof.value;
|
||||||
}
|
}
|
||||||
let profBonus;
|
let profBonus;
|
||||||
if (char.atts.proificiencyBonus){
|
if (char.skills.proificiencyBonus){
|
||||||
if (!char.atts.proficiencyBonus.computed){
|
if (!char.skills.proficiencyBonus.computed){
|
||||||
computeStat(char.atts.proficiencyBonus, char);
|
computeStat(char.skills.proficiencyBonus, char);
|
||||||
}
|
}
|
||||||
profBonus = char.atts.proficiencyBonus.result;
|
profBonus = char.skills.proficiencyBonus.result;
|
||||||
} else {
|
} else {
|
||||||
profBonus = Math.floor(char.level / 4 + 1.75);
|
profBonus = Math.floor(char.level / 4 + 1.75);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,151 +16,9 @@ Schemas.Character = new SimpleSchema({
|
|||||||
flaws: {type: String, defaultValue: "", trim: false, optional: true},
|
flaws: {type: String, defaultValue: "", trim: false, optional: true},
|
||||||
backstory: {type: String, defaultValue: "", trim: false, optional: true},
|
backstory: {type: String, defaultValue: "", trim: false, optional: true},
|
||||||
|
|
||||||
//attributes
|
|
||||||
//ability scores
|
|
||||||
strength: {type: Schemas.Attribute},
|
|
||||||
dexterity: {type: Schemas.Attribute},
|
|
||||||
constitution: {type: Schemas.Attribute},
|
|
||||||
intelligence: {type: Schemas.Attribute},
|
|
||||||
wisdom: {type: Schemas.Attribute},
|
|
||||||
charisma: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//stats
|
|
||||||
hitPoints: {type: Schemas.Attribute},
|
|
||||||
tempHP: {type: Schemas.Attribute},
|
|
||||||
experience: {type: Schemas.Attribute},
|
|
||||||
proficiencyBonus: {type: Schemas.Attribute},
|
|
||||||
speed: {type: Schemas.Attribute},
|
|
||||||
weight: {type: Schemas.Attribute},
|
|
||||||
age: {type: Schemas.Attribute},
|
|
||||||
ageRate: {type: Schemas.Attribute},
|
|
||||||
armor: {type: Schemas.Attribute},
|
|
||||||
carryMultiplier: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//resources
|
|
||||||
level1SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level2SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level3SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level4SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level5SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level6SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level7SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level8SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level9SpellSlots: {type: Schemas.Attribute},
|
|
||||||
ki: {type: Schemas.Attribute},
|
|
||||||
sorceryPoints: {type: Schemas.Attribute},
|
|
||||||
rages: {type: Schemas.Attribute},
|
|
||||||
superiorityDice: {type: Schemas.Attribute},
|
|
||||||
expertiseDice: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//specific features
|
|
||||||
rageDamage: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//hit dice
|
|
||||||
d6HitDice: {type: Schemas.Attribute},
|
|
||||||
d8HitDice: {type: Schemas.Attribute},
|
|
||||||
d10HitDice: {type: Schemas.Attribute},
|
|
||||||
d12HitDice: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//vulnerabilities
|
|
||||||
acidMultiplier: {type: Schemas.Attribute},
|
|
||||||
bludgeoningMultiplier: {type: Schemas.Attribute},
|
|
||||||
coldMultiplier: {type: Schemas.Attribute},
|
|
||||||
fireMultiplier: {type: Schemas.Attribute},
|
|
||||||
forceMultiplier: {type: Schemas.Attribute},
|
|
||||||
lightningMultiplier: {type: Schemas.Attribute},
|
|
||||||
necroticMultiplier: {type: Schemas.Attribute},
|
|
||||||
piercingMultiplier: {type: Schemas.Attribute},
|
|
||||||
poisonMultiplier: {type: Schemas.Attribute},
|
|
||||||
psychicMultiplier: {type: Schemas.Attribute},
|
|
||||||
radiantMultiplier: {type: Schemas.Attribute},
|
|
||||||
slashingMultiplier: {type: Schemas.Attribute},
|
|
||||||
thunderMultiplier: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//skills
|
|
||||||
//saves
|
|
||||||
strengthSave: {type: Schemas.Skill},
|
|
||||||
"strengthSave.ability": {type: String, defaultValue: "strength"},
|
|
||||||
|
|
||||||
dexteritySave: {type: Schemas.Skill},
|
|
||||||
"dexteritySave.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
constitutionSave:{type: Schemas.Skill},
|
|
||||||
"constitutionSave.ability": {type: String, defaultValue: "constitution"},
|
|
||||||
|
|
||||||
intelligenceSave:{type: Schemas.Skill},
|
|
||||||
"intelligenceSave.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
wisdomSave: {type: Schemas.Skill},
|
|
||||||
"wisdomSave.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
charismaSave: {type: Schemas.Skill},
|
|
||||||
"charismaSave.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
//skill skills
|
|
||||||
acrobatics: {type: Schemas.Skill},
|
|
||||||
"acrobatics.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
animalHandling: {type: Schemas.Skill},
|
|
||||||
"animalHandling.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
arcana: {type: Schemas.Skill},
|
|
||||||
"arcana.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
athletics: {type: Schemas.Skill},
|
|
||||||
"athletics.ability": {type: String, defaultValue: "strength"},
|
|
||||||
|
|
||||||
deception: {type: Schemas.Skill},
|
|
||||||
"deception.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
history: {type: Schemas.Skill},
|
|
||||||
"history.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
insight: {type: Schemas.Skill},
|
|
||||||
"insight.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
intimidation: {type: Schemas.Skill},
|
|
||||||
"intimidation.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
investigation: {type: Schemas.Skill},
|
|
||||||
"investigation.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
medicine: {type: Schemas.Skill},
|
|
||||||
"medicine.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
nature: {type: Schemas.Skill},
|
|
||||||
"nature.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
perception: {type: Schemas.Skill},
|
|
||||||
"perception.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
performance: {type: Schemas.Skill},
|
|
||||||
"performance.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
persuasion: {type: Schemas.Skill},
|
|
||||||
"persuasion.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
religion: {type: Schemas.Skill},
|
|
||||||
"religion.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
sleightOfHand: {type: Schemas.Skill},
|
|
||||||
"sleightOfHand.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
stealth: {type: Schemas.Skill},
|
|
||||||
"stealth.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
survival: {type: Schemas.Skill},
|
|
||||||
"survival.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
//Mechanical Skills
|
|
||||||
initiative: {type: Schemas.Skill},
|
|
||||||
"initiative.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
dexterityArmor: {type: Schemas.Skill},
|
|
||||||
"dexterityArmor.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
//mechanics
|
//mechanics
|
||||||
deathSave: {type: Schemas.DeathSave},
|
deathSave: {type: Schemas.DeathSave},
|
||||||
|
xp: {type: Number, defaultValue: 0},
|
||||||
|
|
||||||
//permissions
|
//permissions
|
||||||
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
||||||
@@ -196,238 +54,8 @@ Schemas.Character = new SimpleSchema({
|
|||||||
|
|
||||||
Characters.attachSchema(Schemas.Character);
|
Characters.attachSchema(Schemas.Character);
|
||||||
|
|
||||||
var attributeBase = preventLoop(function(charId, statName){
|
|
||||||
check(statName, String);
|
|
||||||
//if it's a damage multiplier, we treat it specially
|
|
||||||
if (_.contains(DAMAGE_MULTIPLIERS, statName)){
|
|
||||||
var invulnerabilityCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 0,
|
|
||||||
}).count();
|
|
||||||
if (invulnerabilityCount) return 0;
|
|
||||||
var resistCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 0.5,
|
|
||||||
}).count();
|
|
||||||
var vulnCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 2,
|
|
||||||
}).count();
|
|
||||||
if (!resistCount && !vulnCount){
|
|
||||||
return 1;
|
|
||||||
} else if (resistCount && !vulnCount){
|
|
||||||
return 0.5;
|
|
||||||
} else if (!resistCount && vulnCount){
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var value;
|
|
||||||
var base = 0;
|
|
||||||
var add = 0;
|
|
||||||
var mul = 1;
|
|
||||||
var min = Number.NEGATIVE_INFINITY;
|
|
||||||
var max = Number.POSITIVE_INFINITY;
|
|
||||||
|
|
||||||
Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: {$in: ["base", "add", "mul", "min", "max"]},
|
|
||||||
}).forEach(function(effect) {
|
|
||||||
value = evaluateEffect(charId, effect);
|
|
||||||
if (effect.operation === "base"){
|
|
||||||
if (value > base) base = value;
|
|
||||||
} else if (effect.operation === "add"){
|
|
||||||
add += value;
|
|
||||||
} else if (effect.operation === "mul"){
|
|
||||||
mul *= value;
|
|
||||||
} else if (effect.operation === "min"){
|
|
||||||
if (value > min) min = value;
|
|
||||||
} else if (effect.operation === "max"){
|
|
||||||
if (value < max) max = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var result = (base + add) * mul;
|
|
||||||
if (result < min) result = min;
|
|
||||||
if (result > max) result = max;
|
|
||||||
// Don't round carry multiplier
|
|
||||||
if (statName === "carryMultiplier"){
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return Math.floor(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Meteor.isClient) {
|
|
||||||
Template.registerHelper("characterCalculate", function(func, charId, input) {
|
|
||||||
try {
|
|
||||||
return Characters.calculate[func](charId, input);
|
|
||||||
} catch (e){
|
|
||||||
if (!Characters.calculate[func]){
|
|
||||||
throw new Error(func + "is not a function name");
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//create a local memoize with a argument concatenating hash function
|
|
||||||
var memoize = function(f) {
|
|
||||||
if (Meteor.isServer) return f;
|
|
||||||
return Tracker.memoize(f, function() {
|
|
||||||
return _.reduce(arguments, function(memo, arg) {
|
|
||||||
return memo + arg;
|
|
||||||
}, "");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//memoize funcitons that have finds and slow loops
|
//memoize funcitons that have finds and slow loops
|
||||||
Characters.calculate = {
|
Characters.calculate = {
|
||||||
getField: function(charId, fieldName) {
|
|
||||||
var fieldSelector = {};
|
|
||||||
fieldSelector[fieldName] = 1;
|
|
||||||
var char = Characters.findOne(charId, {fields: fieldSelector});
|
|
||||||
if (!char) return;
|
|
||||||
var field = char[fieldName];
|
|
||||||
if (field === undefined){
|
|
||||||
throw new Meteor.Error(
|
|
||||||
"getField failed",
|
|
||||||
"getField could not find field " +
|
|
||||||
fieldName +
|
|
||||||
" in character " +
|
|
||||||
char._id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
},
|
|
||||||
fieldValue: function(charId, fieldName) {
|
|
||||||
if (!Schemas.Character.schema(fieldName)){
|
|
||||||
throw new Meteor.Error(
|
|
||||||
"Field not found",
|
|
||||||
"Character's schema does not contain a field called: " + fieldName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//duck typing to get the right value function
|
|
||||||
//.ability implies skill
|
|
||||||
if (Schemas.Character.schema(fieldName + ".ability")){
|
|
||||||
return Characters.calculate.skillMod(charId, fieldName);
|
|
||||||
}
|
|
||||||
//adjustment implies attribute
|
|
||||||
if (Schemas.Character.schema(fieldName + ".adjustment")){
|
|
||||||
return Characters.calculate.attributeValue(charId, fieldName);
|
|
||||||
}
|
|
||||||
//fall back to just returning the field itself
|
|
||||||
return Characters.calculate.getField(charId, fieldName);
|
|
||||||
},
|
|
||||||
attributeValue: memoize(function(charId, attributeName){
|
|
||||||
var attribute = Characters.calculate.getField(charId, attributeName);
|
|
||||||
if (!attribute) return;
|
|
||||||
//base value
|
|
||||||
var value = Characters.calculate.attributeBase(charId, attributeName);
|
|
||||||
//plus adjustment
|
|
||||||
value += attribute.adjustment;
|
|
||||||
return value;
|
|
||||||
}),
|
|
||||||
attributeBase: memoize(function(charId, attributeName){
|
|
||||||
return attributeBase(charId, attributeName);
|
|
||||||
}),
|
|
||||||
skillMod: memoize(preventLoop(function(charId, skillName){
|
|
||||||
var skill = Characters.calculate.getField(charId, skillName);
|
|
||||||
if (!skill) return;
|
|
||||||
//get the final value of the ability score
|
|
||||||
var ability = Characters.calculate.attributeValue(charId, skill.ability);
|
|
||||||
|
|
||||||
//base modifier
|
|
||||||
var mod = +getMod(ability);
|
|
||||||
|
|
||||||
//multiply proficiency bonus by largest value in proficiency array
|
|
||||||
var prof = Characters.calculate.proficiency(charId, skillName);
|
|
||||||
|
|
||||||
//add multiplied proficiency bonus to modifier
|
|
||||||
mod += prof * Characters.calculate.attributeValue(charId, "proficiencyBonus");
|
|
||||||
|
|
||||||
//apply all effects
|
|
||||||
var value;
|
|
||||||
var add = 0;
|
|
||||||
var mul = 1;
|
|
||||||
var min = Number.NEGATIVE_INFINITY;
|
|
||||||
var max = Number.POSITIVE_INFINITY;
|
|
||||||
|
|
||||||
Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: skillName,
|
|
||||||
enabled: true,
|
|
||||||
operation: {$in: ["base", "add", "mul", "min", "max"]},
|
|
||||||
}).forEach(function(effect) {
|
|
||||||
value = evaluateEffect(charId, effect);
|
|
||||||
if (effect.operation === "add"){
|
|
||||||
add += value;
|
|
||||||
} else if (effect.operation === "mul"){
|
|
||||||
mul *= value;
|
|
||||||
} else if (effect.operation === "min"){
|
|
||||||
if (value > min) min = value;
|
|
||||||
} else if (effect.operation === "max"){
|
|
||||||
if (value < max) max = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var result = (mod + add) * mul;
|
|
||||||
if (result < min) result = min;
|
|
||||||
if (result > max) result = max;
|
|
||||||
|
|
||||||
return Math.floor(result);
|
|
||||||
})),
|
|
||||||
proficiency: memoize(function(charId, skillName){
|
|
||||||
//return largest value in proficiency array
|
|
||||||
var prof = Proficiencies.findOne(
|
|
||||||
{charId: charId, name: skillName, enabled: true},
|
|
||||||
{sort: {value: -1}}
|
|
||||||
);
|
|
||||||
return prof && prof.value || 0;
|
|
||||||
}),
|
|
||||||
passiveSkill: memoize(function(charId, skillName){
|
|
||||||
var mod = +Characters.calculate.skillMod(charId, skillName);
|
|
||||||
var value = 10 + mod;
|
|
||||||
Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "passiveAdd"}
|
|
||||||
).forEach(function(effect){
|
|
||||||
value += evaluateEffect(charId, effect);
|
|
||||||
});
|
|
||||||
var advantage = Characters.calculate.advantage(charId, skillName);
|
|
||||||
value += 5 * advantage;
|
|
||||||
return Math.floor(value);
|
|
||||||
}),
|
|
||||||
advantage: memoize(function(charId, skillName){
|
|
||||||
var advantage = Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "advantage"}
|
|
||||||
).count();
|
|
||||||
var disadvantage = Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "disadvantage"}
|
|
||||||
).count();
|
|
||||||
if (advantage && !disadvantage) return 1;
|
|
||||||
if (disadvantage && !advantage) return -1;
|
|
||||||
return 0;
|
|
||||||
}),
|
|
||||||
abilityMod: function(charId, attribute){
|
|
||||||
return getMod(
|
|
||||||
Characters.calculate.attributeValue(charId, attribute)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
passiveAbility: function(charId, attribute){
|
|
||||||
var mod = +getMod(Characters.calculate.attributeValue(charId, attribute));
|
|
||||||
return 10 + mod;
|
|
||||||
},
|
|
||||||
xpLevel: function(charId){
|
xpLevel: function(charId){
|
||||||
var xp = Characters.calculate.experience(charId);
|
var xp = Characters.calculate.experience(charId);
|
||||||
for (var i = 0; i < 19; i++){
|
for (var i = 0; i < 19; i++){
|
||||||
@@ -438,116 +66,28 @@ Characters.calculate = {
|
|||||||
if (xp > 355000) return 20;
|
if (xp > 355000) return 20;
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
level: memoize(function(charId){
|
|
||||||
var level = 0;
|
|
||||||
Classes.find({charId: charId}).forEach(function(cls){
|
|
||||||
level += cls.level;
|
|
||||||
});
|
|
||||||
return level;
|
|
||||||
}),
|
|
||||||
experience: memoize(function(charId){
|
|
||||||
var xp = 0;
|
|
||||||
Experiences.find(
|
|
||||||
{charId: charId},
|
|
||||||
{fields: {value: 1}}
|
|
||||||
).forEach(function(e){
|
|
||||||
xp += e.value;
|
|
||||||
});
|
|
||||||
return xp;
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var deprecated = function() {
|
|
||||||
//var err = new Error("this function has been deprecated");
|
|
||||||
var name = "";
|
|
||||||
if (Template.instance()){
|
|
||||||
name = Template.instance().view.name;
|
|
||||||
}
|
|
||||||
var logString = "this function has been deprecated \n";
|
|
||||||
if (name){
|
|
||||||
logString += "View: " + name + "\n\n";
|
|
||||||
}
|
|
||||||
//logString += err.stack + "\n\n---------------------\n\n";
|
|
||||||
console.log(logString);
|
|
||||||
};
|
|
||||||
|
|
||||||
//functions and calculated values.
|
|
||||||
//These functions can only rely on this._id since no other
|
|
||||||
//field is likely to be attached to all returned characters
|
|
||||||
Characters.helpers({
|
|
||||||
//returns the value stored in the field requested
|
|
||||||
//will set up dependencies on just that field
|
|
||||||
getField : function(fieldName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.getField(this._id, fieldName);
|
|
||||||
},
|
|
||||||
//returns the value of a field
|
|
||||||
fieldValue : function(fieldName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.fieldValue(this._id, fieldName);
|
|
||||||
},
|
|
||||||
attributeValue: function(attributeName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.attributeValue(this._id, attributeName);
|
|
||||||
},
|
|
||||||
attributeBase: function(attributeName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.attributeBase(this._id, attributeName);
|
|
||||||
},
|
|
||||||
skillMod: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.skillMod(this._id, skillName);
|
|
||||||
},
|
|
||||||
proficiency: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.proficiency(this._id, skillName);
|
|
||||||
},
|
|
||||||
passiveSkill: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.passiveSkill(this._id, skillName);
|
|
||||||
},
|
|
||||||
advantage: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.advantage(this._id, skillName);
|
|
||||||
},
|
|
||||||
abilityMod: function(attribute){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.abilityMod(this._id, attribute);
|
|
||||||
},
|
|
||||||
passiveAbility: function(attribute){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.passiveAbility(this._id, attribute);
|
|
||||||
},
|
|
||||||
xpLevel: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.xpLevel(this._id);
|
|
||||||
},
|
|
||||||
level: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.level(this._id);
|
|
||||||
},
|
|
||||||
experience: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.experience(this._id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//clean up all data related to that character before removing it
|
//clean up all data related to that character before removing it
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer){
|
||||||
Characters.after.remove(function(userId, character) {
|
Characters.after.remove(function(userId, character) {
|
||||||
Actions .remove({charId: character._id});
|
let charId = character._id;
|
||||||
Attacks .remove({charId: character._id});
|
Actions .remove({charId});
|
||||||
Buffs .remove({charId: character._id});
|
Attacks .remove({charId});
|
||||||
Classes .remove({charId: character._id});
|
Attributes .remove({charId});
|
||||||
CustomBuffs .remove({charId: character._id});
|
Buffs .remove({charId});
|
||||||
Effects .remove({charId: character._id});
|
Classes .remove({charId});
|
||||||
Experiences .remove({charId: character._id});
|
CustomBuffs .remove({charId});
|
||||||
Features .remove({charId: character._id});
|
DamageMultipliers.remove({charId});
|
||||||
Notes .remove({charId: character._id});
|
Effects .remove({charId});
|
||||||
Proficiencies .remove({charId: character._id});
|
Experiences .remove({charId});
|
||||||
SpellLists .remove({charId: character._id});
|
Features .remove({charId});
|
||||||
Items .remove({charId: character._id});
|
Notes .remove({charId});
|
||||||
Containers .remove({charId: character._id});
|
Proficiencies .remove({charId});
|
||||||
|
Skills .remove({charId});
|
||||||
|
SpellLists .remove({charId});
|
||||||
|
Items .remove({charId});
|
||||||
|
Containers .remove({charId});
|
||||||
});
|
});
|
||||||
Characters.after.update(function(userId, doc, fieldNames, modifier, options) {
|
Characters.after.update(function(userId, doc, fieldNames, modifier, options) {
|
||||||
if (_.contains(fieldNames, "name")){
|
if (_.contains(fieldNames, "name")){
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ Schemas.Skill = new SimpleSchema({
|
|||||||
allowedValues: [
|
allowedValues: [
|
||||||
"skill",
|
"skill",
|
||||||
"save",
|
"save",
|
||||||
|
"stat",
|
||||||
"tool",
|
"tool",
|
||||||
"weapon",
|
"weapon",
|
||||||
"language",
|
"language",
|
||||||
|
"utility", //not displayed anywhere
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
|
|||||||
96
dataSources/rulesets/character5e.json
Normal file
96
dataSources/rulesets/character5e.json
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"attributes": {
|
||||||
|
"ability": [
|
||||||
|
"Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"
|
||||||
|
],
|
||||||
|
"stat": [
|
||||||
|
"Speed",
|
||||||
|
{"name": "Armor Class", "variableName": "armor"},
|
||||||
|
],
|
||||||
|
"hitDice": [
|
||||||
|
{"name": "d6 Hit Dice", "variableName": "d6HitDice"},
|
||||||
|
{"name": "d8 Hit Dice", "variableName": "d8HitDice"},
|
||||||
|
{"name": "d10 Hit Dice", "variableName": "d10HitDice"},
|
||||||
|
{"name": "d12 Hit Dice", "variableName": "d12HitDice"},
|
||||||
|
],
|
||||||
|
"healthBar": [
|
||||||
|
{"name": "Hit Points", "variableName": "hitPoints"},
|
||||||
|
{"name": "Temporary Hit Points", "variableName": "tempHitPoints"},
|
||||||
|
],
|
||||||
|
"resource": [
|
||||||
|
"Ki", "Rages",
|
||||||
|
{"name": "Sourcery Points", "variableName": "sorceryPoints"},
|
||||||
|
{"name": "Superiority Dice", "variableName": "superiorityDice"},
|
||||||
|
{"name": "Expertise Dice", "variableName": "expertiseDice"},
|
||||||
|
],
|
||||||
|
"spellSlot": [
|
||||||
|
{"name": "Level 1 Spell Slots", "variableName": "level1SpellSlots"},
|
||||||
|
{"name": "Level 2 Spell Slots", "variableName": "level2SpellSlots"},
|
||||||
|
{"name": "Level 3 Spell Slots", "variableName": "level3SpellSlots"},
|
||||||
|
{"name": "Level 4 Spell Slots", "variableName": "level4SpellSlots"},
|
||||||
|
{"name": "Level 5 Spell Slots", "variableName": "level5SpellSlots"},
|
||||||
|
{"name": "Level 6 Spell Slots", "variableName": "level6SpellSlots"},
|
||||||
|
{"name": "Level 7 Spell Slots", "variableName": "level7SpellSlots"},
|
||||||
|
{"name": "Level 8 Spell Slots", "variableName": "level8SpellSlots"},
|
||||||
|
{"name": "Level 9 Spell Slots", "variableName": "level9SpellSlots"},
|
||||||
|
],
|
||||||
|
"utility": [
|
||||||
|
{"name": "Carry Capacity Multiplier", "variableName": "carryMultiplier"},
|
||||||
|
{"name": "Rage Damage", "variableName": "rageDamage"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"skills": {
|
||||||
|
"skill": [
|
||||||
|
{"name": "Acrobatics", "variableName": "acrobatics", "ability": "dexterity"},
|
||||||
|
{"name": "Animal Handling", "variableName": "animalHandling", "ability": "wisdom"},
|
||||||
|
{"name": "Arcana", "variableName": "arcana", "ability": "intelligence"},
|
||||||
|
{"name": "Athletics", "variableName": "athletics", "ability": "strength"},
|
||||||
|
{"name": "Deception", "variableName": "deception", "ability": "charisma"},
|
||||||
|
{"name": "History", "variableName": "history", "ability": "intelligence"},
|
||||||
|
{"name": "Insight", "variableName": "insight", "ability": "wisdom"},
|
||||||
|
{"name": "Intimidation", "variableName": "intimidation", "ability": "charisma"},
|
||||||
|
{"name": "Investigation", "variableName": "investigation", "ability": "intelligence"},
|
||||||
|
{"name": "Medicine", "variableName": "medicine", "ability": "wisdom"},
|
||||||
|
{"name": "Nature", "variableName": "nature", "ability": "intelligence"},
|
||||||
|
{"name": "Perception", "variableName": "perception", "ability": "wisdom"},
|
||||||
|
{"name": "Performance", "variableName": "performance", "ability": "charisma"},
|
||||||
|
{"name": "Persuasion", "variableName": "persuasion", "ability": "charisma"},
|
||||||
|
{"name": "Religion", "variableName": "religion", "ability": "intelligence"},
|
||||||
|
{"name": "Sleight of Hand", "variableName": "sleightOfHand", "ability": "dexterity"},
|
||||||
|
{"name": "Stealth", "variableName": "stealth", "ability": "dexterity"},
|
||||||
|
{"name": "Survival", "variableName": "survival", "ability": "wisdom"},
|
||||||
|
],
|
||||||
|
"save": [
|
||||||
|
{"name": "Strength Save", "variableName": "strengthSave", "ability": "strength"},
|
||||||
|
{"name": "Dexterity Save", "variableName": "dexteritySave", "ability": "dexterity"},
|
||||||
|
{"name": "Constitution Save", "variableName": "constitutionSave", "ability": "constitution"},
|
||||||
|
{"name": "Intelligence Save", "variableName": "intelligenceSave", "ability": "intelligence"},
|
||||||
|
{"name": "Wisdom Save", "variableName": "wisdomSave", "ability": "wisdom"},
|
||||||
|
{"name": "Charisma Save", "variableName": "charismaSave", "ability": "charisma"},
|
||||||
|
],
|
||||||
|
"stat": [
|
||||||
|
{"name": "Proficiency Bonus", "variableName": "proficiencyBonus"},
|
||||||
|
{"name": "initiative", "variableName": "initiative"},
|
||||||
|
],
|
||||||
|
"utility": [
|
||||||
|
{"name": "Dexterity Armor", "variableName": "dexterityArmor", "ability": "dexterity"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"damageMultipliers": [
|
||||||
|
{"name": "Acid Multiplier", "variableName":"acidMultiplier"},
|
||||||
|
{"name": "Bludgeoning Multiplier", "variableName":"bludgeoningMultiplier"},
|
||||||
|
{"name": "Cold Multiplier", "variableName":"coldMultiplier"},
|
||||||
|
{"name": "Fire Multiplier", "variableName":"fireMultiplier"},
|
||||||
|
{"name": "Force Multiplier", "variableName":"forceMultiplier"},
|
||||||
|
{"name": "Lightning Multiplier", "variableName":"lightningMultiplier"},
|
||||||
|
{"name": "Necrotic Multiplier", "variableName":"necroticMultiplier"},
|
||||||
|
{"name": "Piercing Multiplier", "variableName":"piercingMultiplier"},
|
||||||
|
{"name": "Poison Multiplier", "variableName":"poisonMultiplier"},
|
||||||
|
{"name": "Psychic Multiplier", "variableName":"psychicMultiplier"},
|
||||||
|
{"name": "Radiant Multiplier", "variableName":"radiantMultiplier"},
|
||||||
|
{"name": "Slashing Multiplier", "variableName":"slashingMultiplier"},
|
||||||
|
{"name": "Thunder Multiplier", "variableName":"thunderMultiplier"},
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user