Fixed incorrect method call and added debug info to getField

This commit is contained in:
Thaum
2014-11-28 07:39:30 +00:00
parent cd6682a78a
commit e40b3b8f9d
2 changed files with 425 additions and 424 deletions

View File

@@ -2,250 +2,250 @@
Characters = new Meteor.Collection("characters"); Characters = new Meteor.Collection("characters");
Schemas.Character = new SimpleSchema({ Schemas.Character = new SimpleSchema({
//strings //strings
name: { type: String, defaultValue: "", trim: false}, name: { type: String, defaultValue: "", trim: false},
alignment: { type: String, defaultValue: "", trim: false}, alignment: { type: String, defaultValue: "", trim: false},
gender: { type: String, defaultValue: "", trim: false}, gender: { type: String, defaultValue: "", trim: false},
race: { type: String, defaultValue: "", trim: false}, race: { type: String, defaultValue: "", trim: false},
description:{ type: String, defaultValue: "", trim: false}, description:{ type: String, defaultValue: "", trim: false},
personality:{ type: String, defaultValue: "", trim: false}, personality:{ type: String, defaultValue: "", trim: false},
ideals: { type: String, defaultValue: "", trim: false}, ideals: { type: String, defaultValue: "", trim: false},
bonds: { type: String, defaultValue: "", trim: false}, bonds: { type: String, defaultValue: "", trim: false},
flaws: { type: String, defaultValue: "", trim: false}, flaws: { type: String, defaultValue: "", trim: false},
backstory: { type: String, defaultValue: "", trim: false}, backstory: { type: String, defaultValue: "", trim: false},
notes: { type: String, defaultValue: "", trim: false}, notes: { type: String, defaultValue: "", trim: false},
//attributes //attributes
//ability scores //ability scores
strength: {type: Schemas.Attribute}, strength: {type: Schemas.Attribute},
dexterity: {type: Schemas.Attribute}, dexterity: {type: Schemas.Attribute},
constitution: {type: Schemas.Attribute}, constitution: {type: Schemas.Attribute},
intelligence: {type: Schemas.Attribute}, intelligence: {type: Schemas.Attribute},
wisdom: {type: Schemas.Attribute}, wisdom: {type: Schemas.Attribute},
charisma: {type: Schemas.Attribute}, charisma: {type: Schemas.Attribute},
//stats //stats
hitPoints: {type: Schemas.Attribute}, hitPoints: {type: Schemas.Attribute},
"hitPoints.add": { "hitPoints.add": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [ defaultValue: [
{name: "Constitution modifier for each level", calculation: "level * constitutionMod"} {name: "Constitution modifier for each level", calculation: "level * constitutionMod"}
] ]
}, },
experience: {type: Schemas.Attribute}, experience: {type: Schemas.Attribute},
proficiencyBonus: {type: Schemas.Attribute}, proficiencyBonus: {type: Schemas.Attribute},
"proficiencyBonus.add": { "proficiencyBonus.add": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [ defaultValue: [
{name: "Proficiency bonus by level", calculation: "floor(level / 4.1) + 2"} {name: "Proficiency bonus by level", calculation: "floor(level / 4.1) + 2"}
] ]
}, },
speed: {type: Schemas.Attribute}, speed: {type: Schemas.Attribute},
weight: {type: Schemas.Attribute}, weight: {type: Schemas.Attribute},
age: {type: Schemas.Attribute}, age: {type: Schemas.Attribute},
ageRate: {type: Schemas.Attribute}, ageRate: {type: Schemas.Attribute},
armor: {type: Schemas.Attribute}, armor: {type: Schemas.Attribute},
"armor.add": { "armor.add": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [ defaultValue: [
{name: "Dexterity Modifier", calculation: "dexterityArmor"} {name: "Dexterity Modifier", calculation: "dexterityArmor"}
] ]
}, },
//resources //resources
level1SpellSlots: {type: Schemas.Attribute}, level1SpellSlots: {type: Schemas.Attribute},
level2SpellSlots: {type: Schemas.Attribute}, level2SpellSlots: {type: Schemas.Attribute},
level3SpellSlots: {type: Schemas.Attribute}, level3SpellSlots: {type: Schemas.Attribute},
level4SpellSlots: {type: Schemas.Attribute}, level4SpellSlots: {type: Schemas.Attribute},
level5SpellSlots: {type: Schemas.Attribute}, level5SpellSlots: {type: Schemas.Attribute},
level6SpellSlots: {type: Schemas.Attribute}, level6SpellSlots: {type: Schemas.Attribute},
level7SpellSlots: {type: Schemas.Attribute}, level7SpellSlots: {type: Schemas.Attribute},
level8SpellSlots: {type: Schemas.Attribute}, level8SpellSlots: {type: Schemas.Attribute},
level9SpellSlots: {type: Schemas.Attribute}, level9SpellSlots: {type: Schemas.Attribute},
ki: {type: Schemas.Attribute}, ki: {type: Schemas.Attribute},
sorceryPoints: {type: Schemas.Attribute}, sorceryPoints: {type: Schemas.Attribute},
rages: {type: Schemas.Attribute}, rages: {type: Schemas.Attribute},
superiorityDice: {type: Schemas.Attribute}, superiorityDice: {type: Schemas.Attribute},
expertiseDice: {type: Schemas.Attribute}, expertiseDice: {type: Schemas.Attribute},
//hit dice //hit dice
d6HitDice: {type: Schemas.Attribute}, d6HitDice: {type: Schemas.Attribute},
d8HitDice: {type: Schemas.Attribute}, d8HitDice: {type: Schemas.Attribute},
d10HitDice: {type: Schemas.Attribute}, d10HitDice: {type: Schemas.Attribute},
d12HitDice: {type: Schemas.Attribute}, d12HitDice: {type: Schemas.Attribute},
//vulnerabilities //vulnerabilities
acidMultiplier: {type: Schemas.Vulnerability}, acidMultiplier: {type: Schemas.Vulnerability},
bludgeoningMultiplier: {type: Schemas.Vulnerability}, bludgeoningMultiplier: {type: Schemas.Vulnerability},
coldMultiplier: {type: Schemas.Vulnerability}, coldMultiplier: {type: Schemas.Vulnerability},
fireMultiplier: {type: Schemas.Vulnerability}, fireMultiplier: {type: Schemas.Vulnerability},
forceMultiplier: {type: Schemas.Vulnerability}, forceMultiplier: {type: Schemas.Vulnerability},
lightningMultiplier: {type: Schemas.Vulnerability}, lightningMultiplier: {type: Schemas.Vulnerability},
necroticMultiplier: {type: Schemas.Vulnerability}, necroticMultiplier: {type: Schemas.Vulnerability},
piercingMultiplier: {type: Schemas.Vulnerability}, piercingMultiplier: {type: Schemas.Vulnerability},
poisonMultiplier: {type: Schemas.Vulnerability}, poisonMultiplier: {type: Schemas.Vulnerability},
psychicMultiplier: {type: Schemas.Vulnerability}, psychicMultiplier: {type: Schemas.Vulnerability},
radiantMultiplier: {type: Schemas.Vulnerability}, radiantMultiplier: {type: Schemas.Vulnerability},
slashingMultiplier: {type: Schemas.Vulnerability}, slashingMultiplier: {type: Schemas.Vulnerability},
thunderMultiplier: {type: Schemas.Vulnerability}, thunderMultiplier: {type: Schemas.Vulnerability},
//skills //skills
//saves //saves
strengthSave: {type: Schemas.Skill}, strengthSave: {type: Schemas.Skill},
"strengthSave.ability": { type: String, defaultValue: "strength" }, "strengthSave.ability": { type: String, defaultValue: "strength" },
dexteritySave: {type: Schemas.Skill}, dexteritySave: {type: Schemas.Skill},
"dexteritySave.ability": { type: String, defaultValue: "dexterity" }, "dexteritySave.ability": { type: String, defaultValue: "dexterity" },
constitutionSave:{type: Schemas.Skill}, constitutionSave:{type: Schemas.Skill},
"constitutionSave.ability": { type: String, defaultValue: "constitution" }, "constitutionSave.ability": { type: String, defaultValue: "constitution" },
intelligenceSave:{type: Schemas.Skill}, intelligenceSave:{type: Schemas.Skill},
"intelligenceSave.ability": { type: String, defaultValue: "intelligence" }, "intelligenceSave.ability": { type: String, defaultValue: "intelligence" },
wisdomSave: {type: Schemas.Skill}, wisdomSave: {type: Schemas.Skill},
"wisdomSave.ability": { type: String, defaultValue: "wisdom" }, "wisdomSave.ability": { type: String, defaultValue: "wisdom" },
charismaSave: {type: Schemas.Skill}, charismaSave: {type: Schemas.Skill},
"charismaSave.ability": { type: String, defaultValue: "charisma" }, "charismaSave.ability": { type: String, defaultValue: "charisma" },
//skill skills //skill skills
acrobatics: {type: Schemas.Skill}, acrobatics: {type: Schemas.Skill},
"acrobatics.ability": { type: String, defaultValue: "dexterity" }, "acrobatics.ability": { type: String, defaultValue: "dexterity" },
animalHandling: {type: Schemas.Skill}, animalHandling: {type: Schemas.Skill},
"animalHandling.ability": { type: String, defaultValue: "wisdom" }, "animalHandling.ability": { type: String, defaultValue: "wisdom" },
arcana: {type: Schemas.Skill}, arcana: {type: Schemas.Skill},
"arcana.ability": { type: String, defaultValue: "intelligence" }, "arcana.ability": { type: String, defaultValue: "intelligence" },
athletics: {type: Schemas.Skill}, athletics: {type: Schemas.Skill},
"athletics.ability": { type: String, defaultValue: "strength" }, "athletics.ability": { type: String, defaultValue: "strength" },
deception: {type: Schemas.Skill}, deception: {type: Schemas.Skill},
"deception.ability": { type: String, defaultValue: "charisma" }, "deception.ability": { type: String, defaultValue: "charisma" },
history: {type: Schemas.Skill}, history: {type: Schemas.Skill},
"history.ability": { type: String, defaultValue: "intelligence" }, "history.ability": { type: String, defaultValue: "intelligence" },
insight: {type: Schemas.Skill}, insight: {type: Schemas.Skill},
"insight.ability": { type: String, defaultValue: "wisdom" }, "insight.ability": { type: String, defaultValue: "wisdom" },
intimidation: {type: Schemas.Skill}, intimidation: {type: Schemas.Skill},
"intimidation.ability": { type: String, defaultValue: "charisma" }, "intimidation.ability": { type: String, defaultValue: "charisma" },
investigation: {type: Schemas.Skill}, investigation: {type: Schemas.Skill},
"investigation.ability": { type: String, defaultValue: "intelligence" }, "investigation.ability": { type: String, defaultValue: "intelligence" },
medicine: {type: Schemas.Skill}, medicine: {type: Schemas.Skill},
"medicine.ability": { type: String, defaultValue: "wisdom" }, "medicine.ability": { type: String, defaultValue: "wisdom" },
nature: {type: Schemas.Skill}, nature: {type: Schemas.Skill},
"nature.ability": { type: String, defaultValue: "intelligence" }, "nature.ability": { type: String, defaultValue: "intelligence" },
perception: {type: Schemas.Skill}, perception: {type: Schemas.Skill},
"perception.ability": { type: String, defaultValue: "wisdom" }, "perception.ability": { type: String, defaultValue: "wisdom" },
performance: {type: Schemas.Skill}, performance: {type: Schemas.Skill},
"performance.ability": { type: String, defaultValue: "charisma" }, "performance.ability": { type: String, defaultValue: "charisma" },
persuasion: {type: Schemas.Skill}, persuasion: {type: Schemas.Skill},
"persuasion.ability": { type: String, defaultValue: "charisma" }, "persuasion.ability": { type: String, defaultValue: "charisma" },
religion: {type: Schemas.Skill}, religion: {type: Schemas.Skill},
"religion.ability": { type: String, defaultValue: "intelligence" }, "religion.ability": { type: String, defaultValue: "intelligence" },
sleightOfHand: {type: Schemas.Skill}, sleightOfHand: {type: Schemas.Skill},
"sleightOfHand.ability": { type: String, defaultValue: "dexterity" }, "sleightOfHand.ability": { type: String, defaultValue: "dexterity" },
stealth: {type: Schemas.Skill}, stealth: {type: Schemas.Skill},
"stealth.ability": { type: String, defaultValue: "dexterity" }, "stealth.ability": { type: String, defaultValue: "dexterity" },
survival: {type: Schemas.Skill}, survival: {type: Schemas.Skill},
"survival.ability": { type: String, defaultValue: "wisdom" }, "survival.ability": { type: String, defaultValue: "wisdom" },
//Mechanical Skills //Mechanical Skills
initiative: {type: Schemas.Skill}, initiative: {type: Schemas.Skill},
"initiative.ability": { type: String, defaultValue: "dexterity" }, "initiative.ability": { type: String, defaultValue: "dexterity" },
strengthAttack: {type: Schemas.Skill}, strengthAttack: {type: Schemas.Skill},
"strengthAttack.ability": {type: String,defaultValue: "strength"}, "strengthAttack.ability": {type: String,defaultValue: "strength"},
"strengthAttack.proficiency": { "strengthAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
dexterityAttack: {type: Schemas.Skill}, dexterityAttack: {type: Schemas.Skill},
"dexterityAttack.ability": { type: String, defaultValue: "dexterity" }, "dexterityAttack.ability": { type: String, defaultValue: "dexterity" },
"dexterityAttack.proficiency": { "dexterityAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
constitutionAttack: {type: Schemas.Skill}, constitutionAttack: {type: Schemas.Skill},
"constitutionAttack.ability":{ type: String, defaultValue: "constitution" }, "constitutionAttack.ability":{ type: String, defaultValue: "constitution" },
"constitutionAttack.proficiency": { "constitutionAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
intelligenceAttack: {type: Schemas.Skill}, intelligenceAttack: {type: Schemas.Skill},
"intelligenceAttack.ability":{ type: String, defaultValue: "intelligence" }, "intelligenceAttack.ability":{ type: String, defaultValue: "intelligence" },
"intelligenceAttack.proficiency": { "intelligenceAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
wisdomAttack: {type: Schemas.Skill}, wisdomAttack: {type: Schemas.Skill},
"wisdomAttack.ability": { type: String, defaultValue: "wisdom" }, "wisdomAttack.ability": { type: String, defaultValue: "wisdom" },
"wisdomAttack.proficiency": { "wisdomAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
charismaAttack: {type: Schemas.Skill}, charismaAttack: {type: Schemas.Skill},
"charismaAttack.ability": { type: String, defaultValue: "charisma" }, "charismaAttack.ability": { type: String, defaultValue: "charisma" },
"charismaAttack.proficiency": { "charismaAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
rangedAttack: {type: Schemas.Skill}, rangedAttack: {type: Schemas.Skill},
"rangedAttack.ability": { type: String, defaultValue: "dexterity" }, "rangedAttack.ability": { type: String, defaultValue: "dexterity" },
"rangedAttack.proficiency": { "rangedAttack.proficiency": {
type: [Schemas.Effect], type: [Schemas.Effect],
defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}] defaultValue: [{_id: Random.id(),name: "Attack Proficiency",value: 1}]
}, },
dexterityArmor: {type: Schemas.Skill}, dexterityArmor: {type: Schemas.Skill},
"dexterityArmor.ability": { type: String, defaultValue: "dexterity" }, "dexterityArmor.ability": { type: String, defaultValue: "dexterity" },
//proficiencies //proficiencies
weaponsProficiencies: { weaponsProficiencies: {
type: [Schemas.Proficiency], type: [Schemas.Proficiency],
defaultValue: [] defaultValue: []
}, },
toolsProficiencies: { toolsProficiencies: {
type: [Schemas.Proficiency], type: [Schemas.Proficiency],
defaultValue: [] defaultValue: []
}, },
languages: { languages: {
type: [Schemas.Proficiency], type: [Schemas.Proficiency],
defaultValue: [] defaultValue: []
}, },
//mechanics //mechanics
features: { type: [Schemas.Feature], defaultValue: []}, features: { type: [Schemas.Feature], defaultValue: []},
deathSave: { type: Schemas.DeathSave }, deathSave: { type: Schemas.DeathSave },
time: { type: Number, min: 0, decimal: true, defaultValue: 0}, time: { type: Number, min: 0, decimal: true, defaultValue: 0},
initiativeOrder:{ type: Number, min: 0, max: 1, decimal: true, defaultValue: 0}, initiativeOrder:{ type: Number, min: 0, max: 1, decimal: true, defaultValue: 0},
expirations: { type: [Schemas.Expiration], defaultValue: []}, expirations: { type: [Schemas.Expiration], defaultValue: []},
spells: { type: [Schemas.Spell], defaultValue: []} spells: { type: [Schemas.Spell], defaultValue: []}
//TODO add permission stuff for owner, readers and writers //TODO add permission stuff for owner, readers and writers
}); });
Characters.attachSchema(Schemas.Character); Characters.attachSchema(Schemas.Character);
@@ -253,252 +253,253 @@ Characters.attachSchema(Schemas.Character);
//reactively remove expired effects //reactively remove expired effects
//this can be optimised a lot once clients can do projections //this can be optimised a lot once clients can do projections
Characters.find({},{fields: {time: 1, expirations: 1, features: 1}}).observe({ Characters.find({},{fields: {time: 1, expirations: 1, features: 1}}).observe({
changed: function(character){ changed: function(character){
var currentTime = character.time; var currentTime = character.time;
_.each(character.expirations, function(expiration){ _.each(character.expirations, function(expiration){
if(expiration.expiry <= currentTime){ if(expiration.expiry <= currentTime){
var charId = character._id; var charId = character._id;
var stat = expiration.stat; var stat = expiration.stat;
//remove expired effects //remove expired effects
_.each(expiration.effectIds, function(effectId){ _.each(expiration.effectIds, function(effectId){
pullEffect(charId, stat, effectId); pullEffect(charId, stat, effectId);
}); });
//disable expired features //disable expired features
_.each(expiration.featureIds, function(featureId){ _.each(expiration.featureIds, function(featureId){
var feature = Characters.findOne({_id: charId, "features._id": featureId}, {fields: {features: 1}}).features[0]; var feature = Characters.findOne({_id: charId, "features._id": featureId}, {fields: {features: 1}}).features[0];
feature.enabled = false; feature.enabled = false;
Characters.update({_id: charId, "features._id": featureId}, {$set: {"features.$": feature}}); Characters.update({_id: charId, "features._id": featureId}, {$set: {"features.$": feature}});
}); });
pullExpiry(charId, expiration._id); pullExpiry(charId, expiration._id);
} }
}); });
} }
}); });
var attributeBase = function(charId, attribute){ var attributeBase = function(charId, attribute){
var value = 0; var value = 0;
//add all values in add array //add all values in add array
_.each(attribute.add, function(effect){ _.each(attribute.add, function(effect){
value += evaluateEffect(charId, effect); value += evaluateEffect(charId, effect);
}); });
//multiply all values in mul array //multiply all values in mul array
_.each(attribute.mul, function(effect){ _.each(attribute.mul, function(effect){
value *= evaluateEffect(charId, effect); value *= evaluateEffect(charId, effect);
}); });
//largest min //largest min
_.each(attribute.min, function(effect){ _.each(attribute.min, function(effect){
var min = evaluateEffect(charId, effect); var min = evaluateEffect(charId, effect);
value = value > min? value : min; value = value > min? value : min;
}); });
//smallest max //smallest max
_.each(attribute.max, function(effect){ _.each(attribute.max, function(effect){
var max = evaluateEffect(charId, effect); var max = evaluateEffect(charId, effect);
value = value < max? value : max; value = value < max? value : max;
}); });
return value; return value;
} }
//functions and calculated values. //functions and calculated values.
//These functions can only rely on this._id since no other //These functions can only rely on this._id since no other
//field is likely to be attached to all returned characters //field is likely to be attached to all returned characters
Characters.helpers({ Characters.helpers({
//returns the value stored in the field requested //returns the value stored in the field requested
//will set up dependencies on just that field //will set up dependencies on just that field
getField : function(fieldName){ getField : function(fieldName){
var fieldSelector = {}; var fieldSelector = {};
fieldSelector[fieldName] = 1; fieldSelector[fieldName] = 1;
var char = Characters.findOne(this._id, {fields: fieldSelector}); var char = Characters.findOne(this._id, {fields: fieldSelector});
var field = char[fieldName]; var field = char[fieldName];
if(field === undefined){ if(!field){
console.log("no field ", fieldName, " exists for character ", char) throw new Meteor.Error("getField failed",
} "getField could not find field" + fieldName + " in character "+ char._id);
return field; }
}, return field;
//returns the value of a field },
fieldValue : function(fieldName){ //returns the value of a field
if(!Schemas.Character.schema(fieldName)){ fieldValue : function(fieldName){
console.log("Character's schema does not contain a field called: " + fieldName); if(!Schemas.Character.schema(fieldName)){
return; console.log("Character's schema does not contain a field called: " + fieldName);
} return;
//duck typing to get the right value function }
//.proficiency implies skill //duck typing to get the right value function
if (Schemas.Character.schema(fieldName + ".proficiency")){ //.proficiency implies skill
return this.skillMod(fieldName); if (Schemas.Character.schema(fieldName + ".proficiency")){
} return this.skillMod(fieldName);
//base without proficiency implies attribute }
if (Schemas.Character.schema(fieldName + ".base")){ //base without proficiency implies attribute
return this.attributeValue(fieldName); if (Schemas.Character.schema(fieldName + ".base")){
} return this.attributeValue(fieldName);
//fall back to just returning the field itself }
return this.getField(fieldName); //fall back to just returning the field itself
}, return this.getField(fieldName);
},
attributeValue: (function(){ attributeValue: (function(){
//store a private array of attributes we've visited without returning //store a private array of attributes we've visited without returning
//if we try to visit the same attribute twice before resolving its value //if we try to visit the same attribute twice before resolving its value
//we are in a dependency loop and need to GTFO //we are in a dependency loop and need to GTFO
var visitedAttributes = []; var visitedAttributes = [];
return function(attributeName){ return function(attributeName){
check(attributeName, String); check(attributeName, String);
//we're still evaluating this attribute, must be in a loop //we're still evaluating this attribute, must be in a loop
if(_.contains(visitedAttributes, attributeName)) { if(_.contains(visitedAttributes, attributeName)) {
console.log("dependency loop detected"); console.log("dependency loop detected");
return NaN; return NaN;
} }
//push this attribute to the list of visited attributes //push this attribute to the list of visited attributes
//we can't visit it again unless it returns first //we can't visit it again unless it returns first
visitedAttributes.push(attributeName); visitedAttributes.push(attributeName);
var charId = this._id; var charId = this._id;
var attribute = this.getField(attributeName); var attribute = this.getField(attributeName);
//base value //base value
var value = attributeBase(charId, attribute); var value = attributeBase(charId, attribute);
value += attribute.adjustment; value += attribute.adjustment;
//this attribute returns, pull it from the array, we may visit it again safely //this attribute returns, pull it from the array, we may visit it again safely
visitedAttributes = _.without(visitedAttributes, attributeName); visitedAttributes = _.without(visitedAttributes, attributeName);
return value; return value;
} }
})(), })(),
attributeBase: (function(){ attributeBase: (function(){
//store a private array of attributes we've visited without returning //store a private array of attributes we've visited without returning
//if we try to visit the same attribute twice before resolving its value //if we try to visit the same attribute twice before resolving its value
//we are in a dependency loop and need to GTFO //we are in a dependency loop and need to GTFO
var visitedAttributes = []; var visitedAttributes = [];
return function(attributeName){ return function(attributeName){
check(attributeName, String); check(attributeName, String);
//we're still evaluating this attribute, must be in a loop //we're still evaluating this attribute, must be in a loop
if(_.contains(visitedAttributes, attributeName)) { if(_.contains(visitedAttributes, attributeName)) {
console.log("dependency loop detected"); console.log("dependency loop detected");
return NaN; return NaN;
} }
//push this attribute to the list of visited attributes //push this attribute to the list of visited attributes
//we can't visit it again unless it returns first //we can't visit it again unless it returns first
visitedAttributes.push(attributeName); visitedAttributes.push(attributeName);
var charId = this._id; var charId = this._id;
var attribute = this.getField(attributeName); var attribute = this.getField(attributeName);
//base value //base value
var value = attributeBase(charId, attribute); var value = attributeBase(charId, attribute);
//this attribute returns, pull it from the array, we may visit it again safely //this attribute returns, pull it from the array, we may visit it again safely
visitedAttributes = _.without(visitedAttributes, attributeName); visitedAttributes = _.without(visitedAttributes, attributeName);
return value; return value;
} }
})(), })(),
skillMod: (function(){ skillMod: (function(){
//store a private array of skills we've visited without returning //store a private array of skills we've visited without returning
//if we try to visit the same skill twice before resolving its value //if we try to visit the same skill twice before resolving its value
//we are in a dependency loop and need to GTFO //we are in a dependency loop and need to GTFO
var visitedSkills = []; var visitedSkills = [];
return function(skillName){ return function(skillName){
check(skillName, String); check(skillName, String);
//we're still evaluating this attribute, must be in a loop //we're still evaluating this attribute, must be in a loop
if(_.contains(visitedSkills, skillName)) { if(_.contains(visitedSkills, skillName)) {
console.log("dependency loop detected"); console.log("dependency loop detected");
return NaN; return NaN;
} }
//push this skill to the list of visited skills //push this skill to the list of visited skills
//we can't visit it again unless it returns first //we can't visit it again unless it returns first
visitedSkills.push(skillName); visitedSkills.push(skillName);
var charId = this._id; var charId = this._id;
skill = this.getField(skillName); skill = this.getField(skillName);
//get the final value of the ability score //get the final value of the ability score
var ability = this.attributeValue(skill.ability); var ability = this.attributeValue(skill.ability);
//base modifier //base modifier
var mod = +getMod(ability) var mod = +getMod(ability)
//multiply proficiency bonus by largest value in proficiency array //multiply proficiency bonus by largest value in proficiency array
var prof = this.proficiency(skill); var prof = this.proficiency(skill);
//add multiplied proficiency bonus to modifier //add multiplied proficiency bonus to modifier
mod += prof * this.attributeValue("proficiencyBonus"); mod += prof * this.attributeValue("proficiencyBonus");
//add all values in add array //add all values in add array
_.each(skill.add, function(effect){ _.each(skill.add, function(effect){
mod += evaluateEffect(charId, effect); mod += evaluateEffect(charId, effect);
}); });
//multiply all values in mul array //multiply all values in mul array
_.each(skill.mul, function(effect){ _.each(skill.mul, function(effect){
mod *= evaluateEffect(charId, effect); mod *= evaluateEffect(charId, effect);
}); });
//largest min //largest min
_.each(skill.min, function(effect){ _.each(skill.min, function(effect){
var min = evaluateEffect(charId, effect); var min = evaluateEffect(charId, effect);
mod = mod > min? mod : min; mod = mod > min? mod : min;
}); });
//smallest max //smallest max
_.each(skill.max, function(effect){ _.each(skill.max, function(effect){
var max = evaluateEffect(charId, effect); var max = evaluateEffect(charId, effect);
mod = mod < max? mod : max; mod = mod < max? mod : max;
}); });
//done traversing the tree, this skill returns, pull it from the array //done traversing the tree, this skill returns, pull it from the array
visitedSkills = _.without(visitedSkills, skillName); visitedSkills = _.without(visitedSkills, skillName);
return signedString(mod); return signedString(mod);
} }
})(), })(),
proficiency: function(skill){ proficiency: function(skill){
if (_.isString(skill)){ if (_.isString(skill)){
skill = this.getField(skill); skill = this.getField(skill);
} }
var charId = this._id; var charId = this._id;
//return largest value in proficiency array //return largest value in proficiency array
var prof = 0; var prof = 0;
_.each(skill.proficiency, function(effect){ _.each(skill.proficiency, function(effect){
var newProf = evaluateEffect(charId, effect); var newProf = evaluateEffect(charId, effect);
if (newProf > prof){ if (newProf > prof){
prof = newProf; prof = newProf;
} }
}); });
return prof; return prof;
}, },
passiveSkill: function(skillName){ passiveSkill: function(skillName){
if (_.isString(skillName)){ if (_.isString(skillName)){
var skill = this.getField(skillName); var skill = this.getField(skillName);
} }
var charId = this._id var charId = this._id
var mod = +this.skillMod(skillName); var mod = +this.skillMod(skillName);
var value = 10 + mod; var value = 10 + mod;
_.each(skill.passiveAdd, function(effect){ _.each(skill.passiveAdd, function(effect){
value += evaluateEffect(charId, effect); value += evaluateEffect(charId, effect);
}); });
return value; return value;
//TODO decide whether (dis)advantage gives (-)+5 to passive checks //TODO decide whether (dis)advantage gives (-)+5 to passive checks
}, },
abilityMod: function(attribute){ abilityMod: function(attribute){
return signedString(getMod(this.attributeValue(attribute))); return signedString(getMod(this.attributeValue(attribute)));
}, },
passiveAbility: function(attribute){ passiveAbility: function(attribute){
var mod = +getMod(this.attributeValue(attribute)); var mod = +getMod(this.attributeValue(attribute));
return 10 + mod; return 10 + mod;
}, },
level: function(){ level: function(){
var xp = this.attributeValue("experience"); var xp = this.attributeValue("experience");
var xpTable = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, var xpTable = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000,
85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000,
305000, 355000]; 305000, 355000];
for(var i = 0; i < 19; i++){ for(var i = 0; i < 19; i++){
if(xp < xpTable[i]){ if(xp < xpTable[i]){
return i; return i;
} }
}; };
if(xp > 355000) return 20; if(xp > 355000) return 20;
return 0; return 0;
} }
}); });

View File

@@ -68,13 +68,13 @@
</template> </template>
<template name="spellSlots"> <template name="spellSlots">
{{attributevalue "level1SpellSlots"}} {{attributeValue "level1SpellSlots"}}
{{attributevalue "level2SpellSlots"}} {{attributeValue "level2SpellSlots"}}
{{attributevalue "level3SpellSlots"}} {{attributeValue "level3SpellSlots"}}
{{attributevalue "level4SpellSlots"}} {{attributeValue "level4SpellSlots"}}
{{attributevalue "level5SpellSlots"}} {{attributeValue "level5SpellSlots"}}
{{attributevalue "level6SpellSlots"}} {{attributeValue "level6SpellSlots"}}
{{attributevalue "level7SpellSlots"}} {{attributeValue "level7SpellSlots"}}
{{attributevalue "level8SpellSlots"}} {{attributeValue "level8SpellSlots"}}
{{attributevalue "level9SpellSlots"}} {{attributeValue "level9SpellSlots"}}
</template> </template>