Added effect math tests
This commit is contained in:
2
.codio
2
.codio
@@ -3,7 +3,7 @@
|
||||
|
||||
// Run button configuration
|
||||
"commands": {
|
||||
"Run Meteor": "ROOT_URL=http://period-sheriff-3000.codio.io\ncd rpg-docs \n meteor run"
|
||||
"Run Meteor": "cd rpg-docs \n ROOT_URL=http://period-sheriff.codio.io:3000 meteor"
|
||||
},
|
||||
|
||||
// Preview button configuration
|
||||
|
||||
@@ -257,87 +257,46 @@ Characters.helpers({
|
||||
return value;
|
||||
},
|
||||
|
||||
attributeBase: (function(){
|
||||
//store a private array of attributes we've visited without returning
|
||||
//if we try to visit the same attribute twice before resolving its value
|
||||
//we are in a dependency loop and need to GTFO
|
||||
var visitedAttributes = [];
|
||||
return function(attributeName){
|
||||
check(attributeName, String);
|
||||
//we're still evaluating this attribute, must be in a loop
|
||||
if(_.contains(visitedAttributes, attributeName)) {
|
||||
console.log("dependency loop detected");
|
||||
return NaN;
|
||||
}
|
||||
//push this attribute to the list of visited attributes
|
||||
//we can't visit it again unless it returns first
|
||||
visitedAttributes.push(attributeName);
|
||||
try{
|
||||
var charId = this._id;
|
||||
//base value
|
||||
var value = attributeBase(charId, attributeName);
|
||||
}finally{
|
||||
//this attribute returns or fails, pull it from the array, we may visit it again safely
|
||||
visitedAttributes = _.without(visitedAttributes, attributeName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
})(),
|
||||
attributeBase: preventLoop(function(attributeName){
|
||||
var charId = this._id;
|
||||
//base value
|
||||
return attributeBase(charId, attributeName);
|
||||
}),
|
||||
|
||||
skillMod: (function(){
|
||||
//store a private array of skills we've visited without returning
|
||||
//if we try to visit the same skill twice before resolving its value
|
||||
//we are in a dependency loop and need to GTFO
|
||||
var visitedSkills = [];
|
||||
return function(skillName){
|
||||
check(skillName, String);
|
||||
//we're still evaluating this attribute, must be in a loop
|
||||
if(_.contains(visitedSkills, skillName)) {
|
||||
console.log("dependency loop detected");
|
||||
return NaN;
|
||||
}
|
||||
//push this skill to the list of visited skills
|
||||
//we can't visit it again unless it returns first
|
||||
visitedSkills.push(skillName);
|
||||
try{
|
||||
var charId = this._id;
|
||||
skill = this.getField(skillName);
|
||||
//get the final value of the ability score
|
||||
var ability = this.attributeValue(skill.ability);
|
||||
skillMod: preventLoop(function(skillName){
|
||||
var charId = this._id;
|
||||
var skill = this.getField(skillName);
|
||||
//get the final value of the ability score
|
||||
var ability = this.attributeValue(skill.ability);
|
||||
|
||||
//base modifier
|
||||
var mod = +getMod(ability)
|
||||
//base modifier
|
||||
var mod = +getMod(ability)
|
||||
|
||||
//multiply proficiency bonus by largest value in proficiency array
|
||||
var prof = this.proficiency(skillName);
|
||||
//multiply proficiency bonus by largest value in proficiency array
|
||||
var prof = this.proficiency(skillName);
|
||||
|
||||
//add multiplied proficiency bonus to modifier
|
||||
mod += prof * this.attributeValue("proficiencyBonus");
|
||||
Effects.find({charId: charId, stat: skillName, enabled: true}).forEach(function(effect){
|
||||
switch(effect.operation) {
|
||||
case "add":
|
||||
mod += evaluateEffect(charId, effect);
|
||||
break;
|
||||
case "mul":
|
||||
mod *= evaluateEffect(charId, effect);
|
||||
break;
|
||||
case "min":
|
||||
var min = evaluateEffect(charId, effect);
|
||||
mod = mod > min? mod : min;
|
||||
break;
|
||||
case "max":
|
||||
var max = evaluateEffect(charId, effect);
|
||||
mod = mod < max? mod : max;
|
||||
break;
|
||||
}
|
||||
});
|
||||
} finally{
|
||||
//this skill returns or fails, pull it from the array
|
||||
visitedSkills = _.without(visitedSkills, skillName);
|
||||
}
|
||||
return signedString(mod);
|
||||
}
|
||||
})(),
|
||||
//add multiplied proficiency bonus to modifier
|
||||
mod += prof * this.attributeValue("proficiencyBonus");
|
||||
|
||||
//apply all effects
|
||||
var rawEffects = Effects.find({charId: charId, stat: skillName, enabled: true}).fetch();
|
||||
var effects = _.groupBy(rawEffects, "operation");
|
||||
_.forEach(effects.add, function(effect){
|
||||
mod += evaluateEffect(charId, effect);
|
||||
});
|
||||
_.forEach(effects.mul, function(effect){
|
||||
mod *= evaluateEffect(charId, effect);
|
||||
});
|
||||
_.forEach(effects.min, function(effect){
|
||||
var min = evaluateEffect(charId, effect);
|
||||
mod = mod > min? mod : min;
|
||||
});
|
||||
_.forEach(effects.max, function(effect){
|
||||
var max = evaluateEffect(charId, effect);
|
||||
mod = mod < max? mod : max;
|
||||
});
|
||||
return signedString(mod);
|
||||
}),
|
||||
|
||||
proficiency: function(skillName){
|
||||
var charId = this._id;
|
||||
@@ -418,7 +377,7 @@ Characters.helpers({
|
||||
});
|
||||
|
||||
//clean up all data related to that character before removing it
|
||||
Characters.before.remove(function (userId, character) {
|
||||
Characters.after.remove(function (userId, character) {
|
||||
if(Meteor.isServer){
|
||||
Actions .remove({charId: character._id});
|
||||
Attacks .remove({charId: character._id});
|
||||
|
||||
27
rpg-docs/lib/functions/preventLoop.js
Normal file
27
rpg-docs/lib/functions/preventLoop.js
Normal file
@@ -0,0 +1,27 @@
|
||||
preventLoop = function(inputFunction){
|
||||
if(!_.isFunction(inputFunction)) throw new Meteor.Error("Not a function",
|
||||
"preventLoop can only take a function as an argument");
|
||||
//store a private array of arguments we have been given without returning
|
||||
//if we try to visit the same argument twice before resolving its value
|
||||
//we are in a dependency loop and need to GTFO
|
||||
var visitedArgs = [];
|
||||
return function(argument){
|
||||
var value;
|
||||
//we're still evaluating this attribute, must be in a loop
|
||||
if(_.contains(visitedArgs, argument)) {
|
||||
console.warn("dependency loop detected");
|
||||
return NaN;
|
||||
} else{
|
||||
//push this skill to the list of visited skills
|
||||
//we can't visit it again unless it returns first
|
||||
visitedArgs.push(argument);
|
||||
}
|
||||
try{
|
||||
value = inputFunction.call(this, argument);
|
||||
} finally{
|
||||
//this argument returns or fails, pull it from the array
|
||||
visitedArgs = _.without(visitedArgs, argument);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,26 @@
|
||||
var getEffect = function(charId, op, value){
|
||||
return getAttributeEffect(charId, "constitution", op, value);
|
||||
};
|
||||
|
||||
var getAttributeEffect = function(charId, attribute, op, value){
|
||||
return {
|
||||
charId: charId,
|
||||
type: "inate",
|
||||
stat: "constitution",
|
||||
stat: attribute,
|
||||
operation: op,
|
||||
value: value,
|
||||
parent: {
|
||||
id: charId,
|
||||
collection: "Characters"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getSkillEffect = function(charId, op, value){
|
||||
return {
|
||||
charId: charId,
|
||||
type: "inate",
|
||||
stat: "athletics",
|
||||
operation: op,
|
||||
value: value,
|
||||
parent: {
|
||||
@@ -15,81 +33,140 @@ var getEffect = function(charId, op, value){
|
||||
if (!(typeof MochaWeb === 'undefined')){
|
||||
MochaWeb.testOnly(function(){
|
||||
describe("Character", function(){
|
||||
Effects.remove({});
|
||||
Characters.remove({});
|
||||
var charId = Characters.insert({owner: "FWeGYyDY5jc4HuTh8"});
|
||||
var char = Characters.findOne(charId);
|
||||
var con = function(){return char.attributeValue("constitution")};
|
||||
var charId, char, con, ath, strMod;
|
||||
|
||||
beforeEach(function(){
|
||||
Effects.remove({});
|
||||
Characters.remove({});
|
||||
charId = Characters.insert({owner: "FWeGYyDY5jc4HuTh8"});
|
||||
char = Characters.findOne(charId);
|
||||
con = function(){return char.attributeValue("constitution")};
|
||||
ath = function(){return char.skillMod("athletics")};
|
||||
strMod = function(){return char.abilityMod("strength")};
|
||||
});
|
||||
|
||||
describe("effects", function(){
|
||||
describe("attributeValue", function(){
|
||||
|
||||
it("should be set to highest base", function(done){
|
||||
Effects.insert(getEffect(charId, "base", 10), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
Effects.insert(getEffect(charId, "base", 6), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
beforeEach(function(){
|
||||
Effects.remove({});
|
||||
});
|
||||
|
||||
it("should be set to highest base", function(){
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
Effects.insert(getEffect(charId, "base", 6));
|
||||
chai.assert.equal(10, con());
|
||||
done();
|
||||
});
|
||||
|
||||
it("should add", function(done){
|
||||
Effects.insert(getEffect(charId, "add", 2), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
it("should add", function(){
|
||||
Effects.insert(getEffect(charId, "add", 2));
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
chai.assert.equal(12, con());
|
||||
done();
|
||||
});
|
||||
|
||||
it("should multiply", function(done){
|
||||
Effects.insert(getEffect(charId, "mul", 2), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
it("should multiply after adding", function(){
|
||||
Effects.insert(getEffect(charId, "mul", 2));
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
Effects.insert(getEffect(charId, "add", 2));
|
||||
chai.assert.equal(24, con());
|
||||
done();
|
||||
});
|
||||
|
||||
it("should be at least highest min", function(done){
|
||||
Effects.insert(getEffect(charId, "min", 22), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
it("should be at least highest min", function(){
|
||||
Effects.insert(getEffect(charId, "min", 22));
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
Effects.insert(getEffect(charId, "add", 2));
|
||||
Effects.insert(getEffect(charId, "mul", 2));
|
||||
chai.assert.equal(con(), 24);
|
||||
Effects.insert(getEffect(charId, "min", 28), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
Effects.insert(getEffect(charId, "min", 28));
|
||||
chai.assert.equal(28, con());
|
||||
done();
|
||||
});
|
||||
|
||||
it("should be at most lowest max", function(done){
|
||||
Effects.insert(getEffect(charId, "max", 30), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
chai.assert.equal(28, con());
|
||||
Effects.insert(getEffect(charId, "max", 5), function(err, id){
|
||||
if(err) done(err);
|
||||
});
|
||||
it("should be at most lowest max after minning", function(){
|
||||
Effects.insert(getEffect(charId, "max", 5));
|
||||
Effects.insert(getEffect(charId, "min", 22));
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
Effects.insert(getEffect(charId, "add", 2));
|
||||
Effects.insert(getEffect(charId, "mul", 2));
|
||||
chai.assert.equal(5, con());
|
||||
Effects.insert(getEffect(charId, "max", 6));
|
||||
chai.assert.equal(5, con());
|
||||
done();
|
||||
});
|
||||
|
||||
it("should respect adjustment", function(done){
|
||||
Characters.update(charId, {$set: {"constitution.adjustment": -2}}, function(err, num){
|
||||
if(err) done(err);
|
||||
})
|
||||
it("should respect adjustment", function(){
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
Effects.insert(getEffect(charId, "add", 2));
|
||||
Effects.insert(getEffect(charId, "mul", 2));
|
||||
Effects.insert(getEffect(charId, "min", 28));
|
||||
Effects.insert(getEffect(charId, "max", 5));
|
||||
Characters.update(charId, {$set: {"constitution.adjustment": -2}})
|
||||
chai.assert.equal(3, con());
|
||||
var conBase = char.attributeBase("constitution");
|
||||
chai.assert.equal(5, conBase)
|
||||
done();
|
||||
});
|
||||
|
||||
it("should be removed when the character is deleted", function(){
|
||||
Effects.insert(getEffect(charId, "base", 10));
|
||||
var count = Effects.find({charId: charId}).count();
|
||||
chai.assert.equal(count, 1);
|
||||
Characters.remove(charId);
|
||||
var count = Effects.find({charId: charId}).count();
|
||||
chai.assert.equal(count, 0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("skillMod", function(){
|
||||
beforeEach(function(){
|
||||
Effects.remove({});
|
||||
});
|
||||
|
||||
it("should get its base value from the ability mod", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
chai.assert.equal(3, strMod());
|
||||
chai.assert.equal(3, ath());
|
||||
});
|
||||
|
||||
it("should add a multiple of proficiency bonus", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
Effects.insert(getAttributeEffect(charId, "proficiencyBonus", "base", 7));
|
||||
chai.assert.equal(7, char.attributeValue("proficiencyBonus"), "the proficiency bonus is calculated correctly");
|
||||
Effects.insert(getSkillEffect(charId, "proficiency", 0.5));
|
||||
Effects.insert(getSkillEffect(charId, "proficiency", 2));
|
||||
chai.assert.equal(17, ath(), "3 strength + (7 x 2) proficiency bonus");
|
||||
});
|
||||
|
||||
it("should add", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
Effects.insert(getSkillEffect(charId, "add", 2));
|
||||
chai.assert.equal(5, ath());
|
||||
});
|
||||
|
||||
it("should multiply", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
Effects.insert(getSkillEffect(charId, "mul", 2));
|
||||
chai.assert.equal(6, ath());
|
||||
});
|
||||
|
||||
it("should be at least highest min", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
Effects.insert(getSkillEffect(charId, "min", 1));
|
||||
chai.assert.equal(3, ath());
|
||||
Effects.insert(getSkillEffect(charId, "min", 5));
|
||||
chai.assert.equal(5, ath());
|
||||
});
|
||||
|
||||
it("should be at most lowest max", function(){
|
||||
Effects.insert(getAttributeEffect(charId, "strength", "base", 16));
|
||||
Effects.insert(getSkillEffect(charId, "max", 5));
|
||||
chai.assert.equal(3, ath());
|
||||
Effects.insert(getSkillEffect(charId, "max", 2));
|
||||
chai.assert.equal(2, ath());
|
||||
});
|
||||
|
||||
it("should be removed when the character is deleted", function(){
|
||||
Characters.remove(charId);
|
||||
var count = Effects.find({charId: charId}).count();
|
||||
chai.assert.equal(count, 0);
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user