This commit is contained in:
Thaum
2014-11-03 06:17:35 +00:00
commit 2a82e39cf0
34 changed files with 1133 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
Armor = function(name, value){
this.name = name;
this.value = value;
this.equipped = true;
this.dexModMax = 20;
this.dexModMin = -20;
this.strengthNeeded = 0;
this.stealthDisadvantage = false;
}

View File

@@ -0,0 +1,324 @@
//set up the collection for characters
Characters = new Meteor.Collection("characters", {
//transform function alters the object returned by the database
transform: function (doc) {
//extend character with its protoypal functions
var newInstance = Object.create(protoCharacter);
doc = _.extend(newInstance, doc);
return doc;
}
});
//Attributes are numerical values
Attribute = function(base){
this.base = base; //the unmodified value of the attribute
//effects of the form {name: "Ring of Protection", value: 1}
this.add = []; //bonuses added to the attribute
this.mul = []; //multipliers to the attribute (after adding bonuses)
this.min = []; //effects setting the minimum value of the attribute
this.max = []; //effects setting the maximum value of the attribute
this.conditional = []; //conditional modifiers
}
var attributes = [
"strength",
"dexterity",
"constitution",
"intelligence",
"wisdom",
"charisma",
"hitPoints",
"proficiencyBonus",
"speed",
"armor",
"weight",
"weightCarried",
"age",
"ageRate",
"level1SpellSlots",
"level2SpellSlots",
"level3SpellSlots",
"level4SpellSlots",
"level5SpellSlots",
"level6SpellSlots",
"level7SpellSlots",
"level8SpellSlots",
"level9SpellSlots",
"ki",
"sorceryPoints",
"rages"
];
//Skills are bonuses to rolls: "+2" etc.
//They are based off of some ability
Skill = function(ability){
//proficiencies of the form {name: "Jack of all Trades", value: 0.5}
//only the highest is used
this.proficiency = [];
//ability name that this skill uses as base for roll
this.ability = ability;
this.add = [];
this.mul = [];
this.min = [];
this.max = [];
this.advantage = []; //effects granting advantage
this.disadvantage = [];
this.passiveAdd = []; //only added to passive checks
this.fail = []; //all checks are failed
this.conditional = []; //conditional modifiers
}
var skills = [
{skill: "strengthSave", ability: "strength"},
{skill: "dexteritySave", ability: "dexterity"},
{skill: "constitutionSave", ability: "constitution"},
{skill: "intelligenceSave", ability: "intelligence"},
{skill: "wisdomSave", ability: "wisdom"},
{skill: "charismaSave", ability: "charisma"},
{skill: "acrobatics", ability: "dexterity"},
{skill: "animalHandling", ability: "wisdom"},
{skill: "arcana",ability: "intelligence"},
{skill: "athletics", ability: "strength"},
{skill: "deception", ability: "charisma"},
{skill: "history", ability: "intelligence"},
{skill: "insight", ability: "wisdom"},
{skill: "intimidation", ability: "charisma"},
{skill: "investigation", ability: "intelligence"},
{skill: "medicine", ability: "wisdom"},
{skill: "nature", ability: "intelligence"},
{skill: "perception", ability: "wisdom"},
{skill: "performance", ability: "charisma"},
{skill: "persuasion", ability: "charisma"},
{skill: "religion", ability: "intelligence"},
{skill: "sleightOfHand", ability: "dexterity"},
{skill: "stealth", ability: "dexterity"},
{skill: "survival", ability: "wisdom"},
{skill: "initiative", ability: "dexterity"},
{skill: "strengthAttack", ability: "strength"},
{skill: "dexterityAttack", ability: "dexterity"},
{skill: "rangedAttack", ability: "dexterity"},
{skill: "dexterityArmor", ability: "dexterity"}
];
//Plain text fields for the character
var strings = [
"name",
"alignment",
"gender",
"race",
"description",
"personality",
"ideals",
"bonds",
"flaws"
];
//Data structure for the character
//no functions can be added to this constructor
Character = function(owner){
//attributes
this.attributes = {};
for(var i = 0, l = attributes.length; i < l; i++){
this.attributes[attributes[i]] = new Attribute(0);
}
//add 10 and dex bonus to armor
this.attributes.armor.add.push({name: "Base Armor Class", value: 10});
this.attributes.armor.add.push({name: "Dexterity Modifier", value: "skillMod skills.dexterityArmor"});
//skills
this.skills = {};
for(var i = 0, l = skills.length; i < l; i++){
this.skills[skills[i].skill] = new Skill(skills[i].ability);
}
this.deathSave = {
success : 0,
fail: 0
};
this.hitDice = [];
this.weaponProficiencies = [];
this.toolProficiencies = [];
this.languages = [];
this.features = [];
this.spells = [];
this.classes = [];
this.experience = new Experience();
this.vulnerability = {};
for(var i = 0, l = DamageTypes.length; i < l; i++){
this.vulnerability[DamageTypes[i]] = new Attribute(1);
this.vulnerability[DamageTypes[i]].min.push({name: "Resistance doesn't stack", value: 0.5});
this.vulnerability[DamageTypes[i]].max.push({name: "Vulnerability doesn't stack", value: 2});
}
//admin
this.owner = owner;
this.readers = [];
this.writers = [];
}
//functions and calculated values go here
var protoCharacter = {
attributeValue: function(attribute){
if (attribute === undefined) return;
//base value
var value = attribute.base;
//add all values in add array
for(var i = 0, l = attribute.add.length; i < l; i++){
var add = pop(attribute.add[i].value, this);
value += add ;
}
//multiply all values in mul array
for(var i = 0, l = attribute.mul.length; i < l; i++){
var mul = pop(attribute.mul[i], this);
value *= mul;
}
//largest min
for(var i = 0, l = attribute.min.length; i < l; i++){
var min = pop(attribute.min[i], this);
value = value > min? value : min;
}
//smallest max
for(var i = 0, l = attribute.max.length; i < l; i++){
var max = pop(attribute.max[i], this);
value = value < max? value : max;
}
return value;
},
skillMod: function(skill){
//get the final value of the ability score
var ability = this.attributeValue(this.attributes[skill.ability]);
//base modifier
var mod = +getMod(ability)
//multiply proficiency bonus by largest value in proficiency array
var prof = 0;
for(var i = 0, l = skill.proficiency.length; i < l; i++){
var newProf = pop(skill.proficiency[i].value, this);
if (newProf > prof){
prof = newProf;
}
}
//add multiplied proficiency bonus to modifier
mod += prof * this.attributeValue(this.attributes.proficiencyBonus);
//add all values in add array
for(var i = 0, l = skill.add.length; i < l; i++){
mod += pop(skill.add[i].value, this);
}
//multiply all values in mul array
for(var i = 0, l = skill.mul.length; i < l; i++){
mod *= pop(skill.mul[i].value, this);
}
//largest min
for(var i = 0, l = skill.min.length; i < l; i++){
var min = pop(skill.min[i], this);
mod = mod > min? mod : min;
}
//smallest max
for(var i = 0, l = skill.max.length; i < l; i++){
var max = pop(skill.max[i], this);
mod = mod < max? mod : max;
}
return signedString(mod);
},
passiveSkill: function(skill){
var mod = +this.skillMod(skill);
var value = 10 + mod;
for(var i = 0, l = skill.passiveAdd.length; i < l; i++){
value += pop(skill.passiveAdd[i].value, this);
}
return value;
//TODO decide whether (dis)advantage gives (-)+5 to passive checks
},
abilityMod: function(attribute){
return signedString(getMod(this.attributeValue(attribute)));
},
passiveAbility: function(attribute){
var mod = +getMod(this.attributeValue(attribute));
return 10 + mod;
}
}
getMod = function(score){
return Math.floor((score-10)/2);
}
signedString = function(number){
return number > 0? "+" + number : "" + number;
}
// turns dot notation strings into keys of root
// argument formats:
// 157, anything -> 157
// "some.number", object -> object.some.number
// "some.function", object -> object.some.function()
// "some.function arg1 arg2", object -> object.some.function(arg1, arg2)
pop = function(input, root){
if(typeof(input) === "string"){
//we need root for this part
if(root === undefined) return;
//this is a likely to fail if the string is malformed
try{
//split over spaces
var parts = input.split(" ");
//for each word
for (var i = 0; i < parts.length; i++){
//split over dots
var str = parts[i].split(".");
//start at root
parts[i] = root;
//for each word between dots
for (var j = 0; j < str.length; j++){
parts[i] = parts[i][str[j]];
}
}
//pull the first word out, might be a function
var func = parts.splice(0, 1)[0];
//if it's a function, apply the arguments to it
if(_.isFunction(func)) return +func.apply(root, parts);
//if it's a number, return it
if(!isNaN(func)) return +func;
} catch (err) {
//TODO pokemon catch statement is bad
//"gotta catch em all"
console.log(err);
return;
}
}
return +input;
}

