mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-05 03:52:14 +00:00
much cleanup to scoring#score
This commit is contained in:
parent
77072909f0
commit
09af820783
1 changed files with 78 additions and 75 deletions
|
|
@ -122,68 +122,73 @@ updateStats = (newStats, update) ->
|
||||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||||
score = (taskId, direction, times, update) ->
|
score = (taskId, direction, times, update) ->
|
||||||
times ||= 1
|
times ||= 1
|
||||||
|
|
||||||
|
userObj = update || user.get()
|
||||||
|
{money, hp, exp, lvl} = userObj.stats
|
||||||
|
|
||||||
taskPath = "tasks.#{taskId}"
|
taskPath = "tasks.#{taskId}"
|
||||||
taskObj = model.get("_user.#{taskPath}")
|
taskObj = model.get("_user.#{taskPath}")
|
||||||
{type, value} = taskObj
|
{type, value} = taskObj
|
||||||
userObj = update || user.get()
|
|
||||||
|
|
||||||
# Don't adjust values for rewards, or for habits that don't have both + and -
|
|
||||||
adjustvalue = (type != 'reward')
|
|
||||||
if (type == 'habit') and (taskObj.up==false or taskObj.down==false)
|
|
||||||
adjustvalue = false
|
|
||||||
|
|
||||||
delta = 0
|
delta = 0
|
||||||
# If multiple days have passed, multiply times days missed
|
calculateDelta = (adjustvalue=true) ->
|
||||||
# TODO integrate this multiplier into the formula, so don't have to loop
|
# If multiple days have passed, multiply times days missed
|
||||||
_.times times, (n) ->
|
# TODO integrate this multiplier into the formula, so don't have to loop
|
||||||
# Each iteration calculate the delta (nextDelta), which is then accumulated in delta
|
_.times times, (n) ->
|
||||||
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
# Each iteration calculate the delta (nextDelta), which is then accumulated in delta
|
||||||
# rather than iteratively
|
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
||||||
nextDelta = taskDeltaFormula(value, direction)
|
# rather than iteratively
|
||||||
value += nextDelta if adjustvalue
|
nextDelta = taskDeltaFormula(value, direction)
|
||||||
delta += nextDelta
|
value += nextDelta if adjustvalue
|
||||||
|
delta += nextDelta
|
||||||
|
|
||||||
if type == 'habit'
|
addPoints = () ->
|
||||||
# Add habit value to habit-history (if different)
|
|
||||||
historyEntry = { date: new Date(), value: value } if taskObj.value != value
|
|
||||||
if update
|
|
||||||
update.tasks[taskObj.id].history ||= []
|
|
||||||
update.tasks[taskObj.id].history.push historyEntry
|
|
||||||
else
|
|
||||||
model.push "_user.#{taskPath}.history", historyEntry
|
|
||||||
userSet "#{taskPath}.value", value, update
|
|
||||||
|
|
||||||
if update
|
|
||||||
# Will modify the user later as an aggregate, just return the delta
|
|
||||||
return if (type == 'daily') then delta else 0
|
|
||||||
|
|
||||||
# Update the user's status
|
|
||||||
{money, hp, exp, lvl} = userObj.stats
|
|
||||||
|
|
||||||
if type == 'reward'
|
|
||||||
# purchase item
|
|
||||||
money -= taskObj.value
|
|
||||||
num = parseFloat(taskObj.value).toFixed(2)
|
|
||||||
# if too expensive, reduce health & zero money
|
|
||||||
if money < 0
|
|
||||||
hp += money # hp - money difference
|
|
||||||
money = 0
|
|
||||||
|
|
||||||
# Add points to exp & money if positive delta
|
|
||||||
# Only take away mony if it was a mistake (aka, a checkbox)
|
|
||||||
if (delta > 0 or (type in ['daily', 'todo'])) and !update? # update==cron
|
|
||||||
modified = expModifier(delta)
|
modified = expModifier(delta)
|
||||||
exp += modified
|
exp += modified
|
||||||
money += modified
|
money += modified
|
||||||
# Deduct from health (rewards case handled above)
|
|
||||||
else unless type in ['reward', 'todo']
|
subtractPoints = () ->
|
||||||
modified = hpModifier(delta)
|
modified = hpModifier(delta)
|
||||||
hp += modified
|
hp += modified
|
||||||
|
|
||||||
|
switch type
|
||||||
|
when 'habit'
|
||||||
|
# Don't adjust values for habits that don't have both + and -
|
||||||
|
adjustvalue = if (taskObj.up==false or taskObj.down==false) then false else true
|
||||||
|
calculateDelta(adjustvalue)
|
||||||
|
# Add habit value to habit-history (if different)
|
||||||
|
historyEntry = { date: new Date(), value: value } if taskObj.value != value
|
||||||
|
if (delta > 0) then addPoints() else subtractPoints()
|
||||||
|
if update
|
||||||
|
update.tasks[taskObj.id].history ||= []
|
||||||
|
update.tasks[taskObj.id].history.push historyEntry
|
||||||
|
else
|
||||||
|
model.push "_user.#{taskPath}.history", historyEntry
|
||||||
|
|
||||||
|
when 'daily', 'todo'
|
||||||
|
calculateDelta()
|
||||||
|
if delta > 0
|
||||||
|
addPoints()
|
||||||
|
else if delta < 0 and !update? # update==cron
|
||||||
|
addPoints() # trick to "undo" points when they mistakenly checked a checkbox
|
||||||
|
else
|
||||||
|
subtractPoints()
|
||||||
|
|
||||||
|
when 'reward'
|
||||||
|
# Don't adjust values for rewards
|
||||||
|
calculateDelta(false)
|
||||||
|
# purchase item
|
||||||
|
money -= taskObj.value
|
||||||
|
num = parseFloat(taskObj.value).toFixed(2)
|
||||||
|
# if too expensive, reduce health & zero money
|
||||||
|
if money < 0
|
||||||
|
hp += money # hp - money difference
|
||||||
|
money = 0
|
||||||
|
|
||||||
|
userSet "#{taskPath}.value", value, update
|
||||||
updateStats {hp: hp, exp: exp, money: money}, update
|
updateStats {hp: hp, exp: exp, money: money}, update
|
||||||
|
|
||||||
return delta
|
|
||||||
|
|
||||||
# At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
# At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
||||||
# For incomplete Dailys, deduct experience
|
# For incomplete Dailys, deduct experience
|
||||||
cron = (userObj) ->
|
cron = (userObj) ->
|
||||||
|
|
@ -195,15 +200,23 @@ cron = (userObj) ->
|
||||||
# Tally function, which is called asyncronously below - but function is defined here.
|
# Tally function, which is called asyncronously below - but function is defined here.
|
||||||
# We need access to some closure variables above
|
# We need access to some closure variables above
|
||||||
todoTally = 0
|
todoTally = 0
|
||||||
hpTally = 0
|
|
||||||
tallyTask = (taskObj, callback) ->
|
# Tally each task
|
||||||
|
# _.each user.get('tasks'), (taskObj) -> tallyTask(taskObj, ->)
|
||||||
|
# Asyncronous version:
|
||||||
|
tasks = _.toArray(userObj.tasks)
|
||||||
|
userObj.history ||= {}
|
||||||
|
userObj.history.todos ||= []
|
||||||
|
userObj.history.exp ||= []
|
||||||
|
|
||||||
|
_.each tasks, (taskObj) ->
|
||||||
# setTimeout {THIS_FUNCTION}, 1 # strange hack that seems necessary when using async
|
# setTimeout {THIS_FUNCTION}, 1 # strange hack that seems necessary when using async
|
||||||
{id, type, completed, repeat} = taskObj
|
{id, type, completed, repeat} = taskObj
|
||||||
#don't know why this happens, but it does. need to investigate
|
#don't know why this happens, but it does. need to investigate
|
||||||
unless id?
|
unless id?
|
||||||
return callback('a task had a null id during cron, this should not be happening')
|
return callback('a task had a null id during cron, this should not be happening')
|
||||||
if type in ['todo', 'daily']
|
if type in ['todo', 'daily']
|
||||||
# Deduct experience for missed Daily tasks,
|
# Deduct experience for missed Daily tasks,
|
||||||
# but not for Todos (just increase todo's value)
|
# but not for Todos (just increase todo's value)
|
||||||
unless completed
|
unless completed
|
||||||
# for todos & typical dailies, these are equivalent
|
# for todos & typical dailies, these are equivalent
|
||||||
|
|
@ -216,7 +229,7 @@ cron = (userObj) ->
|
||||||
thatDay = moment().subtract('days', n+1)
|
thatDay = moment().subtract('days', n+1)
|
||||||
if repeat[helpers.dayMapping[thatDay.day()]]==true
|
if repeat[helpers.dayMapping[thatDay.day()]]==true
|
||||||
daysFailed++
|
daysFailed++
|
||||||
hpTally += score(id, 'down', daysFailed, userObj)
|
score(id, 'down', daysFailed, userObj)
|
||||||
|
|
||||||
value = userObj.tasks[taskObj.id].value #get updated value
|
value = userObj.tasks[taskObj.id].value #get updated value
|
||||||
if type == 'daily'
|
if type == 'daily'
|
||||||
|
|
@ -226,28 +239,18 @@ cron = (userObj) ->
|
||||||
absVal = if (completed) then Math.abs(value) else value
|
absVal = if (completed) then Math.abs(value) else value
|
||||||
todoTally += absVal
|
todoTally += absVal
|
||||||
userObj.tasks[taskObj.id].completed = false if type == 'daily'
|
userObj.tasks[taskObj.id].completed = false if type == 'daily'
|
||||||
callback()
|
|
||||||
|
# Finished tallying, this is the 'completed' callback
|
||||||
# Tally each task
|
userObj.history.todos.push { date: today, value: todoTally }
|
||||||
# _.each user.get('tasks'), (taskObj) -> tallyTask(taskObj, ->)
|
# tally experience
|
||||||
# Asyncronous version:
|
expTally = userObj.stats.exp
|
||||||
tasks = _.toArray(userObj.tasks)
|
lvl = 0 #iterator
|
||||||
userObj.history ||= {}
|
while lvl < (userObj.stats.lvl-1)
|
||||||
userObj.history.todos ||= []
|
lvl++
|
||||||
userObj.history.exp ||= []
|
expTally += (lvl*100)/5
|
||||||
async.forEach tasks, tallyTask, (err) ->
|
userObj.history.exp.push { date: today, value: expTally }
|
||||||
# Finished tallying, this is the 'completed' callback
|
userObj.lastCron = today # reset cron
|
||||||
userObj.history.todos.push { date: today, value: todoTally }
|
return userObj
|
||||||
# tally experience
|
|
||||||
expTally = userObj.stats.exp
|
|
||||||
lvl = 0 #iterator
|
|
||||||
while lvl < (userObj.stats.lvl-1)
|
|
||||||
lvl++
|
|
||||||
expTally += (lvl*100)/5
|
|
||||||
userObj.history.exp.push { date: today, value: expTally }
|
|
||||||
updateStats {hp:userObj.stats.hp + hpTally}, userObj # finally, the user if they've failed the last few days
|
|
||||||
userObj.lastCron = today # reset cron
|
|
||||||
return userObj
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue