Notification and Algorithim changes

This commit is contained in:
Philip How 2013-03-02 18:23:43 +00:00
parent 09467aee1b
commit 08839278b3
7 changed files with 112 additions and 86 deletions

50
src/app/algos.coffee Normal file
View file

@ -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

View file

@ -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 "<i class='icon-heart'></i> - #{rounded} HP", 'hp' # lost hp from purchase
else if num > 0
statsNotification "<i class='icon-heart'></i> + #{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 "<i class='icon-star'></i> - #{rounded} XP", 'xp'
else if num > 0
statsNotification "<i class='icon-star'></i> + #{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 "<i class='icon-star'></i> - #{rounded} XP", 'xp'
else if num > 0
statsNotification "<i class='icon-star'></i> + #{rounded} XP", 'xp'
user.on 'set', 'stats.gp', (captures, args) ->
num = captures - args

View file

@ -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 = {}

View file

@ -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

View file

@ -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 }

View file

@ -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
}

View file

@ -29,7 +29,7 @@
{#if _user.history.exp}
<a x-bind=click:toggleChart data-toggle-id="exp-chart" data-history-path="_user.history.exp" rel=tooltip title="Progress"><i class=icon-signal></i></a>&nbsp;
{/}
<i class=icon-star></i> {round(_user.stats.exp)} / {_tnl}
<i class=icon-star></i> {floor(_user.stats.exp)} / {_tnl}
</span>
</div>
<div id="exp-chart" style="display:none;"></div>