mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-05 03:52:14 +00:00
Notification and Algorithim changes
This commit is contained in:
parent
09467aee1b
commit
08839278b3
7 changed files with 112 additions and 86 deletions
50
src/app/algos.coffee
Normal file
50
src/app/algos.coffee
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{/}
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue