From 08839278b38f0e29cc293c1f656aaae58a1993cc Mon Sep 17 00:00:00 2001 From: Philip How Date: Sat, 2 Mar 2013 18:23:43 +0000 Subject: [PATCH] Notification and Algorithim changes --- src/app/algos.coffee | 50 ++++++++++++++++++++++++++++ src/app/browser.coffee | 22 ++++++++----- src/app/character.coffee | 1 + src/app/helpers.coffee | 3 ++ src/app/items.coffee | 50 ++++++++++++++-------------- src/app/scoring.coffee | 70 +++++++++++----------------------------- views/app/header.html | 2 +- 7 files changed, 112 insertions(+), 86 deletions(-) create mode 100644 src/app/algos.coffee diff --git a/src/app/algos.coffee b/src/app/algos.coffee new file mode 100644 index 0000000000..69ab9b4e93 --- /dev/null +++ b/src/app/algos.coffee @@ -0,0 +1,50 @@ + +MODIFIER = .02 + +module.exports.tnl = (level) -> + return (Math.pow(level,2)*10)+(level*10)+80 + +### + Calculates Exp modificaiton based on level and weapon strength + {value} task.value for exp gain + {weaponStrength) weapon strength + {level} current user level +### +module.exports.expModifier = (value, weaponStrength, level) -> + levelModifier = (level-1) * MODIFIER + weaponModifier = weaponStrength / 100 + strength = 1 + weaponModifier + levelModifier + return value * strength + +### + Calculates HP modification based on level and armor defence + {value} task.value for hp loss + {armorDefense} defense from armor + {helmDefense} defense from helm + {level} current user level +### +module.exports.hpModifier = (value, armorDefense, helmDefense, shieldDefense, level) -> + levelModifier = (level-1) * MODIFIER + armorModifier = (armorDefense + helmDefense + shieldDefense) / 100 + defense = 1 - levelModifier + armorModifier + return value * defense + +### + Future use +### +module.exports.gpModifier = (value, modifier) -> + return value * modifier + +### + Calculates the next task.value based on direction + Uses a capped inverse log y=.9^x, y>= -5 + {currentValue} the current value of the task + {direction} up or down +### +module.exports.taskDeltaFormula = (currentValue, direction) -> + sign = if (direction is 'up') then 1 else -1 + delta = Math.pow(0.9,currentValue) * sign + if delta < -5 then delta = -5 + console.log("CurrentValue: " + currentValue + " delta: " + delta) + #delta = if (currentValue < 0) then (( -0.1 * currentValue + 1 ) * sign) else (( Math.pow(0.9,currentValue) ) * sign) + return delta \ No newline at end of file diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 5becd6bcc0..63e6994d58 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -1,11 +1,13 @@ _ = require 'underscore' moment = require 'moment' +#algos = require './algos' module.exports.restoreRefs = restoreRefs = (model) -> # tnl function model.fn '_tnl', '_user.stats.lvl', (lvl) -> # see https://github.com/lefnire/habitrpg/issues/4 # also update in scoring.coffee. TODO create a function accessible in both locations + #TODO find a method of calling algos.tnl() 10*Math.pow(lvl,2)+(lvl*10)+80 #refLists @@ -85,7 +87,9 @@ setupSortable = (model) -> # Note, have to setup helper function here and call it for each type later # due to variable binding of "type" setupSortable = (type) -> - $("ul.#{type}s").sortable + for key, value of type + parsedType = value + $("ul.#{parsedType}s").sortable dropOnEmpty: false cursor: "move" items: "li" @@ -191,19 +195,21 @@ setupGrowlNotifications = (model) -> # Setup listeners which trigger notifications user.on 'set', 'stats.hp', (captures, args) -> num = captures - args + console.log("hp = " + num) rounded = Math.abs(num.toFixed(1)) if num < 0 statsNotification " - #{rounded} HP", 'hp' # lost hp from purchase else if num > 0 statsNotification " + #{rounded} HP", 'hp' # gained hp from potion/level? - user.on 'set', 'stats.exp', (captures, args) -> - num = captures - args - rounded = Math.abs(num.toFixed(1)) - if num < 0 - statsNotification " - #{rounded} XP", 'xp' - else if num > 0 - statsNotification " + #{rounded} XP", 'xp' + user.on 'set', 'stats.exp', (captures, args, isLocal, silent) -> + if not silent + num = captures - args + rounded = Math.abs(num.toFixed(1)) + if num < 0 + statsNotification " - #{rounded} XP", 'xp' + else if num > 0 + statsNotification " + #{rounded} XP", 'xp' user.on 'set', 'stats.gp', (captures, args) -> num = captures - args diff --git a/src/app/character.coffee b/src/app/character.coffee index 07b0996dcb..de1b280c2d 100644 --- a/src/app/character.coffee +++ b/src/app/character.coffee @@ -221,6 +221,7 @@ module.exports.BatchUpdate = BatchUpdate = (model) -> commit: -> model._dontPersist = false # some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js + # pass true if we have levelled to supress xp notification user.set "update__", updates transactionInProgress = false updates = {} diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 38733f11d5..5c2cccaa15 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -14,6 +14,9 @@ module.exports.viewHelpers = (view) -> view.fn "round", (num) -> Math.round num + + view.fn "floor", (num) -> + Math.floor num view.fn "lt", (a, b) -> a < b diff --git a/src/app/items.coffee b/src/app/items.coffee index 911458e60c..a9ca688f52 100644 --- a/src/app/items.coffee +++ b/src/app/items.coffee @@ -2,37 +2,37 @@ _ = require 'underscore' items = module.exports.items = weapon: [ - {index: 0, text: "Training Sword", classes: "weapon_0", notes:'Training weapon.', modifier: 0.00, value:0} - {index: 1, text: "Sword", classes:'weapon_1', notes:'Increases experience gain by 3%.', modifier: 0.03, value:20} - {index: 2, text: "Axe", classes:'weapon_2', notes:'Increases experience gain by 6%.', modifier: 0.06, value:30} - {index: 3, text: "Morningstar", classes:'weapon_3', notes:'Increases experience gain by 9%.', modifier: 0.09, value:45} - {index: 4, text: "Blue Sword", classes:'weapon_4', notes:'Increases experience gain by 12%.', modifier: 0.12, value:65} - {index: 5, text: "Red Sword", classes:'weapon_5', notes:'Increases experience gain by 15%.', modifier: 0.15, value:90} - {index: 6, text: "Golden Sword", classes:'weapon_6', notes:'Increases experience gain by 18%.', modifier: 0.18, value:120} + {index: 0, text: "Training Sword", classes: "weapon_0", notes:'Training weapon.', strength: 0, value:0} + {index: 1, text: "Sword", classes:'weapon_1', notes:'Increases experience gain by 3%.', strength: 3, value:20} + {index: 2, text: "Axe", classes:'weapon_2', notes:'Increases experience gain by 6%.', strength: 6, value:30} + {index: 3, text: "Morningstar", classes:'weapon_3', notes:'Increases experience gain by 9%.', strength: 9, value:45} + {index: 4, text: "Blue Sword", classes:'weapon_4', notes:'Increases experience gain by 12%.', strength: 12, value:65} + {index: 5, text: "Red Sword", classes:'weapon_5', notes:'Increases experience gain by 15%.', strength: 15, value:90} + {index: 6, text: "Golden Sword", classes:'weapon_6', notes:'Increases experience gain by 18%.', strength: 18, value:120} ] armor: [ - {index: 0, text: "Cloth Armor", classes: 'armor_0', notes:'Training armor.', modifier: 0.00, value:0} - {index: 1, text: "Leather Armor", classes: 'armor_1', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:30} - {index: 2, text: "Chain Mail", classes: 'armor_2', notes:'Decreases HP loss by 6%.', modifier: 0.06, value:45} - {index: 3, text: "Plate Mail", classes: 'armor_3', notes:'Decreases HP loss by 7%.', modifier: 0.07, value:65} - {index: 4, text: "Red Armor", classes: 'armor_4', notes:'Decreases HP loss by 8%.', modifier: 0.08, value:90} - {index: 5, text: "Golden Armor", classes: 'armor_5', notes:'Decreases HP loss by 10%.', modifier: 0.1, value:120} + {index: 0, text: "Cloth Armor", classes: 'armor_0', notes:'Training armor.', defense: 0, value:0} + {index: 1, text: "Leather Armor", classes: 'armor_1', notes:'Decreases HP loss by 4%.', defense: 4, value:30} + {index: 2, text: "Chain Mail", classes: 'armor_2', notes:'Decreases HP loss by 6%.', defense: 6, value:45} + {index: 3, text: "Plate Mail", classes: 'armor_3', notes:'Decreases HP loss by 7%.', defense: 7, value:65} + {index: 4, text: "Red Armor", classes: 'armor_4', notes:'Decreases HP loss by 8%.', defense: 8, value:90} + {index: 5, text: "Golden Armor", classes: 'armor_5', notes:'Decreases HP loss by 10%.', defense: 10, value:120} ] head: [ - {index: 0, text: "No Helm", classes: 'head_0', notes:'Training helm.', modifier: 0.00, value:0} - {index: 1, text: "Leather Helm", classes: 'head_1', notes:'Decreases HP loss by 2%.', modifier: 0.02, value:15} - {index: 2, text: "Chain Coif", classes: 'head_2', notes:'Decreases HP loss by 3%.', modifier: 0.03, value:25} - {index: 3, text: "Plate Helm", classes: 'head_3', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:45} - {index: 4, text: "Red Helm", classes: 'head_4', notes:'Decreases HP loss by 5%.', modifier: 0.05, value:60} - {index: 5, text: "Golden Helm", classes: 'head_5', notes:'Decreases HP loss by 6%.', modifier: 0.06, value:80} + {index: 0, text: "No Helm", classes: 'head_0', notes:'Training helm.', defense: 0, value:0} + {index: 1, text: "Leather Helm", classes: 'head_1', notes:'Decreases HP loss by 2%.', defense: 2, value:15} + {index: 2, text: "Chain Coif", classes: 'head_2', notes:'Decreases HP loss by 3%.', defense: 3, value:25} + {index: 3, text: "Plate Helm", classes: 'head_3', notes:'Decreases HP loss by 4%.', defense: 4, value:45} + {index: 4, text: "Red Helm", classes: 'head_4', notes:'Decreases HP loss by 5%.', defense: 5, value:60} + {index: 5, text: "Golden Helm", classes: 'head_5', notes:'Decreases HP loss by 6%.', defense: 6, value:80} ] shield: [ - {index: 0, text: "No Shield", classes: 'shield_0', notes:'No Shield.', modifier: 0.00, value:0} - {index: 1, text: "Wooden Shield", classes: 'shield_1', notes:'Decreases HP loss by 3%', modifier: 0.03, value:20} - {index: 2, text: "Buckler", classes: 'shield_2', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:35} - {index: 3, text: "Enforced Shield", classes: 'shield_3', notes:'Decreases HP loss by 5%.', modifier: 0.05, value:55} - {index: 4, text: "Red Shield", classes: 'shield_4', notes:'Decreases HP loss by 7%.', modifier: 0.07, value:70} - {index: 5, text: "Golden Shield", classes: 'shield_5', notes:'Decreases HP loss by 8%.', modifier: 0.08, value:90} + {index: 0, text: "No Shield", classes: 'shield_0', notes:'No Shield.', defense: 0, value:0} + {index: 1, text: "Wooden Shield", classes: 'shield_1', notes:'Decreases HP loss by 3%', defense: 3, value:20} + {index: 2, text: "Buckler", classes: 'shield_2', notes:'Decreases HP loss by 4%.', defense: 4, value:35} + {index: 3, text: "Enforced Shield", classes: 'shield_3', notes:'Decreases HP loss by 5%.', defense: 5, value:55} + {index: 4, text: "Red Shield", classes: 'shield_4', notes:'Decreases HP loss by 7%.', defense: 7, value:70} + {index: 5, text: "Golden Shield", classes: 'shield_5', notes:'Decreases HP loss by 8%.', defense: 8, value:90} ] potion: {type: 'potion', text: "Potion", notes: "Recover 15 HP", value: 25, classes: 'potion'} reroll: {type: 'reroll', text: "Re-Roll", classes: 'reroll', notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate.", value:0 } diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index ea8ab4b1fb..9abe1fc3ff 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -5,53 +5,12 @@ helpers = require './helpers' browser = require './browser' character = require './character' items = require './items' +algos = require './algos' module.exports.Scoring = (model) -> - - MODIFIER = .02 # each new level, armor, weapon add 2% modifier (this mechanism will change) + MODIFIER = algos.MODIFIER # each new level, armor, weapon add 2% modifier (this mechanism will change) user = model.at '_user' - ### - Calculates Exp & GP modification based on weapon & lvl - {value} task.value for gain - {modifiers} may manually pass in stats as {weapon, exp}. This is used for testing - ### - expModifier = (value, modifiers = {}) -> - weapon = modifiers.weapon || user.get('items.weapon') - lvl = modifiers.lvl || user.get('stats.lvl') - dmg = items.items.weapon[weapon].modifier # each new weapon increases exp gain - dmg += (lvl-1) * MODIFIER # same for lvls - modified = value + (value * dmg) - return modified - - ### - Calculates HP-loss modification based on armor & lvl - {value} task.value which is hurting us - {modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing - ### - hpModifier = (value, modifiers = {}) -> - armor = modifiers.armor || user.get('items.armor') - head = modifiers.head || user.get('items.head') - shield = modifiers.shield || user.get('items.shield') - lvl = modifiers.lvl || user.get('stats.lvl') - ac = items.items.armor[armor].modifier + items.items.head[head].modifier + items.items.shield[shield].modifier # each new armor decreases HP loss - ac += (lvl-1) * MODIFIER # same for lvls - modified = value - (value * ac) - return modified - - ### - Calculates the next task.value based on direction - For negative values, use a line: something like y=-.1x+1 - For positibe values, taper off with inverse log: y=.9^x - Would love to use inverse log for the whole thing, but after 13 fails it hits infinity. Revisit this formula later - {currentValue} the current value of the task, determines it's next value - {direction} 'up' or 'down' - ### - taskDeltaFormula = (currentValue, direction) -> - sign = if (direction == "up") then 1 else -1 - delta = if (currentValue < 0) then (( -0.1 * currentValue + 1 ) * sign) else (( Math.pow(0.9,currentValue) ) * sign) - return delta - ### Updates user stats with new stats. Handles death, leveling up, etc {stats} new stats @@ -75,12 +34,15 @@ module.exports.Scoring = (model) -> if newStats.exp? # level up & carry-over exp tnl = model.get '_tnl' + silent = false if newStats.exp >= tnl + silent = true newStats.exp -= tnl obj.stats.lvl++ obj.stats.hp = 50 obj.stats.exp = newStats.exp + user.pass(silent:true).set('stats.exp', obj.stats.exp) if silent # Set flags when they unlock features if !obj.flags.customizationsNotification and (obj.stats.exp > 10 or obj.stats.lvl > 1) @@ -107,7 +69,6 @@ module.exports.Scoring = (model) -> # {times} # times to call score on this task (1 unless cron, usually) # {update} if we're running updates en-mass (eg, cron on server) pass in userObj score = (taskId, direction, times, batch, cron) -> - commit = false unless batch? commit = true @@ -136,17 +97,23 @@ module.exports.Scoring = (model) -> # Each iteration calculate the delta (nextDelta), which is then accumulated in delta # (aka, the total delta). This weirdness won't be necessary when calculating mathematically # rather than iteratively - nextDelta = taskDeltaFormula(value, direction) + nextDelta = algos.taskDeltaFormula(value, direction) value += nextDelta if adjustvalue delta += nextDelta addPoints = -> - modified = expModifier(delta) + level = user.get('stats.lvl') + weaponStrength = items.items.weapon[user.get('items.weapon')].strength + modified = algos.expModifier(delta,weaponStrength,level) exp += modified*10 gp += delta subtractPoints = -> - modified = hpModifier(delta) + level = user.get('stats.lvl') + armorDefense = items.items.armor[user.get('items.armor')].defense + helmDefense = items.items.head[user.get('items.head')].defense + shieldDefense = items.items.shield[user.get('items.shield')].defense + modified = algos.hpModifier(delta,armorDefense,helmDefense,shieldDefense,level) hp += modified switch type @@ -249,7 +216,7 @@ module.exports.Scoring = (model) -> lvl = 0 #iterator while lvl < (obj.stats.lvl-1) lvl++ - expTally += (lvl*100)/5 + expTally += algos.tnl(lvl) obj.history.exp.push { date: today, value: expTally } # Set the new user specs, and animate HP loss @@ -262,12 +229,11 @@ module.exports.Scoring = (model) -> return { - MODIFIER: MODIFIER score: score cron: cron # testing stuff - expModifier: expModifier - hpModifier: hpModifier - taskDeltaFormula: taskDeltaFormula + expModifier: algos.expModifier + hpModifier: algos.hpModifier + taskDeltaFormula: algos.taskDeltaFormula } diff --git a/views/app/header.html b/views/app/header.html index f3bc5f3c3a..2491090dae 100644 --- a/views/app/header.html +++ b/views/app/header.html @@ -29,7 +29,7 @@ {#if _user.history.exp}   {/} - {round(_user.stats.exp)} / {_tnl} + {floor(_user.stats.exp)} / {_tnl}