diff --git a/test/client-old/spec/services/statServicesSpec.js b/test/client-old/spec/services/statServicesSpec.js index a1d99809c2..e1a9ad0028 100644 --- a/test/client-old/spec/services/statServicesSpec.js +++ b/test/client-old/spec/services/statServicesSpec.js @@ -67,59 +67,6 @@ describe('Stats Service', function() { }); }); - describe('classBonus', function() { - it('calculates class bonus', function() { - var equippedGear = { - "weapon" : "weapon_warrior_1", - "shield" : "shield_warrior_1", - "head" : "head_warrior_1", - "armor" : "armor_warrior_1" - }; - var user = { - fns: { - statsComputed: function () { - return { str: 50 }; - }, - }, - stats: { - lvl: 10, - buffs: { str: 10 }, - str: 10 - }, - items: { - gear: { equipped: equippedGear } - } - }; - var stat = 'str'; - var classBonus = statCalc.classBonus(user, stat); - - expect(classBonus).to.eql(20) - }); - - it('does not return value if user has not been wrapped (_statComputed)', function() { - var equippedGear = { - "weapon" : "weapon_warrior_1", - "shield" : "shield_warrior_1", - "head" : "head_warrior_1", - "armor" : "armor_warrior_1" - }; - var user = { - stats: { - lvl: 10, - buffs: { str: 10 }, - str: 10 - }, - items: { - gear: { equipped: equippedGear } - } - }; - var stat = 'str'; - var classBonus = statCalc.classBonus(user, stat); - - expect(classBonus).to.not.exist; - }); - }); - describe('expDisplay', function() { it('displays exp as "exp / toNextLevelExp"', function() { user.stats.exp = 10; @@ -138,27 +85,6 @@ describe('Stats Service', function() { }); }); - describe('equipmentStatBonus', function() { - it('tallies up stats from equipment that is equipped', function() { - var equippedGear = { - "weapon" : "weapon_special_1", - "shield" : "shield_special_1", - "head" : "head_special_1", - "armor" : "armor_special_1" - }; - - var strStat = statCalc.equipmentStatBonus('str', equippedGear); - var conStat = statCalc.equipmentStatBonus('con', equippedGear); - var intStat = statCalc.equipmentStatBonus('int', equippedGear); - var perStat = statCalc.equipmentStatBonus('per', equippedGear); - - expect(strStat).to.eql(24); - expect(conStat).to.eql(24); - expect(intStat).to.eql(24); - expect(perStat).to.eql(24); - }); - }); - describe('goldDisplay', function() { it('displays gold', function() { var gold = 30; @@ -192,32 +118,6 @@ describe('Stats Service', function() { }); }); - describe('levelBonus', function() { - it('calculates bonus as half of level for even numbered level under 100', function() { - var level = 50; - var bonus = statCalc.levelBonus(level); - expect(bonus).to.eql(25); - }); - - it('calculates bonus as half of level, rounded down, for odd numbered level under 100', function() { - var level = 51; - var bonus = statCalc.levelBonus(level); - expect(bonus).to.eql(25); - }); - - it('calculates bonus as 50 for levels >= 100', function() { - var level = 150; - var bonus = statCalc.levelBonus(level); - expect(bonus).to.eql(50); - }); - - it('calculates bonus as 0 for level 1', function() { - var level = 1; - var bonus = statCalc.levelBonus(level); - expect(bonus).to.eql(0); - }); - }); - describe('mountMasterProgress', function() { it('counts drop mounts that user has', function() { user.items.mounts = { diff --git a/test/common/fns/statsComputed.test.js b/test/common/fns/statsComputed.test.js index 5e53b11f2c..56f9633233 100644 --- a/test/common/fns/statsComputed.test.js +++ b/test/common/fns/statsComputed.test.js @@ -25,4 +25,71 @@ describe('common.fns.statsComputed', () => { expect(result.str).to.eql(0); expect(result.maxMP).to.eql(30); }); + + it('calculates stat bonuses for equipment', () => { + user.items.gear.equipped.weapon = 'weapon_rogue_1'; + let result = statsComputed(user); + + expect(result.str).to.eql(2); + expect(result.gearBonus.str).to.eql(2); + }); + + it('calculates stat bonuses for class', () => { + user.items.gear.equipped.weapon = 'weapon_warrior_1'; + let result = statsComputed(user); + + expect(result.str).to.eql(4.5); + expect(result.gearBonus.str).to.eql(3); + expect(result.classBonus.str).to.eql(1.5); + }); + + it('calculates stat bonuses for level', () => { + user.stats.lvl = 25; + let result = statsComputed(user); + + expect(result.str).to.eql(12); + expect(result.levelBonus.str).to.eql(12); + }); + + it('correctly caps level stat bonuses', () => { + user.stats.lvl = 150; + let result = statsComputed(user); + + expect(result.str).to.eql(50); + expect(result.levelBonus.str).to.eql(50); + }); + + it('sets baseStat field', () => { + user.stats.str = 20; + let result = statsComputed(user); + + expect(result.str).to.eql(20); + expect(result.baseStat.str).to.eql(20); + }); + + it('sets buffs field', () => { + user.stats.buffs.str = 150; + let result = statsComputed(user); + + expect(result.str).to.eql(150); + expect(result.buff.str).to.eql(150); + }); + + it('calculates mp from intelligence', () => { + user.stats.int = 150; + user.stats.buffs.int = 50; + let result = statsComputed(user); + + expect(result.maxMP).to.eql(430); + }); + + it('calculates stat bonuses for back equipment', () => { + user.items.gear.equipped.back = 'back_special_takeThis'; + let result = statsComputed(user); + + expect(result.int).to.eql(1); + expect(result.per).to.eql(1); + expect(result.con).to.eql(1); + expect(result.str).to.eql(1); + }); }); diff --git a/website/client-old/js/services/statServices.js b/website/client-old/js/services/statServices.js index 5be427b2a8..0117a463fd 100644 --- a/website/client-old/js/services/statServices.js +++ b/website/client-old/js/services/statServices.js @@ -74,18 +74,6 @@ return display; } - function levelBonus(level) { - // Level bonus is derived by taking the level, subtracting one, - // taking the smaller of it or maxLevel (100), - // dividing that by two and then raising it to a whole number - - var levelOrMaxLevel = Math.min((level - 1), Shared.maxLevel); - var levelDividedByTwo = levelOrMaxLevel / 2; - var bonus = Math.ceil(levelDividedByTwo ); - - return bonus; - } - function mountMasterProgress(mounts) { var dropMountsFound = Shared.count.mountMasterProgress(mounts); var display = _formatOutOfTotalDisplay(dropMountsFound, TOTAL_NUMBER_OF_DROP_ANIMALS); @@ -114,12 +102,9 @@ return { beastMasterProgress: beastMasterProgress, - classBonus: classBonus, - equipmentStatBonus: equipmentStatBonus, expDisplay: expDisplay, goldDisplay: goldDisplay, hpDisplay: hpDisplay, - levelBonus: levelBonus, mountMasterProgress: mountMasterProgress, mpDisplay: mpDisplay, totalCount: totalCount diff --git a/website/common/script/libs/statsComputed.js b/website/common/script/libs/statsComputed.js index e73b3698e2..8818594aa2 100644 --- a/website/common/script/libs/statsComputed.js +++ b/website/common/script/libs/statsComputed.js @@ -2,28 +2,55 @@ import _ from 'lodash'; import content from '../content/index'; import * as statHelpers from '../statHelpers'; -module.exports = function statsComputed (user) { - let paths = ['stats', 'stats.buffs', 'items.gear.equipped.weapon', 'items.gear.equipped.armor', - 'items.gear.equipped.head', 'items.gear.equipped.shield', 'items.gear.equipped.body', - 'items.gear.equipped.back']; - let computed = _.reduce(['per', 'con', 'str', 'int'], (m, stat) => { - m[stat] = _.reduce(paths, (m2, path) => { - let val = _.get(user, path); - let item = content.gear.flat[val]; - if (!item) item = {}; - if (!item[stat]) { - item[stat] = 0; - } else { - item[stat] = Number(item[stat]); - } - let thisMultiplier = item.klass === user.stats.class || item.specialClass === user.stats.class ? 1.5 : 1; - let thisReturn = path.indexOf('items.gear') !== -1 ? item[stat] * thisMultiplier : Number(val[stat]); - return m2 + thisReturn || 0; - }, 0); - m[stat] += Math.floor(statHelpers.capByLevel(user.stats.lvl) / 2); - return m; - }, {}); +function equipmentStatBonusComputed (stat, user) { + let gear = content.gear.flat; + let gearBonus = 0; + let classBonus = 0; - computed.maxMP = computed.int * 2 + 30; - return computed; + // toObject is required here due to lodash values not working well with mongoose doc objects. + // if toObject doesn't exist, we're on the client side and can assume the object is already plain JSON + // see http://stackoverflow.com/questions/25767334/underscore-js-keys-and-omit-not-working-as-expected + let equipped = user.items.gear.equipped; + let equippedKeys = !equipped.toObject ? _.values(equipped) : _.values(equipped.toObject()); + + _.each(equippedKeys, (equippedItem) => { + let equipmentStat = gear[equippedItem][stat]; + let classBonusMultiplier = gear[equippedItem].klass === user.stats.class || + gear[equippedItem].specialClass === user.stats.class ? 0.5 : 0; + gearBonus += equipmentStat; + classBonus += equipmentStat * classBonusMultiplier; + }); + + return { + gearBonus, + classBonus, + }; +} + +module.exports = function statsComputed (user) { + let statBreakdown = { + gearBonus: {}, + classBonus: {}, + baseStat: {}, + buff: {}, + levelBonus: {}, + }; + _.each(['per', 'con', 'str', 'int'], (stat) => { + let baseStat = _.get(user, 'stats')[stat]; + let buff = _.get(user, 'stats.buffs')[stat]; + let equipmentBonus = equipmentStatBonusComputed(stat, user); + + statBreakdown[stat] = equipmentBonus.gearBonus + equipmentBonus.classBonus + baseStat + buff; + statBreakdown[stat] += Math.floor(statHelpers.capByLevel(user.stats.lvl) / 2); + + statBreakdown.levelBonus[stat] = Math.floor(statHelpers.capByLevel(user.stats.lvl) / 2); + statBreakdown.gearBonus[stat] = equipmentBonus.gearBonus; + statBreakdown.classBonus[stat] = equipmentBonus.classBonus; + statBreakdown.baseStat[stat] = baseStat; + statBreakdown.buff[stat] = buff; + }); + + statBreakdown.maxMP = statBreakdown.int * 2 + 30; + + return statBreakdown; }; diff --git a/website/views/shared/profiles/stats/attributes.jade b/website/views/shared/profiles/stats/attributes.jade index bf5b1dbcba..ecfd493458 100644 --- a/website/views/shared/profiles/stats/attributes.jade +++ b/website/views/shared/profiles/stats/attributes.jade @@ -10,9 +10,9 @@ table.table.table-striped strong : {{profile.fns.statsComputed().#{stat}}} td: ul.list-unstyled - +statList('statCalc.levelBonus(profile.stats.lvl)', 'levelBonus', 'level', true) - +statList('statCalc.equipmentStatBonus("' + stat + '", profile.items.gear.equipped)', 'equipmentBonus', 'equipment', true) - +statList('statCalc.classBonus(profile, "' + stat + '")', 'classBonus', 'classEquipBonus') + +statList('profile.fns.statsComputed().levelBonus.' + stat, 'levelBonus', 'level', true) + +statList('profile.fns.statsComputed().gearBonus.' + stat, 'equipmentBonus', 'equipment', true) + +statList('profile.fns.statsComputed().classBonus.' + stat, 'classBonus', 'classEquipBonus') +statList('profile.stats.' + stat, 'allocatedPoints', 'allocated') +statList('profile.stats.buffs.' + stat, 'buffs', 'buffs', true)