diff --git a/dist/habitrpg-shared.js b/dist/habitrpg-shared.js index a6b307b39b..b309e768e9 100644 --- a/dist/habitrpg-shared.js +++ b/dist/habitrpg-shared.js @@ -63,7 +63,7 @@ process.chdir = function (dir) { }; },{}],3:[function(require,module,exports){ -var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/** +var global=self;/** * @license * Lo-Dash 2.4.1 (Custom Build) * Build: `lodash modern -o ./dist/lodash.js` @@ -12059,69 +12059,66 @@ var process=require("__browserify_process");(function() { */ autoAllocate: function() { - var diff, ideal, preference, suggested; - switch (user.preferences.allocationMode) { - case "flat": - suggested = Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per); - if (user.stats.int === suggested) { - return "int"; - } else if (user.stats.per === suggested) { - return "per"; - } else if (user.stats.str === suggested) { - return "str"; - } else { - return "con"; - } - break; - case "classbased": - ideal = [user.stats.lvl / 7 * 3, user.stats.lvl / 7 * 2, user.stats.lvl / 7, user.stats.lvl / 7]; - switch (user.stats["class"]) { - case "wizard": - preference = ["int", "per", "con", "str"]; - break; - case "rogue": - preference = ["per", "str", "int", "con"]; - break; - case "healer": - preference = ["con", "int", "str", "per"]; - break; - default: - preference = ["str", "con", "per", "int"]; - } - diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]]; - suggested = _.findIndex(diff, (function(val) { - if (val === _.min(diff)) { - return true; + return user.stats[(function() { + var diff, ideal, preference, suggested; + switch (user.preferences.allocationMode) { + case "flat": + switch (Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per)) { + case user.stats.int: + return "int"; + case user.stats.per: + return "per"; + case user.stats.str: + return "str"; + default: + return "con"; } - })); - if (suggested === -1) { - return "str"; - } else { - return preference[suggested]; - } - break; - case "taskbased": - suggested = _.findKey(user.stats.training, (function(val) { - if (val === _.max(user.stats.training)) { - return val; + break; + case "classbased": + ideal = [user.stats.lvl / 7 * 3, user.stats.lvl / 7 * 2, user.stats.lvl / 7, user.stats.lvl / 7]; + preference = (function() { + switch (user.stats["class"]) { + case "wizard": + return ["int", "per", "con", "str"]; + case "rogue": + return ["per", "str", "int", "con"]; + case "healer": + return ["con", "int", "str", "per"]; + default: + return ["str", "con", "per", "int"]; + } + })(); + diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]]; + suggested = _.findIndex(diff, (function(val) { + if (val === _.min(diff)) { + return true; + } + })); + if (~suggested) { + return preference[suggested]; + } else { + return "str"; } - })); - user.stats.training.str = 0; - user.stats.training.int = 0; - user.stats.training.con = 0; - user.stats.training.per = 0; - if (suggested === void 0) { + case "taskbased": + suggested = _.findKey(user.stats.training, (function(val) { + if (val === _.max(user.stats.training)) { + return val; + } + })); + _.merge(user.stats.training, { + str: 0, + int: 0, + con: 0, + per: 0 + }); + return suggested || "str"; + default: return "str"; - } else { - return suggested; - } - break; - default: - return "str"; - } + } + })()]++; }, updateStats: function(stats) { - var suggested, tnl; + var tnl; if (stats.hp <= 0) { return user.stats.hp = 0; } @@ -12140,8 +12137,7 @@ var process=require("__browserify_process");(function() { user.stats.lvl++; tnl = api.tnl(user.stats.lvl); if (user.preferences.automaticAllocation) { - suggested = user.fns.autoAllocate(); - user.stats[suggested]++; + user.fns.autoAllocate(); } else { user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int); } diff --git a/script/index.coffee b/script/index.coffee index fcc336e390..eb8c6c95f5 100644 --- a/script/index.coffee +++ b/script/index.coffee @@ -1006,41 +1006,36 @@ api.wrap = (user) -> {update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately ### autoAllocate: -> - switch user.preferences.allocationMode - when "flat" - suggested = Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per) - if user.stats.int is suggested # In case of ties, favor INT first, to get the next point sooner - return "int" - else if user.stats.per is suggested # Then favor PER, it's a god stat - return "per" - else if user.stats.str is suggested # Then favor STR, everyone loves crits - return "str" - else - return "con" # CON, the unsexiest of attributes - when "classbased" - # Attributes get 3:2:1:1 per 7 levels. - ideal = [(user.stats.lvl / 7 * 3), (user.stats.lvl / 7 * 2), (user.stats.lvl / 7), (user.stats.lvl / 7)] - # Primary, secondary etc. attributes aren't explicitly defined, so hardcode them. In order as above - switch user.stats.class - when "wizard" then preference = ["int", "per", "con", "str"] - when "rogue" then preference = ["per", "str", "int", "con"] - when "healer" then preference = ["con", "int", "str", "per"] - else preference = ["str", "con", "per", "int"] - # Get the difference between the ideal attribute spread according to level, and the user's current spread. - diff = [(user.stats[preference[0]]-ideal[0]),(user.stats[preference[1]]-ideal[1]),(user.stats[preference[2]]-ideal[2]),(user.stats[preference[3]]-ideal[3])] - suggested = _.findIndex(diff, ((val) -> if val is _.min(diff) then true)) # Returns the index of the first attribute that's furthest behind the ideal - if suggested is -1 then return "str" else return preference[suggested] # If _.findIndex failed, we'd get a -1... - when "taskbased" - suggested = _.findKey(user.stats.training, ((val) -> if val is _.max(user.stats.training) then val)) # Returns the stat that's been trained up the most this level - # FIXME Reset training for this level. Tried _.each but couldn't get it to take. - user.stats.training.str = 0 - user.stats.training.int = 0 - user.stats.training.con = 0 - user.stats.training.per = 0 - if suggested is undefined then return "str" else return suggested # Failed _.findkey gives undefined - # tallies = _.reduce user.tasks, ((m,v)-> m[v.attribute or 'str'] += v.value;m), {str:0,int:0,con:0,per:0} - # suggested = _.reduce tallies, ((m,v,k)-> if v>tallies[m] then k else m), 'str' - else return "str" # if all else fails, dump into STR + user.stats[(-> + switch user.preferences.allocationMode + when "flat" + switch Math.min(user.stats.str, user.stats.int, user.stats.con, user.stats.per) + when user.stats.int then "int" # In case of ties, favor INT first, to get the next point sooner + when user.stats.per then "per" # Then favor PER, it's a god stat + when user.stats.str then "str" # Then favor STR, everyone loves crits + else "con" # CON, the unsexiest of attributes + when "classbased" + # Attributes get 3:2:1:1 per 7 levels. + ideal = [(user.stats.lvl / 7 * 3), (user.stats.lvl / 7 * 2), (user.stats.lvl / 7), (user.stats.lvl / 7)] + # Primary, secondary etc. attributes aren't explicitly defined, so hardcode them. In order as above + preference = switch user.stats.class + when "wizard" then ["int", "per", "con", "str"] + when "rogue" then ["per", "str", "int", "con"] + when "healer" then ["con", "int", "str", "per"] + else ["str", "con", "per", "int"] + # Get the difference between the ideal attribute spread according to level, and the user's current spread. + diff = [(user.stats[preference[0]]-ideal[0]),(user.stats[preference[1]]-ideal[1]),(user.stats[preference[2]]-ideal[2]),(user.stats[preference[3]]-ideal[3])] + suggested = _.findIndex(diff, ((val) -> if val is _.min(diff) then true)) # Returns the index of the first attribute that's furthest behind the ideal + return if ~suggested then preference[suggested] else "str" # If _.findIndex failed, we'd get a -1... + when "taskbased" + suggested = _.findKey(user.stats.training, ((val) -> if val is _.max(user.stats.training) then val)) # Returns the stat that's been trained up the most this level + # Reset training for this level. + _.merge user.stats.training, {str:0,int:0,con:0,per:0} + return suggested or "str" # Failed _.findkey gives undefined + # tallies = _.reduce user.tasks, ((m,v)-> m[v.attribute or 'str'] += v.value;m), {str:0,int:0,con:0,per:0} + # suggested = _.reduce tallies, ((m,v,k)-> if v>tallies[m] then k else m), 'str' + else "str" # if all else fails, dump into STR + )()]++ updateStats: (stats) -> # Game Over @@ -1067,8 +1062,7 @@ api.wrap = (user) -> # Auto-allocate a point, or give them a new manual point if user.preferences.automaticAllocation - suggested = user.fns.autoAllocate() - user.stats[suggested]++ + user.fns.autoAllocate() else # add new allocatable points. We could do user.stats.points++, but this does a fail-safe just in case user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int); diff --git a/tests/simulations/autoAllocate.coffee b/tests/simulations/autoAllocate.coffee new file mode 100644 index 0000000000..f722557753 --- /dev/null +++ b/tests/simulations/autoAllocate.coffee @@ -0,0 +1,72 @@ +### +1) clone the repo +2) npm install +3) coffee ./tests/math_samples.coffee + +Current results at https://gist.github.com/lefnire/8049676 +### + +shared = require '../../script/index.coffee' +_ = require 'lodash' +$w = (s)->s.split(' ') + +id = shared.uuid() +user = + stats: + class: 'warrior' + lvl:1, hp:50, gp:0, exp:10 + per:0, int:0, con:0, str:0 + buffs: {per:0, int:0, con:0, str:0} + training: {int:0,con:0,per:0,str:0} + preferences: automaticAllocation: false + party: quest: key:'evilsanta', progress: {up:0,down:0} + achievements: {} + items: + eggs: {} + hatchingPotions: {} + food: {} + gear: + equipped: + weapon: 'weapon_warrior_4' + armor: 'armor_warrior_4' + shield: 'shield_warrior_4' + head: 'head_warrior_4' + habits: [{value:1,type:'habit'}] + dailys: [] + todos: [] + rewards: [] + +modes = + flat: _.cloneDeep user + classbased_warrior: _.cloneDeep user + classbased_rogue: _.cloneDeep user + classbased_wizard: _.cloneDeep user + classbased_healer: _.cloneDeep user + taskbased: _.cloneDeep user + +modes.classbased_warrior.stats.class = 'warrior' +modes.classbased_rogue.stats.class = 'rogue' +modes.classbased_wizard.stats.class = 'wizard' +modes.classbased_healer.stats.class = 'healer' + +_.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) -> + _.merge modes[mode].preferences, + automaticAllocation: true + allocationMode: if mode.indexOf('classbased') is 0 then 'classbased' else mode + shared.wrap(modes[mode]) + +console.log "\n\n================================================" +console.log "New Simulation" +console.log "================================================\n\n" + + +_.times [20], (lvl) -> + console.log ("[lvl #{lvl}]\n--------------\n") + _.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) -> + u = modes[mode] #local var + u.stats.exp = shared.tnl(lvl)+1 # level up + u.habits[0].attribute = u.fns.randomVal({str:'str',int:'int',per:'per',con:'con'}) + u.ops.score {params:{id:u.habits[0].id},direction:'up'} + u.fns.updateStats(u.stats) # trigger stats update + str = mode + (if mode is 'taskbased' then " (#{u.habits[0].attribute})" else "") + console.log str, _.pick(u.stats, $w 'per int con str') \ No newline at end of file diff --git a/tests/math_samples.coffee b/tests/simulations/passive_active_attrs.coffee similarity index 99% rename from tests/math_samples.coffee rename to tests/simulations/passive_active_attrs.coffee index 96ce6e22bd..5f8060bd3e 100644 --- a/tests/math_samples.coffee +++ b/tests/simulations/passive_active_attrs.coffee @@ -6,7 +6,7 @@ Current results at https://gist.github.com/lefnire/8049676 ### -shared = require '../script/index.coffee' +shared = require '../../script/index.coffee' _ = require 'lodash' id = shared.uuid()