From b7bdb141c871dc2a472255800d054d142cc2445a Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 13 Sep 2017 10:01:32 +0200 Subject: [PATCH 01/25] Added basic vMix server side api --- rpg-docs/Model/Character/Characters.js | 1 + rpg-docs/Routes/API.js | 10 ++ rpg-docs/lib/functions/vMixExport.js | 164 +++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 rpg-docs/Routes/API.js create mode 100644 rpg-docs/lib/functions/vMixExport.js diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js index e950409a..be848546 100644 --- a/rpg-docs/Model/Character/Characters.js +++ b/rpg-docs/Model/Character/Characters.js @@ -283,6 +283,7 @@ if (Meteor.isClient) { //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; diff --git a/rpg-docs/Routes/API.js b/rpg-docs/Routes/API.js new file mode 100644 index 00000000..38623253 --- /dev/null +++ b/rpg-docs/Routes/API.js @@ -0,0 +1,10 @@ +Router.map(function() { + this.route("vmixAPI", { + path: "/vmix-character/:_id/", + where: "server", + action: function() { + this.response.setHeader("Content-Type", "application/json"); + this.response.end(vMixJson(this.params._id)); + }, + }); +}); diff --git a/rpg-docs/lib/functions/vMixExport.js b/rpg-docs/lib/functions/vMixExport.js new file mode 100644 index 00000000..aae0393c --- /dev/null +++ b/rpg-docs/lib/functions/vMixExport.js @@ -0,0 +1,164 @@ +vMixJson = function(charId){ + var char = Characters.findOne(charId); + if (!char) { + return JSON.stringify({ + error: "character not found" + }); + } + if (char.settings.viewPermission !== "public"){ + return JSON.stringify({ + error: "character is not viewable to anyone with link" + }); + } + var baseValue = function(attributeName){ + return Characters.calculate.attributeBase(charId, attributeName); + }; + var attributeValue = function(attributeName){ + return Characters.calculate.attributeValue(charId, attributeName); + }; + var skillMod = function(skillName){ + return Characters.calculate.skillMod(charId, skillName); + }; + var damageMods = getDamageMods(charId); + var json = [ + {attribute: "Id", value: char._id}, + {attribute: "Name", value: char.name}, + {attribute: "Source", value: "DiceCloud"}, + {attribute: "Type", value: char.race}, + {attribute: "HPBase", value: baseValue("hitPoints")}, + {attribute: "HPValue", value: attributeValue("hitPoints")}, + {attribute: "HitDice", value: getHitDiceString(charId) || ""}, + {attribute: "AC", value: attributeValue("armor")}, + {attribute: "Initiative", value: skillMod("initiative")}, + {attribute: "Speed", value: attributeValue("speed")}, + + {attribute: "Str", value: attributeValue("strength")}, + {attribute: "Dex", value: attributeValue("dexterity")}, + {attribute: "Con", value: attributeValue("constitution")}, + {attribute: "Cha", value: attributeValue("charisma")}, + {attribute: "Int", value: attributeValue("intelligence")}, + {attribute: "Wis", value: attributeValue("wisdom")}, + + {attribute: "DamageVulnerabilities", value: damageMods.vulnerabilities}, + {attribute: "DamageResistances", value: damageMods.resistances}, + {attribute: "DamageImmunities", value: damageMods.immunities}, + + {attribute: "StrSave", value: skillMod("strengthSave")}, + {attribute: "DexSave", value: skillMod("dexteritySave")}, + {attribute: "ConSave", value: skillMod("constitutionSave")}, + {attribute: "IntSave", value: skillMod("intelligenceSave")}, + {attribute: "WisSave", value: skillMod("wisdomSave")}, + {attribute: "ChaSave", value: skillMod("charismaSave")}, + { + attribute: "passivePerception", + value: Characters.calculate.passiveSkill(charId, "perception"), + }, + {attribute: "Languages", value: getLanguages(charId)}, + {attribute: "Description", value: char.description || ""}, + ]; + json.push(...getSkills(charId)); + return JSON.stringify(json, null, 2); +} + +var getHitDiceString = function(charId){ + var d6 = Characters.calculate.attributeBase(charId, "d6HitDice"); + var d8 = Characters.calculate.attributeBase(charId, "d8HitDice"); + var d10 = Characters.calculate.attributeBase(charId, "d10HitDice"); + var d12 = Characters.calculate.attributeBase(charId, "d12HitDice"); + var con = Characters.calculate.abilityMod(charId,"constitution"); + var string = "" + + (d6 ? `${d6}d6 + ` : "") + + (d8 ? `${d8}d8 + ` : "") + + (d10 ? `${d10}d10 + ` : "") + + (d12 ? `${d12}d12 + ` : "") + + con; +} + +var getArmorString = function(charId){ + var bases = Effects.find({ + charId: charId, + stat: "armor", + operation: "base", + enabled: true, + }).map(e => ({ + ame: e.name, + value: evaluateEffect(charId, e), + })); + var base = bases.length && _.max(bases, b => b.value).name || ""; + var effects = Effects.find({ + charId: charId, + stat: "armor", + operation: {$ne: "base"}, + enabled: true, + }).map(e => e.name); + var strings = base ? [base] : []; + strings = strings.concat(effects); + return strings.join(", "); +} + +var getDamageMods = function(charId){ + // jscs:disable maximumLineLength + var multipliers = [ + {name: "Acid", value: Characters.calculate.attributeValue(charId, "acidMultiplier")}, + {name: "Bludgeoning", value: Characters.calculate.attributeValue(charId, "bludgeoningMultiplier")}, + {name: "Cold", value: Characters.calculate.attributeValue(charId, "coldMultiplier")}, + {name: "Fire", value: Characters.calculate.attributeValue(charId, "fireMultiplier")}, + {name: "Force", value: Characters.calculate.attributeValue(charId, "forceMultiplier")}, + {name: "Lightning", value: Characters.calculate.attributeValue(charId, "lightningMultiplier")}, + {name: "Necrotic", value: Characters.calculate.attributeValue(charId, "necroticMultiplier")}, + {name: "Piercing", value: Characters.calculate.attributeValue(charId, "piercingMultiplier")}, + {name: "Poison", value: Characters.calculate.attributeValue(charId, "poisonMultiplier")}, + {name: "Psychic", value: Characters.calculate.attributeValue(charId, "psychicMultiplier")}, + {name: "Radiant", value: Characters.calculate.attributeValue(charId, "radiantMultiplier")}, + {name: "Slashing", value: Characters.calculate.attributeValue(charId, "slashingMultiplier")}, + {name: "Thunder", value: Characters.calculate.attributeValue(charId, "thunderMultiplier")}, + ]; + // jscs:enable maximumLineLength + multipliers = _.groupBy(multipliers, "value"); + var names = o => o.name; + return { + "immunities": _.map(multipliers["0"], names).join(", "), + "resistances": _.map(multipliers["0.5"], names).join(", "), + "vulnerabilities": _.map(multipliers["2"], names).join(", "), + }; +} + +var getSkills = function(charId){ + var allSkills = [ + {name: "acrobatics", attribute: "dexterity"}, + {name: "animalHandling", attribute: "wisdom"}, + {name: "arcana", attribute: "intelligence"}, + {name: "athletics", attribute: "strength"}, + {name: "deception", attribute: "charisma"}, + {name: "history", attribute: "intelligence"}, + {name: "insight", attribute: "wisdom"}, + {name: "intimidation", attribute: "charisma"}, + {name: "investigation", attribute: "intelligence"}, + {name: "medicine", attribute: "wisdom"}, + {name: "nature", attribute: "intelligence"}, + {name: "perception", attribute: "wisdom"}, + {name: "performance", attribute: "charisma"}, + {name: "persuasion", attribute: "charisma"}, + {name: "religion", attribute: "intelligence"}, + {name: "sleightOfHand", attribute: "dexterity"}, + {name: "stealth", attribute: "dexterity"}, + {name: "survival", attribute: "wisdom"}, + ]; + var skills = []; + _.each(allSkills, skill => { + var value = Characters.calculate.skillMod(charId, skill.name); + var mod = Characters.calculate.abilityMod(charId, skill.attribute); + if (value !== mod){ + skills.push({"attribute": skill.name, value}); + } + }); + return skills; +}; + +var getLanguages = function(charId){ + return Proficiencies.find({ + charId, + enabled: true, + type: "language", + }).map(l => l.name).join(", "); +}; From 7c2aed26a46c25417e38c3d0981e9257a9c2ef73 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 13 Sep 2017 14:01:07 +0200 Subject: [PATCH 02/25] Fixed vMix export, included vMix parties --- rpg-docs/Routes/API.js | 12 +- rpg-docs/lib/functions/characterExport.js | 161 +++++++++++++++++++++ rpg-docs/lib/functions/vMixExport.js | 167 +--------------------- 3 files changed, 177 insertions(+), 163 deletions(-) create mode 100644 rpg-docs/lib/functions/characterExport.js diff --git a/rpg-docs/Routes/API.js b/rpg-docs/Routes/API.js index 38623253..aab73b3d 100644 --- a/rpg-docs/Routes/API.js +++ b/rpg-docs/Routes/API.js @@ -1,10 +1,18 @@ Router.map(function() { - this.route("vmixAPI", { + this.route("vmixCharacter", { path: "/vmix-character/:_id/", where: "server", action: function() { this.response.setHeader("Content-Type", "application/json"); - this.response.end(vMixJson(this.params._id)); + this.response.end(vMixCharacter(this.params._id)); + }, + }); + this.route("vmixParty", { + path: "/vmix-party/:_id/", + where: "server", + action: function() { + this.response.setHeader("Content-Type", "application/json"); + this.response.end(vMixParty(this.params._id)); }, }); }); diff --git a/rpg-docs/lib/functions/characterExport.js b/rpg-docs/lib/functions/characterExport.js new file mode 100644 index 00000000..daac7ec7 --- /dev/null +++ b/rpg-docs/lib/functions/characterExport.js @@ -0,0 +1,161 @@ +characterExport = function(charId){ + var char = Characters.findOne(charId); + if (!char) { + return { + error: charId + " character not found" + }; + } + if (char.settings.viewPermission !== "public" && Meteor.isServer){ + return { + error: charId + " character is not viewable to anyone with link" + }; + } + var baseValue = function(attributeName){ + return Characters.calculate.attributeBase(charId, attributeName); + }; + var attributeValue = function(attributeName){ + return Characters.calculate.attributeValue(charId, attributeName); + }; + var skillMod = function(skillName){ + return Characters.calculate.skillMod(charId, skillName); + }; + var damageMods = getDamageMods(charId); + var character = { + "Id": char._id, + "Name": char.name, + "Source": "DiceCloud", + "Type": char.race, + "HPBase": baseValue("hitPoints"), + "HPValue": attributeValue("hitPoints"), + "HitDice": getHitDiceString(charId) || "", + "AC": attributeValue("armor"), + "Initiative": skillMod("initiative"), + "Speed": attributeValue("speed"), + + "Str": attributeValue("strength"), + "Dex": attributeValue("dexterity"), + "Con": attributeValue("constitution"), + "Cha": attributeValue("charisma"), + "Int": attributeValue("intelligence"), + "Wis": attributeValue("wisdom"), + + "DamageVulnerabilities": damageMods.vulnerabilities, + "DamageResistances": damageMods.resistances, + "DamageImmunities": damageMods.immunities, + + "StrSave": skillMod("strengthSave"), + "DexSave": skillMod("dexteritySave"), + "ConSave": skillMod("constitutionSave"), + "IntSave": skillMod("intelligenceSave"), + "WisSave": skillMod("wisdomSave"), + "ChaSave": skillMod("charismaSave"), + "passivePerception": Characters.calculate.passiveSkill(charId, "perception"), + "Languages": getLanguages(charId), + "Description": char.description || "", + }; + _.extend(character, getSkills(charId)); + return character; +} + +var getHitDiceString = function(charId){ + var d6 = Characters.calculate.attributeBase(charId, "d6HitDice"); + var d8 = Characters.calculate.attributeBase(charId, "d8HitDice"); + var d10 = Characters.calculate.attributeBase(charId, "d10HitDice"); + var d12 = Characters.calculate.attributeBase(charId, "d12HitDice"); + var con = Characters.calculate.abilityMod(charId,"constitution"); + var string = "" + + (d6 ? `${d6}d6 + ` : "") + + (d8 ? `${d8}d8 + ` : "") + + (d10 ? `${d10}d10 + ` : "") + + (d12 ? `${d12}d12 + ` : "") + + con; +} + +var getArmorString = function(charId){ + var bases = Effects.find({ + charId: charId, + stat: "armor", + operation: "base", + enabled: true, + }).map(e => ({ + ame: e.name, + value: evaluateEffect(charId, e), + })); + var base = bases.length && _.max(bases, b => b.value).name || ""; + var effects = Effects.find({ + charId: charId, + stat: "armor", + operation: {$ne: "base"}, + enabled: true, + }).map(e => e.name); + var strings = base ? [base] : []; + strings = strings.concat(effects); + return strings.join(", "); +} + +var getDamageMods = function(charId){ + // jscs:disable maximumLineLength + var multipliers = [ + {name: "Acid", value: Characters.calculate.attributeValue(charId, "acidMultiplier")}, + {name: "Bludgeoning", value: Characters.calculate.attributeValue(charId, "bludgeoningMultiplier")}, + {name: "Cold", value: Characters.calculate.attributeValue(charId, "coldMultiplier")}, + {name: "Fire", value: Characters.calculate.attributeValue(charId, "fireMultiplier")}, + {name: "Force", value: Characters.calculate.attributeValue(charId, "forceMultiplier")}, + {name: "Lightning", value: Characters.calculate.attributeValue(charId, "lightningMultiplier")}, + {name: "Necrotic", value: Characters.calculate.attributeValue(charId, "necroticMultiplier")}, + {name: "Piercing", value: Characters.calculate.attributeValue(charId, "piercingMultiplier")}, + {name: "Poison", value: Characters.calculate.attributeValue(charId, "poisonMultiplier")}, + {name: "Psychic", value: Characters.calculate.attributeValue(charId, "psychicMultiplier")}, + {name: "Radiant", value: Characters.calculate.attributeValue(charId, "radiantMultiplier")}, + {name: "Slashing", value: Characters.calculate.attributeValue(charId, "slashingMultiplier")}, + {name: "Thunder", value: Characters.calculate.attributeValue(charId, "thunderMultiplier")}, + ]; + // jscs:enable maximumLineLength + multipliers = _.groupBy(multipliers, "value"); + var names = o => o.name; + return { + "immunities": _.map(multipliers["0"], names).join(", "), + "resistances": _.map(multipliers["0.5"], names).join(", "), + "vulnerabilities": _.map(multipliers["2"], names).join(", "), + }; +} + +var getSkills = function(charId){ + var allSkills = [ + {name: "acrobatics", attribute: "dexterity"}, + {name: "animalHandling", attribute: "wisdom"}, + {name: "arcana", attribute: "intelligence"}, + {name: "athletics", attribute: "strength"}, + {name: "deception", attribute: "charisma"}, + {name: "history", attribute: "intelligence"}, + {name: "insight", attribute: "wisdom"}, + {name: "intimidation", attribute: "charisma"}, + {name: "investigation", attribute: "intelligence"}, + {name: "medicine", attribute: "wisdom"}, + {name: "nature", attribute: "intelligence"}, + {name: "perception", attribute: "wisdom"}, + {name: "performance", attribute: "charisma"}, + {name: "persuasion", attribute: "charisma"}, + {name: "religion", attribute: "intelligence"}, + {name: "sleightOfHand", attribute: "dexterity"}, + {name: "stealth", attribute: "dexterity"}, + {name: "survival", attribute: "wisdom"}, + ]; + var skills = {}; + _.each(allSkills, skill => { + var value = Characters.calculate.skillMod(charId, skill.name); + var mod = Characters.calculate.abilityMod(charId, skill.attribute); + if (value !== mod){ + skills[skill.name] = value; + } + }); + return skills; +}; + +var getLanguages = function(charId){ + return Proficiencies.find({ + charId, + enabled: true, + type: "language", + }).map(l => l.name).join(", "); +}; diff --git a/rpg-docs/lib/functions/vMixExport.js b/rpg-docs/lib/functions/vMixExport.js index aae0393c..b2a95fd2 100644 --- a/rpg-docs/lib/functions/vMixExport.js +++ b/rpg-docs/lib/functions/vMixExport.js @@ -1,164 +1,9 @@ -vMixJson = function(charId){ - var char = Characters.findOne(charId); - if (!char) { - return JSON.stringify({ - error: "character not found" - }); - } - if (char.settings.viewPermission !== "public"){ - return JSON.stringify({ - error: "character is not viewable to anyone with link" - }); - } - var baseValue = function(attributeName){ - return Characters.calculate.attributeBase(charId, attributeName); - }; - var attributeValue = function(attributeName){ - return Characters.calculate.attributeValue(charId, attributeName); - }; - var skillMod = function(skillName){ - return Characters.calculate.skillMod(charId, skillName); - }; - var damageMods = getDamageMods(charId); - var json = [ - {attribute: "Id", value: char._id}, - {attribute: "Name", value: char.name}, - {attribute: "Source", value: "DiceCloud"}, - {attribute: "Type", value: char.race}, - {attribute: "HPBase", value: baseValue("hitPoints")}, - {attribute: "HPValue", value: attributeValue("hitPoints")}, - {attribute: "HitDice", value: getHitDiceString(charId) || ""}, - {attribute: "AC", value: attributeValue("armor")}, - {attribute: "Initiative", value: skillMod("initiative")}, - {attribute: "Speed", value: attributeValue("speed")}, - - {attribute: "Str", value: attributeValue("strength")}, - {attribute: "Dex", value: attributeValue("dexterity")}, - {attribute: "Con", value: attributeValue("constitution")}, - {attribute: "Cha", value: attributeValue("charisma")}, - {attribute: "Int", value: attributeValue("intelligence")}, - {attribute: "Wis", value: attributeValue("wisdom")}, - - {attribute: "DamageVulnerabilities", value: damageMods.vulnerabilities}, - {attribute: "DamageResistances", value: damageMods.resistances}, - {attribute: "DamageImmunities", value: damageMods.immunities}, - - {attribute: "StrSave", value: skillMod("strengthSave")}, - {attribute: "DexSave", value: skillMod("dexteritySave")}, - {attribute: "ConSave", value: skillMod("constitutionSave")}, - {attribute: "IntSave", value: skillMod("intelligenceSave")}, - {attribute: "WisSave", value: skillMod("wisdomSave")}, - {attribute: "ChaSave", value: skillMod("charismaSave")}, - { - attribute: "passivePerception", - value: Characters.calculate.passiveSkill(charId, "perception"), - }, - {attribute: "Languages", value: getLanguages(charId)}, - {attribute: "Description", value: char.description || ""}, - ]; - json.push(...getSkills(charId)); - return JSON.stringify(json, null, 2); -} - -var getHitDiceString = function(charId){ - var d6 = Characters.calculate.attributeBase(charId, "d6HitDice"); - var d8 = Characters.calculate.attributeBase(charId, "d8HitDice"); - var d10 = Characters.calculate.attributeBase(charId, "d10HitDice"); - var d12 = Characters.calculate.attributeBase(charId, "d12HitDice"); - var con = Characters.calculate.abilityMod(charId,"constitution"); - var string = "" + - (d6 ? `${d6}d6 + ` : "") + - (d8 ? `${d8}d8 + ` : "") + - (d10 ? `${d10}d10 + ` : "") + - (d12 ? `${d12}d12 + ` : "") + - con; -} - -var getArmorString = function(charId){ - var bases = Effects.find({ - charId: charId, - stat: "armor", - operation: "base", - enabled: true, - }).map(e => ({ - ame: e.name, - value: evaluateEffect(charId, e), - })); - var base = bases.length && _.max(bases, b => b.value).name || ""; - var effects = Effects.find({ - charId: charId, - stat: "armor", - operation: {$ne: "base"}, - enabled: true, - }).map(e => e.name); - var strings = base ? [base] : []; - strings = strings.concat(effects); - return strings.join(", "); -} - -var getDamageMods = function(charId){ - // jscs:disable maximumLineLength - var multipliers = [ - {name: "Acid", value: Characters.calculate.attributeValue(charId, "acidMultiplier")}, - {name: "Bludgeoning", value: Characters.calculate.attributeValue(charId, "bludgeoningMultiplier")}, - {name: "Cold", value: Characters.calculate.attributeValue(charId, "coldMultiplier")}, - {name: "Fire", value: Characters.calculate.attributeValue(charId, "fireMultiplier")}, - {name: "Force", value: Characters.calculate.attributeValue(charId, "forceMultiplier")}, - {name: "Lightning", value: Characters.calculate.attributeValue(charId, "lightningMultiplier")}, - {name: "Necrotic", value: Characters.calculate.attributeValue(charId, "necroticMultiplier")}, - {name: "Piercing", value: Characters.calculate.attributeValue(charId, "piercingMultiplier")}, - {name: "Poison", value: Characters.calculate.attributeValue(charId, "poisonMultiplier")}, - {name: "Psychic", value: Characters.calculate.attributeValue(charId, "psychicMultiplier")}, - {name: "Radiant", value: Characters.calculate.attributeValue(charId, "radiantMultiplier")}, - {name: "Slashing", value: Characters.calculate.attributeValue(charId, "slashingMultiplier")}, - {name: "Thunder", value: Characters.calculate.attributeValue(charId, "thunderMultiplier")}, - ]; - // jscs:enable maximumLineLength - multipliers = _.groupBy(multipliers, "value"); - var names = o => o.name; - return { - "immunities": _.map(multipliers["0"], names).join(", "), - "resistances": _.map(multipliers["0.5"], names).join(", "), - "vulnerabilities": _.map(multipliers["2"], names).join(", "), - }; -} - -var getSkills = function(charId){ - var allSkills = [ - {name: "acrobatics", attribute: "dexterity"}, - {name: "animalHandling", attribute: "wisdom"}, - {name: "arcana", attribute: "intelligence"}, - {name: "athletics", attribute: "strength"}, - {name: "deception", attribute: "charisma"}, - {name: "history", attribute: "intelligence"}, - {name: "insight", attribute: "wisdom"}, - {name: "intimidation", attribute: "charisma"}, - {name: "investigation", attribute: "intelligence"}, - {name: "medicine", attribute: "wisdom"}, - {name: "nature", attribute: "intelligence"}, - {name: "perception", attribute: "wisdom"}, - {name: "performance", attribute: "charisma"}, - {name: "persuasion", attribute: "charisma"}, - {name: "religion", attribute: "intelligence"}, - {name: "sleightOfHand", attribute: "dexterity"}, - {name: "stealth", attribute: "dexterity"}, - {name: "survival", attribute: "wisdom"}, - ]; - var skills = []; - _.each(allSkills, skill => { - var value = Characters.calculate.skillMod(charId, skill.name); - var mod = Characters.calculate.abilityMod(charId, skill.attribute); - if (value !== mod){ - skills.push({"attribute": skill.name, value}); - } - }); - return skills; +vMixCharacter = function(charId){ + return JSON.stringify([characterExport(charId)], null, 2); }; -var getLanguages = function(charId){ - return Proficiencies.find({ - charId, - enabled: true, - type: "language", - }).map(l => l.name).join(", "); +vMixParty = function(partyId){ + var party = Parties.findOne(partyId); + var chars = _.map(party.characters, charId => characterExport(charId)); + return JSON.stringify(chars, null, 2); }; From 535fcd77cf96bcb332087280b7fa76606346cb78 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 13 Sep 2017 16:01:05 +0200 Subject: [PATCH 03/25] Added missing attributes to vmix export --- rpg-docs/lib/functions/characterExport.js | 82 +++++++++++++++++++---- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/rpg-docs/lib/functions/characterExport.js b/rpg-docs/lib/functions/characterExport.js index daac7ec7..0db37c10 100644 --- a/rpg-docs/lib/functions/characterExport.js +++ b/rpg-docs/lib/functions/characterExport.js @@ -16,6 +16,9 @@ characterExport = function(charId){ var attributeValue = function(attributeName){ return Characters.calculate.attributeValue(charId, attributeName); }; + var abilityMod = function(attributeName){ + return Characters.calculate.abilityMod(charId, attributeName) + }; var skillMod = function(skillName){ return Characters.calculate.skillMod(charId, skillName); }; @@ -24,20 +27,43 @@ characterExport = function(charId){ "Id": char._id, "Name": char.name, "Source": "DiceCloud", - "Type": char.race, + "Alignment": char.alignment || "", + "Gender": char.gender || "", + "Race": char.race || "", + "Level": Characters.calculate.level(charId), + "Experience": Characters.calculate.experience(charId), + "Class": getClasses(charId), "HPBase": baseValue("hitPoints"), "HPValue": attributeValue("hitPoints"), "HitDice": getHitDiceString(charId) || "", "AC": attributeValue("armor"), "Initiative": skillMod("initiative"), "Speed": attributeValue("speed"), + "ProficiencyBonus": attributeValue("proficiencyBonus"), + "passivePerception": Characters.calculate.passiveSkill(charId, "perception"), - "Str": attributeValue("strength"), - "Dex": attributeValue("dexterity"), - "Con": attributeValue("constitution"), - "Cha": attributeValue("charisma"), - "Int": attributeValue("intelligence"), - "Wis": attributeValue("wisdom"), + "Languages": getLanguages(charId), + "Description": char.description || "", + "Backstory": char.backstory || "", + "Personality": char.Personality || "" , + "Bonds": char.bonds || "", + "Ideals": char.ideals || "", + "Flaws": char.flaws || "", + "PictureURL": char.picture || "", + + "Strength": attributeValue("strength"), + "Dexterity": attributeValue("dexterity"), + "Constitution": attributeValue("constitution"), + "intelligence": attributeValue("intelligence"), + "Wisdom": attributeValue("wisdom"), + "Charisma": attributeValue("charisma"), + + "StrengthMod": abilityMod("strength"), + "DexterityMod": abilityMod("dexterity"), + "ConstitutionMod": abilityMod("constitution"), + "intelligenceMod": abilityMod("intelligence"), + "WisdomMod": abilityMod("wisdom"), + "CharismaMod": abilityMod("charisma"), "DamageVulnerabilities": damageMods.vulnerabilities, "DamageResistances": damageMods.resistances, @@ -49,11 +75,23 @@ characterExport = function(charId){ "IntSave": skillMod("intelligenceSave"), "WisSave": skillMod("wisdomSave"), "ChaSave": skillMod("charismaSave"), - "passivePerception": Characters.calculate.passiveSkill(charId, "perception"), - "Languages": getLanguages(charId), - "Description": char.description || "", + + "Level1SpellSlots": attributeValue("level1SpellSlots"), + "Level2SpellSlots": attributeValue("level2SpellSlots"), + "Level3SpellSlots": attributeValue("level3SpellSlots"), + "Level4SpellSlots": attributeValue("level4SpellSlots"), + "Level5SpellSlots": attributeValue("level5SpellSlots"), + "Level6SpellSlots": attributeValue("level6SpellSlots"), + "Level7SpellSlots": attributeValue("level7SpellSlots"), + "Level8SpellSlots": attributeValue("level8SpellSlots"), + "Level9SpellSlots": attributeValue("level9SpellSlots"), + "Ki": attributeValue("ki"), + "Rages": attributeValue("rages"), + "RageDamage": attributeValue("rageDamage"), + "SorceryPoints": attributeValue("sorceryPoints"), }; _.extend(character, getSkills(charId)); + _.extend(character, getAttacks(charId)); return character; } @@ -144,10 +182,7 @@ var getSkills = function(charId){ var skills = {}; _.each(allSkills, skill => { var value = Characters.calculate.skillMod(charId, skill.name); - var mod = Characters.calculate.abilityMod(charId, skill.attribute); - if (value !== mod){ - skills[skill.name] = value; - } + skills[skill.name] = value; }); return skills; }; @@ -159,3 +194,22 @@ var getLanguages = function(charId){ type: "language", }).map(l => l.name).join(", "); }; + +var getClasses = function(charId){ + return Classes.find({charId}).map(c => `${c.name} ${c.level}`).join(", "); +}; + +var getAttacks = function(charId){ + var attacks = {}; + var i = 1; + Attacks.find( + {charId, enabled: true}, + {sort: {color: 1, name: 1}} + ).forEach(a => { + attacks[`Attack${i++}`] = a.name + + ` +${evaluate(charId, a.attackBonus)} to hit, ` + + `${evaluateString(charId, a.damage)} ${a.damageType} damage, ` + + `${a.details}`; + }); + return attacks; +} From 8a58002415081a47629b2ea511d3f2906e1602c3 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 22 Sep 2017 10:42:34 +0200 Subject: [PATCH 04/25] Fixed spells in the library using their library ID's in the spells collection closes #137 --- rpg-docs/client/views/character/spells/spells.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rpg-docs/client/views/character/spells/spells.js b/rpg-docs/client/views/character/spells/spells.js index 247445f0..e2b4da76 100644 --- a/rpg-docs/client/views/character/spells/spells.js +++ b/rpg-docs/client/views/character/spells/spells.js @@ -267,9 +267,11 @@ Template.spells.events({ //loop through all returned spells _.each(resultArray, (rawSpell, index) =>{ // Make the library spell into a regular spell - let spell = _.omit(rawSpell, "library", "attacks", "effects"); + let spell = _.omit(rawSpell, "_id", "library", "attacks", "effects"); + // Use the ID generated earlier for the first spell so we + // can animate to it if (index == 0) { - spell._id = spellId; //only do this for the first spell added + spell._id = spellId; } spell.charId = charId; spell.parent = { From 020930b2e4d3e825877067814f207057cbb6eda6 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 22 Sep 2017 11:03:00 +0200 Subject: [PATCH 05/25] Checked if old hitpoint slider exists before resetting it closes #135 --- rpg-docs/client/views/character/stats/healthCard/healthCard.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpg-docs/client/views/character/stats/healthCard/healthCard.js b/rpg-docs/client/views/character/stats/healthCard/healthCard.js index 58d20f68..1c1e7751 100644 --- a/rpg-docs/client/views/character/stats/healthCard/healthCard.js +++ b/rpg-docs/client/views/character/stats/healthCard/healthCard.js @@ -16,7 +16,8 @@ Template.healthCard.onRendered(function(){ const id = Template.currentData()._id; if (oldId !== id){ this.find("#hitPointSlider").resetOldValue(); - this.find("#temporaryHitPointSlider").resetOldValue(); + var thpSlider = this.find("#temporaryHitPointSlider"); + thpSlider && thpSlider.resetOldValue(); oldId = id; } }); From 6528fc8bab34af262dab91f16a9ff9d837332a19 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 22 Sep 2017 11:41:32 +0200 Subject: [PATCH 06/25] Improved vMix export closes #138 --- rpg-docs/lib/functions/characterExport.js | 51 +++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/rpg-docs/lib/functions/characterExport.js b/rpg-docs/lib/functions/characterExport.js index 0db37c10..dd8e95c5 100644 --- a/rpg-docs/lib/functions/characterExport.js +++ b/rpg-docs/lib/functions/characterExport.js @@ -17,11 +17,18 @@ characterExport = function(charId){ return Characters.calculate.attributeValue(charId, attributeName); }; var abilityMod = function(attributeName){ - return Characters.calculate.abilityMod(charId, attributeName) + return signedString( + Characters.calculate.abilityMod(charId, attributeName) + ); }; var skillMod = function(skillName){ - return Characters.calculate.skillMod(charId, skillName); + return signedString( + Characters.calculate.skillMod(charId, skillName) + ); }; + var proficiency = function(skillName){ + return Characters.calculate.proficiency(charId, skillName); + } var damageMods = getDamageMods(charId); var character = { "Id": char._id, @@ -45,7 +52,7 @@ characterExport = function(charId){ "Languages": getLanguages(charId), "Description": char.description || "", "Backstory": char.backstory || "", - "Personality": char.Personality || "" , + "Personality": char.personality || "" , "Bonds": char.bonds || "", "Ideals": char.ideals || "", "Flaws": char.flaws || "", @@ -69,12 +76,18 @@ characterExport = function(charId){ "DamageResistances": damageMods.resistances, "DamageImmunities": damageMods.immunities, - "StrSave": skillMod("strengthSave"), - "DexSave": skillMod("dexteritySave"), - "ConSave": skillMod("constitutionSave"), - "IntSave": skillMod("intelligenceSave"), - "WisSave": skillMod("wisdomSave"), - "ChaSave": skillMod("charismaSave"), + "StrengthSave": skillMod("strengthSave"), + "StrengthSaveProficiency": proficiency("strengthSave"), + "DexteritySave": skillMod("dexteritySave"), + "DexteritySaveProficiency": proficiency("dexteritySave"), + "ConstitutionSave": skillMod("constitutionSave"), + "ConstitutionSaveProficiency": proficiency("constitutionSave"), + "intelligenceSave": skillMod("intelligenceSave"), + "intelligenceSaveProficiency": proficiency("intelligenceSave"), + "WisdomSave": skillMod("wisdomSave"), + "WisdomSaveProficiency": proficiency("wisdomSave"), + "CharismaSave": skillMod("charismaSave"), + "CharismaSaveProficiency": proficiency("charismaSave"), "Level1SpellSlots": attributeValue("level1SpellSlots"), "Level2SpellSlots": attributeValue("level2SpellSlots"), @@ -89,6 +102,10 @@ characterExport = function(charId){ "Rages": attributeValue("rages"), "RageDamage": attributeValue("rageDamage"), "SorceryPoints": attributeValue("sorceryPoints"), + + "DeathSavePasses": char.deathSave.pass, + "DeathSaveFails": char.deathSave.fail, + "DeathSaveStable": char.deathSave.stable, }; _.extend(character, getSkills(charId)); _.extend(character, getAttacks(charId)); @@ -107,6 +124,7 @@ var getHitDiceString = function(charId){ (d10 ? `${d10}d10 + ` : "") + (d12 ? `${d12}d12 + ` : "") + con; + return string; } var getArmorString = function(charId){ @@ -181,8 +199,13 @@ var getSkills = function(charId){ ]; var skills = {}; _.each(allSkills, skill => { - var value = Characters.calculate.skillMod(charId, skill.name); - skills[skill.name] = value; + var value = signedString( + Characters.calculate.skillMod(charId, skill.name) + ); + var prof = Characters.calculate.proficiency(charId, skill.name); + var name = skill.name.charAt(0).toUpperCase() + skill.name.slice(1); + skills[name] = value; + skills[name + "Proficiency"] = prof; }); return skills; }; @@ -212,4 +235,8 @@ var getAttacks = function(charId){ `${a.details}`; }); return attacks; -} +}; + +var signedString = function(number) { + return number >= 0 ? "+" + number : "" + number; +}; From 614284c73dc477aa626c5ed507e70fa32b4ef3f0 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 22 Sep 2017 12:52:21 +0200 Subject: [PATCH 07/25] Updated Meteor and some packages --- rpg-docs/.meteor/.finished-upgraders | 2 + rpg-docs/.meteor/packages | 30 +++---- rpg-docs/.meteor/release | 2 +- rpg-docs/.meteor/versions | 117 ++++++++++++++------------- 4 files changed, 80 insertions(+), 71 deletions(-) diff --git a/rpg-docs/.meteor/.finished-upgraders b/rpg-docs/.meteor/.finished-upgraders index aa607041..910574ce 100644 --- a/rpg-docs/.meteor/.finished-upgraders +++ b/rpg-docs/.meteor/.finished-upgraders @@ -13,3 +13,5 @@ notices-for-facebook-graph-api-2 1.3.0-split-minifiers-package 1.4.0-remove-old-dev-bundle-link 1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package diff --git a/rpg-docs/.meteor/packages b/rpg-docs/.meteor/packages index d3dffb6f..9baf9dae 100644 --- a/rpg-docs/.meteor/packages +++ b/rpg-docs/.meteor/packages @@ -4,7 +4,7 @@ # but you can also edit it by hand. iron:router -accounts-password@1.3.3 +accounts-password@1.4.0 accounts-ui@1.1.9 random@1.0.10 dburles:collection-helpers @@ -18,35 +18,37 @@ dburles:mongo-collection-instances percolate:migrations ecwyne:mathjs useraccounts:polymer -accounts-google@1.0.11 +accounts-google@1.2.0 splendido:accounts-meld -email@1.1.18 +email@1.2.3 meteorhacks:subs-manager chuangbo:marked reywood:iron-router-ga -meteor-base@1.0.4 +meteor-base@1.1.0 mobile-experience@1.0.4 -mongo@1.1.14 +mongo@1.2.0 blaze-html-templates session@1.1.7 jquery@1.11.10 -tracker@1.1.1 -logging@1.1.16 +tracker@1.1.3 +logging@1.1.17 reload@1.1.11 -ejson@1.0.13 +ejson@1.0.14 spacebars -check@1.2.4 +check@1.2.5 useraccounts:iron-routing wizonesolutions:canonical -standard-minifier-js@1.2.1 -shell-server@0.2.1 +standard-minifier-js@2.1.1 +shell-server@0.2.4 seba:minifiers-autoprefixer nikogosovd:multiple-uihooks templates:array -ecmascript@0.6.1 +ecmascript@0.8.2 es5-shim@4.6.15 differential:vulcanize -reactive-dict +reactive-dict@1.1.9 percolate:synced-cron ongoworks:speakingurl -service-configuration +service-configuration@1.0.11 +google-config-ui +dynamic-import diff --git a/rpg-docs/.meteor/release b/rpg-docs/.meteor/release index b7694ea3..47c31abc 100644 --- a/rpg-docs/.meteor/release +++ b/rpg-docs/.meteor/release @@ -1 +1 @@ -METEOR@1.4.2.6 +METEOR@1.5.2 diff --git a/rpg-docs/.meteor/versions b/rpg-docs/.meteor/versions index c2254df9..f0675aa8 100644 --- a/rpg-docs/.meteor/versions +++ b/rpg-docs/.meteor/versions @@ -1,53 +1,57 @@ -accounts-base@1.2.14 -accounts-google@1.0.11 +accounts-base@1.3.3 +accounts-google@1.2.0 accounts-oauth@1.1.15 -accounts-password@1.3.3 +accounts-password@1.4.0 accounts-ui@1.1.9 -accounts-ui-unstyled@1.1.13 +accounts-ui-unstyled@1.2.1 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 aldeed:schema-deny@1.1.0 aldeed:schema-index@1.1.1 aldeed:simple-schema@1.5.3 -allow-deny@1.0.5 +allow-deny@1.0.9 autoupdate@1.3.12 -babel-compiler@6.13.0 +babel-compiler@6.20.0 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 -blaze@2.3.0 -blaze-html-templates@1.1.0 +blaze@2.3.2 +blaze-html-templates@1.1.2 blaze-tools@1.0.10 -boilerplate-generator@1.0.11 +boilerplate-generator@1.2.0 caching-compiler@1.1.9 -caching-html-compiler@1.1.0 +caching-html-compiler@1.1.2 callback-hook@1.0.10 -check@1.2.4 +check@1.2.5 chuangbo:marked@0.3.5_1 coffeescript@1.11.1_4 dburles:collection-helpers@1.1.0 dburles:mongo-collection-instances@0.3.5 -ddp@1.2.5 -ddp-client@1.3.2 -ddp-common@1.2.8 -ddp-rate-limiter@1.0.6 -ddp-server@1.3.12 +ddp@1.3.1 +ddp-client@2.1.3 +ddp-common@1.2.9 +ddp-rate-limiter@1.0.7 +ddp-server@2.0.2 deps@1.0.12 diff-sequence@1.0.7 differential:vulcanize@3.0.0 -ecmascript@0.6.1 -ecmascript-runtime@0.3.15 +dynamic-import@0.1.3 +ecmascript@0.8.2 +ecmascript-runtime@0.4.1 +ecmascript-runtime-client@0.4.3 +ecmascript-runtime-server@0.4.1 ecwyne:mathjs@0.25.0 -ejson@1.0.13 -email@1.1.18 +ejson@1.0.14 +email@1.2.3 es5-shim@4.6.15 fastclick@1.0.13 geojson-utils@1.0.10 -google@1.1.15 +google-config-ui@1.0.0 +google-oauth@1.2.4 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 -http@1.2.10 +http@1.2.12 id-map@1.0.9 iron:controller@1.0.12 iron:core@1.0.11 @@ -55,45 +59,46 @@ iron:dynamic-template@1.0.12 iron:layout@1.0.12 iron:location@1.0.11 iron:middleware-stack@1.1.0 -iron:router@1.1.1 -iron:url@1.0.11 +iron:router@1.1.2 +iron:url@1.1.0 jquery@1.11.10 lai:collection-extensions@0.2.1_1 -launch-screen@1.1.0 +launch-screen@1.1.1 less@2.7.9 livedata@1.0.18 -localstorage@1.0.12 -logging@1.1.16 +localstorage@1.1.1 +logging@1.1.17 matb33:collection-hooks@0.8.4 mdg:validation-error@0.5.1 -meteor@1.6.0 -meteor-base@1.0.4 +meteor@1.7.2 +meteor-base@1.1.0 meteorhacks:subs-manager@1.6.4 minifier-css@1.2.16 -minifier-js@1.2.17 -minimongo@1.0.19 +minifier-js@2.1.3 +minimongo@1.3.1 mobile-experience@1.0.4 -mobile-status-bar@1.0.13 -modules@0.7.7 -modules-runtime@0.7.8 -momentjs:moment@2.17.1 -mongo@1.1.14 +mobile-status-bar@1.0.14 +modules@0.10.0 +modules-runtime@0.8.0 +momentjs:moment@2.18.1 +mongo@1.2.2 +mongo-dev-server@1.0.1 mongo-id@1.0.6 nikogosovd:multiple-uihooks@0.1.8 -npm-bcrypt@0.9.2 -npm-mongo@2.2.16_1 -oauth@1.1.12 +npm-bcrypt@0.9.3 +npm-mongo@2.2.30 +oauth@1.1.13 oauth2@1.1.11 -observe-sequence@1.0.14 +observe-sequence@1.0.16 ongoworks:speakingurl@9.0.0 ordered-dict@1.0.9 percolate:migrations@0.9.8 percolate:synced-cron@1.3.2 -promise@0.8.8 +promise@0.9.0 raix:eventemitter@0.1.3 random@1.0.10 -rate-limit@1.0.6 -reactive-dict@1.1.8 +rate-limit@1.0.8 +reactive-dict@1.1.9 reactive-var@1.0.11 reload@1.1.11 retry@1.0.9 @@ -103,27 +108,27 @@ seba:minifiers-autoprefixer@1.0.1 service-configuration@1.0.11 session@1.1.7 sha@1.0.9 -shell-server@0.2.1 -softwarerero:accounts-t9n@1.3.7 -spacebars@1.0.13 -spacebars-compiler@1.1.0 +shell-server@0.2.4 +softwarerero:accounts-t9n@1.3.11 +spacebars@1.0.15 +spacebars-compiler@1.1.3 splendido:accounts-emails-field@1.2.0 splendido:accounts-meld@1.3.1 srp@1.0.10 -standard-minifier-js@1.2.2 +standard-minifier-js@2.1.1 templates:array@1.0.3 -templating@1.3.0 -templating-compiler@1.3.0 -templating-runtime@1.3.0 -templating-tools@1.1.0 -tracker@1.1.1 -ui@1.0.12 +templating@1.3.2 +templating-compiler@1.3.3 +templating-runtime@1.3.2 +templating-tools@1.1.2 +tracker@1.1.3 +ui@1.0.13 underscore@1.0.10 -url@1.0.11 +url@1.1.0 useraccounts:core@1.14.2 useraccounts:iron-routing@1.14.2 useraccounts:polymer@1.14.2 -webapp@1.3.12 +webapp@1.3.19 webapp-hashing@1.0.9 wizonesolutions:canonical@0.0.5 zimme:collection-behaviours@1.1.3 From 750022f0f1a4c158a3167f927da04d4551ffaae7 Mon Sep 17 00:00:00 2001 From: Thaum Rystra Date: Sun, 24 Sep 2017 04:02:15 +0200 Subject: [PATCH 08/25] Added missing indices --- rpg-docs/Model/Character/Characters.js | 6 +++--- rpg-docs/lib/functions/parenting.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js index be848546..85778c30 100644 --- a/rpg-docs/Model/Character/Characters.js +++ b/rpg-docs/Model/Character/Characters.js @@ -164,9 +164,9 @@ Schemas.Character = new SimpleSchema({ //permissions party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}, - owner: {type: String, regEx: SimpleSchema.RegEx.Id}, - readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []}, - writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []}, + owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1}, + readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1}, + writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1}, color: { type: String, allowedValues: _.pluck(colorOptions, "key"), diff --git a/rpg-docs/lib/functions/parenting.js b/rpg-docs/lib/functions/parenting.js index 88da47a1..eb3e01f2 100644 --- a/rpg-docs/lib/functions/parenting.js +++ b/rpg-docs/lib/functions/parenting.js @@ -1,7 +1,7 @@ var childSchema = new SimpleSchema({ parent: {type: Object}, "parent.collection": {type: String}, - "parent.id": {type: String, regEx: SimpleSchema.RegEx.Id}, + "parent.id": {type: String, regEx: SimpleSchema.RegEx.Id, index: 1}, "parent.group": {type: String, optional: true}, "removedWith": { optional: true, From 877f516565e132e2e23c7f5aa064ebcab45293e0 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 26 Sep 2017 13:36:01 +0200 Subject: [PATCH 09/25] Added efficient computation of characters to replace heavy export functionality --- rpg-docs/lib/constants/abilities.js | 11 + rpg-docs/lib/constants/attributes.js | 34 ++++ rpg-docs/lib/constants/skills.js | 29 +++ rpg-docs/lib/functions/characterExport.js | 158 ++++++++------- rpg-docs/lib/functions/computeCharacter.js | 223 +++++++++++++++++++++ 5 files changed, 384 insertions(+), 71 deletions(-) create mode 100644 rpg-docs/lib/constants/attributes.js create mode 100644 rpg-docs/lib/functions/computeCharacter.js diff --git a/rpg-docs/lib/constants/abilities.js b/rpg-docs/lib/constants/abilities.js index 646d318a..085a0f13 100644 --- a/rpg-docs/lib/constants/abilities.js +++ b/rpg-docs/lib/constants/abilities.js @@ -6,3 +6,14 @@ abilities = [ "wisdom", "charisma", ]; + +ABILITIES = abilities; + +ABILITY_MODS = [ + "strengthMod", + "dexterityMod", + "constitutionMod", + "intelligenceMod", + "wisdomMod", + "charismaMod", +]; diff --git a/rpg-docs/lib/constants/attributes.js b/rpg-docs/lib/constants/attributes.js new file mode 100644 index 00000000..115e7f7d --- /dev/null +++ b/rpg-docs/lib/constants/attributes.js @@ -0,0 +1,34 @@ +ATTRIBUTES = [ + "strength", + "dexterity", + "constitution", + "intelligence", + "wisdom", + "charisma", + "hitPoints", + "tempHP", + "experience", + "proficiencyBonus", + "speed", + "armor", + "carryMultiplier", + "level1SpellSlots", + "level2SpellSlots", + "level3SpellSlots", + "level4SpellSlots", + "level5SpellSlots", + "level6SpellSlots", + "level7SpellSlots", + "level8SpellSlots", + "level9SpellSlots", + "ki", + "sorceryPoints", + "rages", + "superiorityDice", + "expertiseDice", + "rageDamage", + "d6HitDice", + "d8HitDice", + "d10HitDice", + "d12HitDice", +]; diff --git a/rpg-docs/lib/constants/skills.js b/rpg-docs/lib/constants/skills.js index d11b1af0..0a47069c 100644 --- a/rpg-docs/lib/constants/skills.js +++ b/rpg-docs/lib/constants/skills.js @@ -19,3 +19,32 @@ SKILLS = [ "survival", "initiative", ]; + +ALL_SKILLS = [ + "strengthSave", + "dexteritySave", + "constitutionSave", + "intelligenceSave", + "wisdomSave", + "charismaSave", + "acrobatics", + "animalHandling", + "arcana", + "athletics", + "deception", + "history", + "insight", + "intimidation", + "investigation", + "medicine", + "nature", + "perception", + "performance", + "persuasion", + "religion", + "sleightOfHand", + "stealth", + "survival", + "initiative", + "dexterityArmor", +]; diff --git a/rpg-docs/lib/functions/characterExport.js b/rpg-docs/lib/functions/characterExport.js index dd8e95c5..fe56c6ae 100644 --- a/rpg-docs/lib/functions/characterExport.js +++ b/rpg-docs/lib/functions/characterExport.js @@ -1,5 +1,11 @@ characterExport = function(charId){ - var char = Characters.findOne(charId); + var { + character, classes, effects, proficiencies, + } = getCharacterForComputation(charId); + var char = character; + computedCharacter = computeCharacter({ + character, classes, effects, proficiencies, + }); if (!char) { return { error: charId + " character not found" @@ -11,25 +17,87 @@ characterExport = function(charId){ }; } var baseValue = function(attributeName){ - return Characters.calculate.attributeBase(charId, attributeName); + var attribute = computedCharacter[attributeName]; + return attribute && attribute.value; }; var attributeValue = function(attributeName){ - return Characters.calculate.attributeValue(charId, attributeName); + var base = baseValue(attributeName); + var adjustment = char[attributeName] && char[attributeName].adjustment; + return base + adjustment; }; var abilityMod = function(attributeName){ - return signedString( - Characters.calculate.abilityMod(charId, attributeName) - ); + return signedString(getMod(attributeValue(attributeName))); }; var skillMod = function(skillName){ - return signedString( - Characters.calculate.skillMod(charId, skillName) - ); + return signedString(baseValue(skillName)); }; var proficiency = function(skillName){ - return Characters.calculate.proficiency(charId, skillName); + var skill = computedCharacter[skillName]; + return skill && skill.proficiency; + }; + var passiveSkill = function(skillName){ + var attribute = computedCharacter[skillName]; + if (!attribute) return; + return 10 + baseValue(skillName) + attribute.passiveAdd; + }; + var experience = function(){ + var xp = 0; + Experiences.find( + {charId: charId}, + {fields: {value: 1}} + ).forEach(function(e){ + xp += e.value; + }); + return xp; + }; + var getClasses = function(){ + return _.map(classes, c => `${c.name} ${c.level}`).join(", "); + }; + var getHitDiceString = function(){ + var d6 = baseValue("d6HitDice"); + var d8 = baseValue("d8HitDice"); + var d10 = baseValue("d10HitDice"); + var d12 = baseValue("d12HitDice"); + var con = abilityMod("constitution"); + var string = "" + + (d6 ? `${d6}d6 + ` : "") + + (d8 ? `${d8}d8 + ` : "") + + (d10 ? `${d10}d10 + ` : "") + + (d12 ? `${d12}d12 + ` : "") + + con; + return string; } - var damageMods = getDamageMods(charId); + var getSkills = function(charId){ + var allSkills = [ + {name: "acrobatics", attribute: "dexterity"}, + {name: "animalHandling", attribute: "wisdom"}, + {name: "arcana", attribute: "intelligence"}, + {name: "athletics", attribute: "strength"}, + {name: "deception", attribute: "charisma"}, + {name: "history", attribute: "intelligence"}, + {name: "insight", attribute: "wisdom"}, + {name: "intimidation", attribute: "charisma"}, + {name: "investigation", attribute: "intelligence"}, + {name: "medicine", attribute: "wisdom"}, + {name: "nature", attribute: "intelligence"}, + {name: "perception", attribute: "wisdom"}, + {name: "performance", attribute: "charisma"}, + {name: "persuasion", attribute: "charisma"}, + {name: "religion", attribute: "intelligence"}, + {name: "sleightOfHand", attribute: "dexterity"}, + {name: "stealth", attribute: "dexterity"}, + {name: "survival", attribute: "wisdom"}, + ]; + var skills = {}; + _.each(allSkills, skill => { + var value = skillMod(skill.name); + var prof = proficiency(skill.name); + var name = skill.name.charAt(0).toUpperCase() + skill.name.slice(1); + skills[name] = value; + skills[name + "Proficiency"] = prof; + }); + return skills; + }; var character = { "Id": char._id, "Name": char.name, @@ -37,8 +105,8 @@ characterExport = function(charId){ "Alignment": char.alignment || "", "Gender": char.gender || "", "Race": char.race || "", - "Level": Characters.calculate.level(charId), - "Experience": Characters.calculate.experience(charId), + "Level": _.reduce(classes, (memo, cls) => memo + cls.level, 0), + "Experience": experience(), "Class": getClasses(charId), "HPBase": baseValue("hitPoints"), "HPValue": attributeValue("hitPoints"), @@ -47,7 +115,7 @@ characterExport = function(charId){ "Initiative": skillMod("initiative"), "Speed": attributeValue("speed"), "ProficiencyBonus": attributeValue("proficiencyBonus"), - "passivePerception": Characters.calculate.passiveSkill(charId, "perception"), + "passivePerception": passiveSkill("perception"), "Languages": getLanguages(charId), "Description": char.description || "", @@ -72,9 +140,9 @@ characterExport = function(charId){ "WisdomMod": abilityMod("wisdom"), "CharismaMod": abilityMod("charisma"), - "DamageVulnerabilities": damageMods.vulnerabilities, - "DamageResistances": damageMods.resistances, - "DamageImmunities": damageMods.immunities, + //"DamageVulnerabilities": damageMods.vulnerabilities, + //"DamageResistances": damageMods.resistances, + //"DamageImmunities": damageMods.immunities, "StrengthSave": skillMod("strengthSave"), "StrengthSaveProficiency": proficiency("strengthSave"), @@ -112,21 +180,6 @@ characterExport = function(charId){ return character; } -var getHitDiceString = function(charId){ - var d6 = Characters.calculate.attributeBase(charId, "d6HitDice"); - var d8 = Characters.calculate.attributeBase(charId, "d8HitDice"); - var d10 = Characters.calculate.attributeBase(charId, "d10HitDice"); - var d12 = Characters.calculate.attributeBase(charId, "d12HitDice"); - var con = Characters.calculate.abilityMod(charId,"constitution"); - var string = "" + - (d6 ? `${d6}d6 + ` : "") + - (d8 ? `${d8}d8 + ` : "") + - (d10 ? `${d10}d10 + ` : "") + - (d12 ? `${d12}d12 + ` : "") + - con; - return string; -} - var getArmorString = function(charId){ var bases = Effects.find({ charId: charId, @@ -148,7 +201,7 @@ var getArmorString = function(charId){ strings = strings.concat(effects); return strings.join(", "); } - +/* var getDamageMods = function(charId){ // jscs:disable maximumLineLength var multipliers = [ @@ -175,40 +228,7 @@ var getDamageMods = function(charId){ "vulnerabilities": _.map(multipliers["2"], names).join(", "), }; } - -var getSkills = function(charId){ - var allSkills = [ - {name: "acrobatics", attribute: "dexterity"}, - {name: "animalHandling", attribute: "wisdom"}, - {name: "arcana", attribute: "intelligence"}, - {name: "athletics", attribute: "strength"}, - {name: "deception", attribute: "charisma"}, - {name: "history", attribute: "intelligence"}, - {name: "insight", attribute: "wisdom"}, - {name: "intimidation", attribute: "charisma"}, - {name: "investigation", attribute: "intelligence"}, - {name: "medicine", attribute: "wisdom"}, - {name: "nature", attribute: "intelligence"}, - {name: "perception", attribute: "wisdom"}, - {name: "performance", attribute: "charisma"}, - {name: "persuasion", attribute: "charisma"}, - {name: "religion", attribute: "intelligence"}, - {name: "sleightOfHand", attribute: "dexterity"}, - {name: "stealth", attribute: "dexterity"}, - {name: "survival", attribute: "wisdom"}, - ]; - var skills = {}; - _.each(allSkills, skill => { - var value = signedString( - Characters.calculate.skillMod(charId, skill.name) - ); - var prof = Characters.calculate.proficiency(charId, skill.name); - var name = skill.name.charAt(0).toUpperCase() + skill.name.slice(1); - skills[name] = value; - skills[name + "Proficiency"] = prof; - }); - return skills; -}; +*/ var getLanguages = function(charId){ return Proficiencies.find({ @@ -218,10 +238,6 @@ var getLanguages = function(charId){ }).map(l => l.name).join(", "); }; -var getClasses = function(charId){ - return Classes.find({charId}).map(c => `${c.name} ${c.level}`).join(", "); -}; - var getAttacks = function(charId){ var attacks = {}; var i = 1; diff --git a/rpg-docs/lib/functions/computeCharacter.js b/rpg-docs/lib/functions/computeCharacter.js new file mode 100644 index 00000000..920e6fa8 --- /dev/null +++ b/rpg-docs/lib/functions/computeCharacter.js @@ -0,0 +1,223 @@ +getCharacterForComputation = function(charId){ + const character = Characters.findOne(charId); + const classes = Classes.find({charId}).fetch(); + const effects = Effects.find({charId, enabled: true}).fetch(); + const proficiencies = Proficiencies.find({ + charId, + enabled: true, + type: {$in: ["skill", "save"]}, + }).fetch(); + return {character, classes, effects, proficiencies}; +} + +computeCharacter = function({character, classes, effects, proficiencies}){ + let computedClasses = computeCharacterClasses(charId, classes); + let changed = false; + computedCharacter = {}; + let i; + for (i = 0; i < 15; i++){ + [computedCharacter, changed] = compute({ + classes: computedClasses, + oldChar: computedCharacter, + charId, + character, + effects, + proficiencies, + }); + if (!changed) break; + } + return computedCharacter; +}; + +var ensureCharacterExists = (character) => { + if (!character) { + throw new Meteor.Error("Character doesn't exist", + "You can't recompute a character that doesn't exist"); + } +}; + +var ensureWritePermissions = (character, userId) => { + if ( + userId && + userId !== character.owner && + !_.contains(character.writers, userId) + ){ + throw new Meteor.Error("Character write denied", + "You don't have permission to recompute this character"); + } +}; + +var computeCharacterClasses = function(charId, classes){ + let computedClasses = {}; + _.each(classes, (cls) => { + if (computedClasses[cls.name]){ + computedClasses[cls.name].level += cls.level; + } else { + computedClasses[cls.name] = cls; + } + }); + return computedClasses; +} + +var compute = function({ + charId, oldChar, character, classes, effects, proficiencies, +}){ + let newChar = {}; + _.each(effects, (effect, index) => { + if (!effect.stat || effect.operation === "conditional") return; + if (!newChar[effect.stat]) newChar[effect.stat] = defaultStat(); + let value = effect.calculation ? + computeEffect(effect.calculation, oldChar, classes) : + effect.value || 0; + let stat = newChar[effect.stat]; + if (!_.isNumber(value)) return; + switch (effect.operation) { + case "base": + if (value > stat.base) stat.base = value; + break; + case "proficiency": + if (value > stat.proficiency) stat.proficiency = value; + break; + case "add": + stat.add += value; + break; + case "mul": + stat.mul *= value; + break; + case "min": + if (value > stat.min) stat.min = value; + break; + case "max": + if (value < stat.max) stat.max = value; + break; + case "advantage": + stat.advantage++; + break; + case "disadvantage": + stat.disadvantage++; + break; + case "passiveAdd": + stat.passiveAdd += value; + break; + case "fail": + stat.fail = true; + break; + } + }); + _.each(proficiencies, proficiency => { + if (!proficiency.name) return; + if (!newChar[proficiency.name]) newChar[proficiency.name] = defaultStat(); + let stat = newChar[proficiency.name]; + let value = proficiency.value; + if (value > stat.proficiency) stat.proficiency = value; + }); + let changed = false; + _.each(ATTRIBUTES, function(statName) { + if (!newChar[statName]) newChar[statName] = defaultStat(); + let stat = newChar[statName]; + stat.value = (stat.base + stat.add) * stat.mul; + if (stat.value < stat.min) stat.value = stat.min; + if (stat.value > stat.max) stat.value = stat.max; + if (!_.isEqual(stat.value, oldChar[statName] && oldChar[statName].value)){ + changed = true; + } + }); + _.each(ALL_SKILLS, function(statName) { + if (!newChar[statName]) newChar[statName] = defaultStat(); + let stat = newChar[statName]; + stat.value = characterAbilityMod( + oldChar, character[statName] && character[statName].ability + ); + stat.value += stat.base + stat.add; + stat.value += stat.proficiency * + characterFieldValue(oldChar, "proficiencyBonus"); + stat.value *= stat.mul; + if (stat.value < stat.min) stat.value = stat.min; + if (stat.value > stat.max) stat.value = stat.max; + if (!_.isEqual(stat.value, oldChar[statName] && oldChar[statName].value)){ + changed = true; + } + }); + return [newChar, changed]; +}; + +var defaultStat = function(){ + return { + base: 0, + proficiency: 0, + add: 0, + mul: 1, + min: Number.NEGATIVE_INFINITY, + max: Number.POSITIVE_INFINITY, + advantage: 0, + disadvantage: 0, + passiveAdd: 0, + fail: false, + } +} + +var computeEffect = function(string, character, classes){ + if (!string) return string; + string = string.replace(/\b[a-z][\w]+/gi, function(sub){ + //fields + if (character[sub]){ + return characterFieldValue(character, sub); + } + //ability modifiers + if (_.contains(ABILITY_MODS, sub)){ + var slice = sub.slice(0, -3); + return getMod( + character[slice] ? characterFieldValue(character, slice) : 0 + ); + } + //class levels + if (/\w+levels?\b/gi.test(sub)){ + //strip out "level" + var className = sub.replace(/levels?\b/gi, ""); + return characterClassLevel(classes, className) + } + //character level + if (sub.toUpperCase() === "LEVEL"){ + return characterTotalLevel(classes); + } + // exclude math functions + if (math[sub]){ + return sub; + } + return 0; + }); + try { + var result = math.eval(string); + return result; + } catch (e){ + return string; + } +}; + +var characterFieldValue = function(character, field){ + if (_.isNumber(character[field] && character[field].value)){ + return character[field].value; + } else { + return field; + } +}; + +var characterClassLevel = function(classes, className){ + if (_.isNumber(classes[className] && classes[className].level)){ + return classes[className].level; + } else { + return className; + } +}; + +var characterTotalLevel = function(classes){ + return _.reduce(classes, (memo, cls) => memo + cls.level, 0); +}; + +var characterAbilityMod = function(character, abilityName){ + if (_.isNumber(character[abilityName] && character[abilityName].value)){ + return getMod(character[abilityName].value); + } else { + return 0; + } +}; From 212986ac37c5d4fb05e36db34c229c4ca3b851c1 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 26 Sep 2017 13:54:33 +0200 Subject: [PATCH 10/25] Fixed missing charId --- rpg-docs/lib/functions/computeCharacter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rpg-docs/lib/functions/computeCharacter.js b/rpg-docs/lib/functions/computeCharacter.js index 920e6fa8..535ed9cd 100644 --- a/rpg-docs/lib/functions/computeCharacter.js +++ b/rpg-docs/lib/functions/computeCharacter.js @@ -11,6 +11,7 @@ getCharacterForComputation = function(charId){ } computeCharacter = function({character, classes, effects, proficiencies}){ + var charId = character._id; let computedClasses = computeCharacterClasses(charId, classes); let changed = false; computedCharacter = {}; From 4e96047e908fae11b0eff08466bda329dd51ca7e Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 26 Sep 2017 14:59:41 +0200 Subject: [PATCH 11/25] Added rate limiting to heavy subscriptions --- rpg-docs/.meteor/packages | 1 + rpg-docs/Model/Character/Characters.js | 1 + rpg-docs/server/publications/characterList.js | 7 +++++++ rpg-docs/server/publications/singleCharacter.js | 16 +++++++++++----- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/rpg-docs/.meteor/packages b/rpg-docs/.meteor/packages index 9baf9dae..0a0338e8 100644 --- a/rpg-docs/.meteor/packages +++ b/rpg-docs/.meteor/packages @@ -52,3 +52,4 @@ ongoworks:speakingurl service-configuration@1.0.11 google-config-ui dynamic-import +ddp-rate-limiter diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js index 85778c30..740d7cea 100644 --- a/rpg-docs/Model/Character/Characters.js +++ b/rpg-docs/Model/Character/Characters.js @@ -185,6 +185,7 @@ Schemas.Character = new SimpleSchema({ type: String, defaultValue: "whitelist", allowedValues: ["whitelist", "public"], + index: 1, }, "settings.swapStatAndModifier": {type: Boolean, defaultValue: false}, "settings.exportFeatures": {type: Boolean, defaultValue: true}, diff --git a/rpg-docs/server/publications/characterList.js b/rpg-docs/server/publications/characterList.js index a33dd71d..cfabd4c4 100644 --- a/rpg-docs/server/publications/characterList.js +++ b/rpg-docs/server/publications/characterList.js @@ -25,3 +25,10 @@ Meteor.publish("characterList", function(){ Parties.find({owner: userId}), ]; }); + +DDPRateLimiter.addRule({ + name: "characterList", + type: "subscription", + userId(){ return true; }, + connectionId(){ return true; }, +}, 8, 5000); diff --git a/rpg-docs/server/publications/singleCharacter.js b/rpg-docs/server/publications/singleCharacter.js index 771375b6..32e6f6a6 100644 --- a/rpg-docs/server/publications/singleCharacter.js +++ b/rpg-docs/server/publications/singleCharacter.js @@ -35,9 +35,16 @@ Meteor.publish("singleCharacter", function(characterId){ } }); +DDPRateLimiter.addRule({ + name: "singleCharacter", + type: "subscription", + userId(){ return true; }, + connectionId(){ return true; }, +}, 8, 5000); + Meteor.publish("singleCharacterName", function(characterId){ userId = this.userId; - var char = Characters.findOne({ + return Characters.find({ _id: characterId, $or: [ {readers: userId}, @@ -45,8 +52,7 @@ Meteor.publish("singleCharacterName", function(characterId){ {owner: userId}, {"settings.viewPermission": "public"}, ], + }, { + fields:{"name": 1} }); - if (char) { - return Characters.find(characterId, {fields:{"name": 1}}); - } -}); \ No newline at end of file +}); From 44da62a96223feb8b533c7509bbdf66fbb52770e Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 26 Sep 2017 15:11:03 +0200 Subject: [PATCH 12/25] Lowered subscription caching to improve performance --- rpg-docs/lib/constants/subsManager.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpg-docs/lib/constants/subsManager.js b/rpg-docs/lib/constants/subsManager.js index 6e5de50d..c8593307 100644 --- a/rpg-docs/lib/constants/subsManager.js +++ b/rpg-docs/lib/constants/subsManager.js @@ -1 +1,6 @@ -subsManager = new SubsManager(); +subsManager = new SubsManager({ + // maximum number of cache subscriptions + cacheLimit: 5, + // any subscription will be expire after 1 minute, if it's not subscribed again + expireIn: 1, +}); From f6b2dde479f55e59a6ac6d7762eff37ae65fd6c0 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 27 Sep 2017 16:19:00 +0200 Subject: [PATCH 13/25] Added basic onboarding steps --- rpg-docs/Model/Character/Characters.js | 5 + rpg-docs/client/style/bounce.css | 17 + .../views/character/characterSheet.html | 7 +- .../client/views/character/characterSheet.js | 26 +- .../features/featureDialog/featureDialog.html | 13 +- .../features/featureDialog/featureDialog.js | 3 + .../views/character/features/features.html | 2 +- .../views/character/features/features.js | 4 + .../views/character/journal/journal.html | 2 +- .../client/views/character/journal/journal.js | 3 + .../newUserStepper/newUserStepper.css | 12 + .../newUserStepper/newUserStepper.html | 26 + .../newUserStepper/newUserStepper.js | 41 + .../views/paperTemplates/infoBox/infoBox.css | 15 + .../views/paperTemplates/infoBox/infoBox.html | 10 + rpg-docs/config.vulcanize | 1 + rpg-docs/lib/constants/useraccountsConfig.js | 18 +- .../fade-in-slide-from-left-animation.html | 45 ++ .../fade-in-slide-from-right-animation.html | 45 ++ .../fade-out-slide-left-animation.html | 45 ++ .../fade-out-slide-right-animation.html | 45 ++ .../paper-stepper/bower.json | 48 ++ .../custom_components/paper-stepper/hero.svg | 27 + .../paper-stepper/paper-step.html | 424 ++++++++++ .../paper-stepper/paper-stepper.html | 735 ++++++++++++++++++ .../paper-stepper/step-horizontal-label.html | 124 +++ .../paper-stepper/step-label-behavior.html | 58 ++ .../step-label-shared-styles.html | 74 ++ .../paper-stepper/step-vertical.html | 177 +++++ 29 files changed, 2036 insertions(+), 16 deletions(-) create mode 100644 rpg-docs/client/style/bounce.css create mode 100644 rpg-docs/client/views/character/newUserStepper/newUserStepper.css create mode 100644 rpg-docs/client/views/character/newUserStepper/newUserStepper.html create mode 100644 rpg-docs/client/views/character/newUserStepper/newUserStepper.js create mode 100644 rpg-docs/client/views/paperTemplates/infoBox/infoBox.css create mode 100644 rpg-docs/client/views/paperTemplates/infoBox/infoBox.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/animations/fade-in-slide-from-left-animation.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/animations/fade-in-slide-from-right-animation.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/animations/fade-out-slide-left-animation.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/animations/fade-out-slide-right-animation.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/bower.json create mode 100644 rpg-docs/public/custom_components/paper-stepper/hero.svg create mode 100644 rpg-docs/public/custom_components/paper-stepper/paper-step.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/paper-stepper.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/step-horizontal-label.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/step-label-behavior.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/step-label-shared-styles.html create mode 100644 rpg-docs/public/custom_components/paper-stepper/step-vertical.html diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js index 740d7cea..50b7156b 100644 --- a/rpg-docs/Model/Character/Characters.js +++ b/rpg-docs/Model/Character/Characters.js @@ -191,6 +191,7 @@ Schemas.Character = new SimpleSchema({ "settings.exportFeatures": {type: Boolean, defaultValue: true}, "settings.exportAttacks": {type: Boolean, defaultValue: true}, "settings.exportDescription": {type: Boolean, defaultValue: true}, + "settings.newUserExperience": {type: Boolean, optional: true}, }); Characters.attachSchema(Schemas.Character); @@ -554,6 +555,10 @@ if (Meteor.isServer){ }); Characters.before.insert(function(userId, doc) { doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-"; + // The first character a user creates should have the new user experience + if (!Characters.find({owner: userId}).count()){ + doc.settings.newUserExperience = true; + } }); } diff --git a/rpg-docs/client/style/bounce.css b/rpg-docs/client/style/bounce.css new file mode 100644 index 00000000..481754d5 --- /dev/null +++ b/rpg-docs/client/style/bounce.css @@ -0,0 +1,17 @@ +@keyframes bounce { + from { + transform: translate(0px,0px); + } + to { + transform: translate(0px,-16px); + } +} + +.bounce{ + animation-name: bounce; + animation-duration: 0.3s; + animation-direction: alternate; + animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); + animation-delay: 0s; + animation-iteration-count: infinite; +} diff --git a/rpg-docs/client/views/character/characterSheet.html b/rpg-docs/client/views/character/characterSheet.html index 9fd7a65f..ca18dbfd 100644 --- a/rpg-docs/client/views/character/characterSheet.html +++ b/rpg-docs/client/views/character/characterSheet.html @@ -44,17 +44,18 @@
- Stats - Features + Stats + Features Inventory {{#unless hideSpellcasting}} Spells {{/unless}} Persona - Journal + Journal
+ {{#if session "showNewUserExperience"}}{{> newUserStepper}}{{/if}}
diff --git a/rpg-docs/client/views/character/characterSheet.js b/rpg-docs/client/views/character/characterSheet.js index 528cf42d..bd14795a 100644 --- a/rpg-docs/client/views/character/characterSheet.js +++ b/rpg-docs/client/views/character/characterSheet.js @@ -29,7 +29,17 @@ Template.characterSheet.onRendered(function() { tabFabMenus = _.times(6, (n) => tabPages[n].find(".mini-holder") ); - }) + }); + + // New user experience starts on the features tab + var settings = Characters.findOne(this.data._id, { + fields: {settings: 1} + }).settings; + if (settings && settings.newUserExperience){ + Session.set(this.data._id + ".selectedTab", "1"); + Session.set("showNewUserExperience", true); + Session.set("newUserExperienceStep", 0); + } //watch this character and make sure their encumbrance is updated //trackEncumbranceConditions(this.data._id, this); @@ -172,6 +182,20 @@ Template.characterSheet.helpers({ var char = Characters.findOne(this._id); return char && char.settings.hideSpellcasting; }, + newUserExperience: function(){ + var char = Characters.findOne(this._id); + return char && char.settings.newUserExperience; + }, + shouldBounce: function(tab){ + console.log(this._id); + const selected = Session.get(this._id + ".selectedTab") + const step = Session.get("newUserExperienceStep"); + console.log({selected, step, tab}); + if (selected == tab) return false; + return (tab === 1 && step === 0) || + (tab === 5 && step === 1) || + (tab === 0 && step === 2); + }, }); Template.characterSheet.events({ diff --git a/rpg-docs/client/views/character/features/featureDialog/featureDialog.html b/rpg-docs/client/views/character/features/featureDialog/featureDialog.html index c73a988c..187e2216 100644 --- a/rpg-docs/client/views/character/features/featureDialog/featureDialog.html +++ b/rpg-docs/client/views/character/features/featureDialog/featureDialog.html @@ -42,9 +42,20 @@ diff --git a/rpg-docs/client/views/character/inventory/inventory.html b/rpg-docs/client/views/character/inventory/inventory.html index cf823d5f..c135c6ec 100644 --- a/rpg-docs/client/views/character/inventory/inventory.html +++ b/rpg-docs/client/views/character/inventory/inventory.html @@ -110,12 +110,12 @@
{{round totalWeight}} lbs
-
+
- Container carried + {{#simpleTooltip}} Container carried{{/simpleTooltip}}
@@ -136,21 +136,21 @@ class="addContainer" mini> - New container + {{#simpleTooltip class="always"}} Container {{/simpleTooltip}}
- Library item + {{#simpleTooltip class="always"}} Item from library {{/simpleTooltip}}
- New item + {{#simpleTooltip class="always"}} Item {{/simpleTooltip}}
{{/fabMenu}} {{/if}} diff --git a/rpg-docs/client/views/character/journal/journal.html b/rpg-docs/client/views/character/journal/journal.html index 5ae06ced..8632954c 100644 --- a/rpg-docs/client/views/character/journal/journal.html +++ b/rpg-docs/client/views/character/journal/journal.html @@ -83,9 +83,12 @@
{{#if canEditCharacter _id}} - +
+ + + {{#simpleTooltip}}Add Note{{/simpleTooltip}} +
{{/if}} diff --git a/rpg-docs/client/views/character/spells/spells.html b/rpg-docs/client/views/character/spells/spells.html index b5011f82..c992daa4 100644 --- a/rpg-docs/client/views/character/spells/spells.html +++ b/rpg-docs/client/views/character/spells/spells.html @@ -53,22 +53,22 @@ {{numPrepared}} / {{evaluate charId maxPrepared}} {{/if}} -
- - Done - +
+ {{#simpleTooltip}} + Done + {{/simpleTooltip}}
{{else}} -
- - Change prepared spells - +
+ {{#simpleTooltip}} + Change prepared spells + {{/simpleTooltip}}
{{/if}}
@@ -124,32 +124,31 @@ {{#if canEditCharacter _id}} {{#fabMenu}}
- - New spell list - - + {{#simpleTooltip class="always"}} + Spell list + {{/simpleTooltip}}
- - Spell library - + {{#simpleTooltip class="always"}} + Spell from library + {{/simpleTooltip}}
- - New spell - + {{#simpleTooltip class="always"}} + Spell + {{/simpleTooltip}}
{{/fabMenu}} {{/if}} diff --git a/rpg-docs/client/views/character/stats/attributeDialog/attributeDialog.js b/rpg-docs/client/views/character/stats/attributeDialog/attributeDialog.js index 1e2bf9a3..202df998 100644 --- a/rpg-docs/client/views/character/stats/attributeDialog/attributeDialog.js +++ b/rpg-docs/client/views/character/stats/attributeDialog/attributeDialog.js @@ -157,4 +157,10 @@ Template.attributeDialogView.helpers({ statValue: function(){ return evaluateEffect(this.charId, this); }, + showNewUserExperience: function(){ + console.log(this.statName); + if (this.statName === "speed"){ + return Session.get("newUserExperienceStep") >= 2; + } + }, }); diff --git a/rpg-docs/client/views/characterList/characterList.html b/rpg-docs/client/views/characterList/characterList.html index a5b00dd2..94962a5b 100644 --- a/rpg-docs/client/views/characterList/characterList.html +++ b/rpg-docs/client/views/characterList/characterList.html @@ -49,14 +49,14 @@ class="addParty" mini> - New Party + {{#simpleTooltip class="always"}} New Party {{/simpleTooltip}}
- New Character + {{#simpleTooltip class="always"}} New Character {{/simpleTooltip}}
{{/fabMenu}} diff --git a/rpg-docs/client/views/paperTemplates/inputSuffixes/inputSuffixes.html b/rpg-docs/client/views/paperTemplates/inputSuffixes/inputSuffixes.html index 9cd48f6b..08c15e04 100644 --- a/rpg-docs/client/views/paperTemplates/inputSuffixes/inputSuffixes.html +++ b/rpg-docs/client/views/paperTemplates/inputSuffixes/inputSuffixes.html @@ -25,7 +25,6 @@ {{/ simpleTooltip}}
- {{# simpleTooltip}} This field accepts formulae in {curly brackets} diff --git a/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.css b/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.css index 39bb2cc3..67822bb6 100644 --- a/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.css +++ b/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.css @@ -1,4 +1,18 @@ -.simple-tooltip:hover .tooltip { +.simple-tooltip { + pointer-events: none; +} + +.simple-tooltip:active { + pointer-events: none; +} + +/* Show the tooltip if a older sibling is hovered */ +*:hover ~ .simple-tooltip > .tooltip { + opacity: 0.9; +} + +/* Show the tooltip if parent is hovered */ +*:hover > .simple-tooltip > .tooltip { opacity: 0.9; } @@ -16,3 +30,7 @@ pointer-events: none; white-space: nowrap; } + +.tooltip.always { + opacity: 0.9; +} diff --git a/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.html b/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.html index 961f80f4..f364ba71 100644 --- a/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.html +++ b/rpg-docs/client/views/paperTemplates/simpleTooltip/simpleTooltip.html @@ -1,6 +1,6 @@