View File

@@ -0,0 +1,118 @@
DamageTypes = [
"acid", "bludgeoning", "cold", "fire", "force",
"lightning", "necrotic", "piercing", "poison", "psychic",
"radiant", "slashing", "thunder"
]
Conditions = {};
Conditions.Blinded = {
description: ["a blinded creature can't see and automatically fails any ability check that requires sight.",
"Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage."]
}
Conditions.Charmed = {
description: ["A charmed creature can't attack the charmer or target the charmer with harmful abilities or magical effects",
"The charmer has advantage on any ability check to interact socially with the creature."]
}
Conditions.Deafened = {
description: ["A deafened creature can't hear and automatically fails any ability check that requires hearing"]
}
Conditions.Frightened = {
description: []
}
Conditions.Grappled = {
description: [
"A grappled creature's speed becomes 0, and it can't benefit from any bonuses to its speed",
"The condition ends if the grappler is incapacitated",
"The conditions also ends if if an effect removes the\
grappled creature from the reach of the grappler or grappling\
effect, such as when a creature is hurled\
away by the thunderwave spell."
],
effects: [
{stat: "attributes.speed.maximum", value: 0}
]
}
Conditions.Incapacitated = {
effects: [
{stat: "attributes.actions.maximum", value: 0},
{stat: "attributes.reactions.maximum", value: 0},
{stat: "attributes.bonusActions.maximum", value: 0}
]
}
Conditions.Invisible = {
}
Conditions.Paralyzed = {
//implies incapacitated
effects: [
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1},
{stat: "attributes.speed.maximum", value: 0}
]
}
_.extend(Conditions.Paralyzed, Conditions.Incapacitated);
Conditions.Petrified = {
effects: [
{stat: "attributes.weight.mul", value: 10},
{stat: "attributes.ageRate.max", value: 0},
{stat: "attributes.ageRate.min", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1},
{stat: "attributes.speed.maximum", value: 0}
]
}
for(var i = 0, l = DamageTypes.length; i < l; i++){
var str = "vulnerability." + DamageTypes[i] + ".mul"
Conditions.Petrified.effects.push({stat: str, value: 0.5});
}
_.extend(Conditions.Petrified, Conditions.Incapacitated);
Conditions.Poisoned = {
description: []
}
Conditions.Prone = {
description: [],
effects: [
{stat: "skills.strengthAttack.disadvantage", value: 1},
{stat: "skills.dexterityAttack.disadvantage", value: 1},
{stat: "skills.rangedAttack.disadvantage", value: 1}
]
}
Conditions.Restrained = {
effects: [
{stat: "attributes.speed.maximum", value: 0}
]
}
Conditions.Stunned = {
//implies incapacitated
effects: [
{stat: "attributes.speed.maximum", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1}
]
}
_.extend(Conditions.Stunned, Conditions.Incapacitated);
Conditions.Unconscious = {
//implies incapacitated
//implies prone
effects: [
{stat: "attributes.speed.maximum", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1}
]
}
_.extend(Conditions.Unconscious, Conditions.Incapacitated);
_.extend(Conditions.Unconscious, Conditions.Prone);

