diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js index b10e2c0e..30003f7a 100644 --- a/rpg-docs/Model/Character/Characters.js +++ b/rpg-docs/Model/Character/Characters.js @@ -5,12 +5,12 @@ Schemas.Character = new SimpleSchema({ strings: { type: Schemas.Strings }, attributes: { type: Schemas.Attributes }, skills: { type: Schemas.Skills }, - vulerabilities: { type: Schemas.Vulnerabilities }, proficiencies: { type: Schemas.Proficiencies }, - features: { type: [Schemas.Feature]}, - time: { type: Number, min: 0, decimal: true}, - initiativeOrder:{ type: Number, min: 0, max: 1, decimal: true}, - expirations: { type: [Schemas.Expiration]} + features: { type: [Schemas.Feature], defaultValue: []}, + deathSave: { type: Schemas.DeathSave }, + time: { type: Number, min: 0, decimal: true, defaultValue: 0}, + initiativeOrder:{ type: Number, min: 0, max: 1, decimal: true, defaultValue: 0}, + expirations: { type: [Schemas.Expiration], defaultValue: []} //TODO add permission stuff for owner, readers and writers //TODO hit dice //TODO spells @@ -38,26 +38,26 @@ Characters.helpers({ if (attribute === undefined) return; //base value var value = attribute.base; - + var char = this; //add all values in add array _.each(attribute.add, function(effect){ - value += pop(effect.value, this) + value += evaluateEffect(char, effect); }); //multiply all values in mul array _.each(attribute.mul, function(effect){ - value *= pop(effect.value, this) + value *= evaluateEffect(char, effect); }); //largest min _.each(attribute.min, function(effect){ - var min = pop(effect.value, this); + var min = evaluateEffect(char, effect); value = value > min? value : min; }); //smallest max _.each(attribute.max, function(effect){ - var max = pop(effect.value, this); + var max = evaluateEffect(char, effect); value = value < max? value : max; }); @@ -66,13 +66,14 @@ Characters.helpers({ proficiency: function(skill){ //return largest value in proficiency array + var char = this; var prof = 0; - for(var i = 0, l = skill.proficiency.length; i < l; i++){ - var newProf = pop(skill.proficiency[i].value, this); + _.each(skill.proficiency, function(effect){ + var newProf = evaluateEffect(char, effect); if (newProf > prof){ prof = newProf; } - } + }); return prof; }, @@ -81,6 +82,7 @@ Characters.helpers({ console.log("Cannot get skillMod of undefined"); return; } + var char = this; //get the final value of the ability score var ability = this.attributeValue(this.attributes[skill.ability]); @@ -95,23 +97,23 @@ Characters.helpers({ //add all values in add array _.each(skill.add, function(effect){ - mod += pop(effect.value, this) + mod += evaluateEffect(char, effect); }); //multiply all values in mul array _.each(skill.mul, function(effect){ - mod *= pop(effect.value, this) + mod *= evaluateEffect(char, effect); }); //largest min _.each(skill.min, function(effect){ - var min = pop(effect.value, this); + var min = evaluateEffect(char, effect); mod = mod > min? mod : min; }); //smallest max _.each(skill.max, function(effect){ - var max = pop(effect.value, this); + var max = evaluateEffect(char, effect); mod = mod < max? mod : max; }); @@ -120,9 +122,10 @@ Characters.helpers({ passiveSkill: function(skill){ var mod = +this.skillMod(skill); + var char = this; var value = 10 + mod; _.each(skill.passiveAdd, function(effect){ - value += pop(effect.value, this); + value += evaluateEffect(char, effect); }); return value; //TODO decide whether (dis)advantage gives (-)+5 to passive checks diff --git a/rpg-docs/Model/Character/SubSchemas/Attributes.js b/rpg-docs/Model/Character/SubSchemas/Attributes.js index d5cd6ecd..5627ce6a 100644 --- a/rpg-docs/Model/Character/SubSchemas/Attributes.js +++ b/rpg-docs/Model/Character/SubSchemas/Attributes.js @@ -13,13 +13,31 @@ Schemas.Attribute = new SimpleSchema({ conditional:{ type: [Schemas.Effect], defaultValue: [] } }); +//note to make an invulnerability add a new max of zero value +Schemas.Vulnerability = new SimpleSchema({ + //same as attribute + base: { + type: Number, + defaultValue: 0 + }, + //effect arrays + add: { type: [Schemas.Effect], defaultValue: [] }, + mul: { type: [Schemas.Effect], defaultValue: [] }, + min: { type: [Schemas.Effect], defaultValue: [{name: "Resistance doesn't stack", value: 0.5}] }, + max: { type: [Schemas.Effect], defaultValue: [{name: "Vulnerability doesn't stack", value: 2}] }, + conditional:{ type: [Schemas.Effect], defaultValue: [] }, +}); + Schemas.Attributes = new SimpleSchema({ + //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}, experience: {type: Schemas.Attribute}, proficiencyBonus: {type: Schemas.Attribute}, @@ -28,6 +46,15 @@ Schemas.Attributes = new SimpleSchema({ weightCarried: {type: Schemas.Attribute}, age: {type: Schemas.Attribute}, ageRate: {type: Schemas.Attribute}, + armor: {type: Schemas.Attribute}, + "armor.add": { + type: [Schemas.Effect], + defaultValue: [ + {name: "Dexterity Modifier", calculation: "dexterityArmor"} + ] + }, + + //resources level1SpellSlots: {type: Schemas.Attribute}, level2SpellSlots: {type: Schemas.Attribute}, level3SpellSlots: {type: Schemas.Attribute}, @@ -40,11 +67,26 @@ Schemas.Attributes = new SimpleSchema({ ki: {type: Schemas.Attribute}, sorceryPoints: {type: Schemas.Attribute}, rages: {type: Schemas.Attribute}, - armor: {type: Schemas.Attribute}, - "armor.add": { - type: [Schemas.Effect], - defaultValue: [ - {name: "Dexterity Modifier", value: "skillMod skills.dexterityArmor"} - ] - } + + + //hit dice + d6HitDice: {type: Schemas.Attribute}, + d8HitDice: {type: Schemas.Attribute}, + d10HitDice: {type: Schemas.Attribute}, + d12HitDice: {type: Schemas.Attribute}, + + //vulnerabilities + acidMultiplier: {type: Schemas.Vulnerability}, + bludgeoningMultiplier: {type: Schemas.Vulnerability}, + coldMultiplier: {type: Schemas.Vulnerability}, + fireMultiplier: {type: Schemas.Vulnerability}, + forceMultiplier: {type: Schemas.Vulnerability}, + lightningMultiplier: {type: Schemas.Vulnerability}, + necroticMultiplier: {type: Schemas.Vulnerability}, + piercingMultiplier: {type: Schemas.Vulnerability}, + poisonMultiplier: {type: Schemas.Vulnerability}, + psychicMultiplier: {type: Schemas.Vulnerability}, + radiantMultiplier: {type: Schemas.Vulnerability}, + slashingMultiplier: {type: Schemas.Vulnerability}, + thunderMultiplier: {type: Schemas.Vulnerability}, }); \ No newline at end of file diff --git a/rpg-docs/Model/Character/SubSchemas/Effect/Effect.js b/rpg-docs/Model/Character/SubSchemas/Effect/Effect.js index 9f68f379..ad526bf8 100644 --- a/rpg-docs/Model/Character/SubSchemas/Effect/Effect.js +++ b/rpg-docs/Model/Character/SubSchemas/Effect/Effect.js @@ -21,5 +21,11 @@ Schemas.Effect = new SimpleSchema({ calculation: { type: String, optional: true + }, + //indicates what the effect originated from + type: { + type: String, + defaultValue: "default", + allowedValues: ["default", "class", "race", "feat", "equippedMagic", "equippedMundane", "external"] } }); \ No newline at end of file diff --git a/rpg-docs/Model/Character/SubSchemas/Effect/Vulnerability.js b/rpg-docs/Model/Character/SubSchemas/Effect/Vulnerability.js new file mode 100644 index 00000000..e69de29b diff --git a/rpg-docs/Model/Character/SubSchemas/Proficiencies b/rpg-docs/Model/Character/SubSchemas/Proficiencies deleted file mode 100644 index e229f836..00000000 --- a/rpg-docs/Model/Character/SubSchemas/Proficiencies +++ /dev/null @@ -1,10 +0,0 @@ -Schemas.Proficiency = new simpleSchema({ - name: {type: String}, - source: {type: String} -}) - -Schemas.Proficiencies = new SimpleSchema({ - weapons: {type: [Schemas.Proficiency]}, - tools: {type: [Schemas.Proficiency]}, - languages: {type: [Schemas.Proficiency]} -}); \ No newline at end of file diff --git a/rpg-docs/Model/Character/SubSchemas/Proficiencies.js b/rpg-docs/Model/Character/SubSchemas/Proficiencies.js new file mode 100644 index 00000000..9db2c493 --- /dev/null +++ b/rpg-docs/Model/Character/SubSchemas/Proficiencies.js @@ -0,0 +1,19 @@ +Schemas.Proficiency = new SimpleSchema({ + name: {type: String}, + source: {type: String} +}) + +Schemas.Proficiencies = new SimpleSchema({ + weapons: { + type: [Schemas.Proficiency], + defaultValue: [] + }, + tools: { + type: [Schemas.Proficiency], + defaultValue: [] + }, + languages: { + type: [Schemas.Proficiency], + defaultValue: [] + } +}); \ No newline at end of file diff --git a/rpg-docs/Model/Character/SubSchemas/Vulnerabilities.js b/rpg-docs/Model/Character/SubSchemas/Vulnerabilities.js deleted file mode 100644 index 8da04688..00000000 --- a/rpg-docs/Model/Character/SubSchemas/Vulnerabilities.js +++ /dev/null @@ -1,24 +0,0 @@ -Schemas.Vulnerability = _.extend({ - "min.defaultValue": [ - {name: "Resistance doesn't stack", value: 0.5} - ], - "max.defaultValue": [ - {name: "Vulnerability doesn't stack", value: 2} - ] -}, Schemas.Attribute); - -Schemas.Vulnerabilities = new SimpleSchema({ - acid: {type: Schemas.Vulnerability}, - bludgeoning:{type: Schemas.Vulnerability}, - cold: {type: Schemas.Vulnerability}, - fire: {type: Schemas.Vulnerability}, - force: {type: Schemas.Vulnerability}, - lightning: {type: Schemas.Vulnerability}, - necrotic: {type: Schemas.Vulnerability}, - piercing: {type: Schemas.Vulnerability}, - poison: {type: Schemas.Vulnerability}, - psychic: {type: Schemas.Vulnerability}, - radiant: {type: Schemas.Vulnerability}, - slashing: {type: Schemas.Vulnerability}, - thunder: {type: Schemas.Vulnerability} -}); \ No newline at end of file diff --git a/rpg-docs/client/globalHelpers/effectList.js b/rpg-docs/client/globalHelpers/effectList.js new file mode 100644 index 00000000..3bc9df75 --- /dev/null +++ b/rpg-docs/client/globalHelpers/effectList.js @@ -0,0 +1,123 @@ +Template.registerHelper("effectList", function(obj, character){ + var result = $("
"); + if(_.has(obj, "conditional") && obj.conditional.length > 0){ + _.each(obj.conditional, function(cond){ + var c = $("
") + .append("* ") + .append(evaluateString(character, cond.name)); + result.append(c); + }); + } + + if(obj.base > 0){ + var c = $("
") + .append($("").addClass("auditValue").append(obj.base)) + .append("Base"); + result.append(c); + } + + if(_.has(obj, "proficiency") && obj.proficiency.length > 0){ + var highestProf = {}; + _.each(obj.proficiency, function(prof, i){ + var value = evaluateEffect(character, prof) + if(i === 0 || value > highestProf.value){ + highestProf.value = value; + highestProf.name = prof.name; + highestProf.calculation = prof.calculation; + } + }); + var c = $("
") + .append($("").addClass("auditValue").append(highestProf.value).append(" x Proficiency Bonus")) + .append(highestProf.name); + result.append(c); + } + + if(_.has(obj, "add") && obj.add.length > 0){ + _.each(obj.add, function(a){ + var value = signedString(evaluateEffect(character, a)); + var c = $("
") + .append($("").addClass("auditValue").append(value)) + .append(a.name); + result.append(c); + }); + } + + if(_.has(obj, "mul") && obj.mul.length > 0){ + _.each(obj.mul, function(a){ + var value = signedString(evaluateEffect(character, a)); + var c = $("
") + .append($("").addClass("auditValue").append("×").append(value)) + .append(a.name); + result.append(c); + }); + } + + if(_.has(obj, "min") && obj.min.length > 0){ + var highestMin = {}; + _.each(obj.min, function(m, i){ + var value = evaluateEffect(character, m) + if(i === 0 || value > highestMin.value){ + highestMin.value = value; + highestMin.name = m.name; + highestMin.calculation = m.calculation; + } + }); + var c = $("
") + .append($("").addClass("auditValue").append(highestMin.value).append(" Minimum")) + .append(highestMin.name); + result.append(c); + } + + if(_.has(obj, "max") && obj.max.length > 0){ + var lowestMax = {}; + _.each(obj.max, function(m, i){ + var value = evaluateEffect(character, m) + if(i === 0 || value < lowestMax.value){ + lowestMax.value = value; + lowestMax.name = m.name; + lowestMax.calculation = m.calculation; + } + }); + var c = $("
") + .append($("").addClass("auditValue").append(lowestMax.value).append(" Maximum")) + .append(lowestMax.name); + result.append(c); + } + + if(obj.base < 0){ + var c = $("
") + .append($("").addClass("auditValue").append(obj.base)) + .append("Damage"); + result.append(c); + } + + if(_.has(obj, "advantage") && obj.advantage.length > 0){ + _.each(obj.advantage, function(adv){ + var c = $("
") + .append($("").addClass("auditValue").append("Advantage")) + .append(adv.name); + result.append(c); + }) + } + + if(_.has(obj, "disadvantage") && obj.disadvantage.length > 0){ + _.each(obj.disadvantage, function(disadv){ + var c = $("
") + .append($("").addClass("auditValue").append("Disadvantage")) + .append(disadv.name); + result.append(c); + }) + } + + if(_.has(obj, "fail") && obj.fail.length > 0){ + _.each(obj.fail, function(f){ + var c = $("
") + .append($("").addClass("auditValue").append("Fail")) + .append(f.name); + result.append(c); + }) + } + var string = result.html() + if (_.isString(string)) return Spacebars.SafeString(string); + return string; +}); \ No newline at end of file diff --git a/rpg-docs/client/views/character/skills/skills.html b/rpg-docs/client/views/character/skills/skills.html index 0fccfb2b..88597d28 100644 --- a/rpg-docs/client/views/character/skills/skills.html +++ b/rpg-docs/client/views/character/skills/skills.html @@ -2,7 +2,7 @@

Saving Throws

{{#each saveList}} - + {{#if failSkill}} @@ -16,7 +16,7 @@

Skills

fail
{{#each skillList}} - + {{#if failSkill}} diff --git a/rpg-docs/client/views/character/textField/textField.js b/rpg-docs/client/views/character/textField/textField.js index bd696df5..aa73b886 100644 --- a/rpg-docs/client/views/character/textField/textField.js +++ b/rpg-docs/client/views/character/textField/textField.js @@ -9,11 +9,13 @@ Template.textField.helpers({ }, input: function(){ var text = this.character.strings[this.field]; - return Spacebars.SafeString(text); + if (_.isString(text)) return Spacebars.SafeString(text); + return text; }, output: function(){ var html = evaluateString(this.character, this.character.strings[this.field]); - return Spacebars.SafeString(html); + if (_.isString(html)) return Spacebars.SafeString(html); + return html; }, outputClass: function(){ if(Template.instance().editing.get()){ @@ -28,6 +30,7 @@ Template.textField.events({ "blur #textInput": function(){ Template.instance().editing.set(false); var text = $("#textInput").html(); + if(!_.isString(text)) text = ""; //TODO sanitise the html var setter = {}; setter["strings."+this.field] = text; diff --git a/rpg-docs/client/views/home/home.html b/rpg-docs/client/views/home/home.html index d49b039b..993e2488 100644 --- a/rpg-docs/client/views/home/home.html +++ b/rpg-docs/client/views/home/home.html @@ -2,7 +2,7 @@ {{>loginButtons}}
{{# each characters}} -
  • {{_id}}
  • +
  • {{_id}}
  • {{/each}}
    diff --git a/rpg-docs/client/views/home/home.js b/rpg-docs/client/views/home/home.js index 48601cce..77bbe1ac 100644 --- a/rpg-docs/client/views/home/home.js +++ b/rpg-docs/client/views/home/home.js @@ -1,8 +1,8 @@ Template.home.events({ "click #addCharacter": function (event, template) { - Characters.insert(new Character(Meteor.userId())); + Characters.insert({}); }, - "click #delete": function(event, template){ + "click .delete": function(event, template){ Characters.remove(this._id); } }); \ No newline at end of file diff --git a/rpg-docs/lib/functions/evaluateString.js b/rpg-docs/lib/functions/evaluateString.js index c6eb3323..e5d71af2 100644 --- a/rpg-docs/lib/functions/evaluateString.js +++ b/rpg-docs/lib/functions/evaluateString.js @@ -1,13 +1,23 @@ evaluate = function(character, string){ + if(!string) return string; string = string.replace(/\b[a-z]+\b/g, function(sub){ //skill mods - if(character.skills[sub]){ + if(_.has(character.skills, sub)){ return +character.skillMod(character.skills[sub]); } //attributes - if(character.attributes[sub]){ + if(_.has(character.attributes, sub)){ return +character.attributeValue(character.attributes[sub]); } + //ability modifiers + var abilityMods = ["strengthMod", "dexterityMod", "constitutionMod", "intelligenceMod", "wisdomMod", "charismaMod"] + if( _.contains(abilityMods, sub) ){ + var slice = sub.slice(0, - 3); + return +character.abilityMod(character.attributes[slice]); + } + if(sub === "level"){ + return +character.level(); + } return sub; }); try{ @@ -21,6 +31,7 @@ evaluate = function(character, string){ evaluateString = function(character, string){ //define brackets as curly brackets around anything that isn't a curly bracket + if(!string) return string; var brackets = /\{[^\{\}]*\}/g; var result = string.replace(brackets, function(exp){ var exp = exp.replace(/(\{|\})/g, "") //remove brackets @@ -33,4 +44,14 @@ evaluateString = function(character, string){ }); //this is going to return HTML, ensure it is santized! return result; +} + +evaluateEffect = function(character, effect){ + if(_.isFinite(effect.value)){ + return effect.value; + } else if(_.isString(effect.calculation)){ + return +evaluate(character, effect.calculation); + } else { + return 0; + } } \ No newline at end of file
    fail