View File

@@ -0,0 +1,52 @@
Experience = function(){
this.total = 0;
this.events = [];
this.level = 0;
}
Experience.prototype.addEvent = function(description, value){
this.events.push({
"description": description,
"value": value
})
this.total += value;
this.level = this.getLevel();
}
Experience.prototype.removeEvent = function(index){
this.total -= this.events[index].value;
this.events.splice(index,1);
this.level = this.getLevel();
}
Experience.prototype.refreshTotal = function(){
this.total = 0;
for(var i = 0, length = this.events.length; i < length; i++){
this.total += this.events[i].value;
}
this.level = this.getLevel();
}
Experience.prototype.getLevel = function(){
var xp = this.total;
if(xp > 355000) return 20;
if(xp > 305000) return 19;
if(xp > 265000) return 18;
if(xp > 225000) return 17;
if(xp > 195000) return 16;
if(xp > 165000) return 15;
if(xp > 140000) return 14;
if(xp > 120000) return 13;
if(xp > 100000) return 12;
if(xp > 85000) return 11;
if(xp > 64000) return 10;
if(xp > 48000) return 9;
if(xp > 34000) return 8;
if(xp > 23000) return 7;
if(xp > 14000) return 6;
if(xp > 6500) return 5;
if(xp > 2700) return 4;
if(xp > 900) return 3;
if(xp > 300) return 2;
return 1;
}

View File

@@ -0,0 +1,4 @@
Feature = function(name){
this.name = name;
this.description = description;
}

View File

@@ -0,0 +1,6 @@
HitDice = function(sides){
this.sides = sides;
this.bonus = 0;
this.number = 0;
this.max = 0;
}

View File

@@ -0,0 +1,11 @@
Spell = function(name){
this.name = name;
this.level = 0;
this.school = "";
this.range = "";
this.verbal = false;
this.somatic = false;
this.material = false;
this.duration = "";
this.description = "";
}