mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-05 12:02:13 +00:00
chore(github): merge pr #327
This commit is contained in:
commit
166d53ac1b
3 changed files with 1236 additions and 592 deletions
1712
dist/habitrpg-shared.js
vendored
1712
dist/habitrpg-shared.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -855,28 +855,69 @@ api.wrap = (user, main=true) ->
|
||||||
delta = 0
|
delta = 0
|
||||||
|
|
||||||
calculateDelta = ->
|
calculateDelta = ->
|
||||||
|
# Calculates the next task.value based on direction
|
||||||
|
# Uses a capped inverse log y=.95^x, y>= -5
|
||||||
|
|
||||||
|
# Min/max on task redness
|
||||||
|
currVal =
|
||||||
|
if task.value < -47.27 then -47.27
|
||||||
|
else if task.value > 21.27 then 21.27
|
||||||
|
else task.value
|
||||||
|
nextDelta = Math.pow(0.9747, currVal) * (if direction is 'down' then -1 else 1)
|
||||||
|
|
||||||
|
# Checklists
|
||||||
|
if task.checklist?.length > 0
|
||||||
|
# If the Daily, only dock them them a portion based on their checklist completion
|
||||||
|
if direction is 'down' and task.type is 'daily' and options.cron
|
||||||
|
nextDelta *= (1 - _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0) / task.checklist.length)
|
||||||
|
# If To-Do, point-match the TD per checklist item completed
|
||||||
|
if task.type is 'todo'
|
||||||
|
nextDelta *= (1 + _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0))
|
||||||
|
nextDelta
|
||||||
|
|
||||||
|
calculateReverseDelta = ->
|
||||||
|
# Approximates the reverse delta for the task value
|
||||||
|
# This is meant to return the task value to its original value when unchecking a task.
|
||||||
|
# First, calculate the the value using the normal way for our first guess although
|
||||||
|
# it will be a bit off
|
||||||
|
currVal =
|
||||||
|
if task.value < -47.27 then -47.27
|
||||||
|
else if task.value > 21.27 then 21.27
|
||||||
|
else task.value
|
||||||
|
testVal = currVal + Math.pow(0.9747, currVal) * (if direction is 'down' then -1 else 1)
|
||||||
|
|
||||||
|
# Now keep moving closer to the original value until we get "close enough"
|
||||||
|
closeEnough = 0.00001
|
||||||
|
while true
|
||||||
|
# Check how close we are to the original value by computing the delta off our guess
|
||||||
|
# and looking at the difference between that and our current value.
|
||||||
|
calc = (testVal) + Math.pow(0.9747, testVal)
|
||||||
|
diff = currVal-calc
|
||||||
|
if Math.abs(diff) < closeEnough
|
||||||
|
break
|
||||||
|
if diff > 0
|
||||||
|
testVal -= diff
|
||||||
|
else
|
||||||
|
testVal += diff
|
||||||
|
|
||||||
|
# When we get close enough, return the difference between our approximated value
|
||||||
|
# and the current value. This will be the delta calculated from the original value
|
||||||
|
# before the task was checked.
|
||||||
|
nextDelta = testVal - currVal
|
||||||
|
|
||||||
|
# Checklists
|
||||||
|
if task.checklist?.length > 0
|
||||||
|
# If To-Do, point-match the TD per checklist item completed
|
||||||
|
if task.type is 'todo'
|
||||||
|
nextDelta *= (1 + _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0))
|
||||||
|
nextDelta
|
||||||
|
|
||||||
|
|
||||||
|
changeTaskValue = ->
|
||||||
# If multiple days have passed, multiply times days missed
|
# If multiple days have passed, multiply times days missed
|
||||||
_.times options.times, ->
|
_.times options.times, ->
|
||||||
# Each iteration calculate the nextDelta, which is then accumulated in the total delta.
|
# Each iteration calculate the nextDelta, which is then accumulated in the total delta.
|
||||||
# Calculates the next task.value based on direction
|
nextDelta = if not options.cron and direction is 'down' then calculateReverseDelta() else calculateDelta()
|
||||||
# Uses a capped inverse log y=.95^x, y>= -5
|
|
||||||
|
|
||||||
# Min/max on task redness
|
|
||||||
currVal =
|
|
||||||
if task.value < -47.27 then -47.27
|
|
||||||
else if task.value > 21.27 then 21.27
|
|
||||||
else task.value
|
|
||||||
nextDelta = Math.pow(0.9747, currVal) * (if direction is 'down' then -1 else 1)
|
|
||||||
|
|
||||||
# Checklists
|
|
||||||
if task.checklist?.length > 0
|
|
||||||
# If the Daily, only dock them them a portion based on their checklist completion
|
|
||||||
if direction is 'down' and task.type is 'daily' and options.cron
|
|
||||||
nextDelta *= (1 - _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0) / task.checklist.length)
|
|
||||||
# If To-Do, point-match the TD per checklist item completed
|
|
||||||
if task.type is 'todo'
|
|
||||||
nextDelta *= (1 + _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0))
|
|
||||||
|
|
||||||
unless task.type is 'reward'
|
unless task.type is 'reward'
|
||||||
if (user.preferences.automaticAllocation is true and user.preferences.allocationMode is 'taskbased' and !(task.type is 'todo' and direction is 'down')) then user.stats.training[task.attribute] += nextDelta
|
if (user.preferences.automaticAllocation is true and user.preferences.allocationMode is 'taskbased' and !(task.type is 'todo' and direction is 'down')) then user.stats.training[task.attribute] += nextDelta
|
||||||
# ===== STRENGTH =====
|
# ===== STRENGTH =====
|
||||||
|
|
@ -908,9 +949,11 @@ api.wrap = (user, main=true) ->
|
||||||
gpMod = (delta * task.priority * _crit * perBonus)
|
gpMod = (delta * task.priority * _crit * perBonus)
|
||||||
stats.gp +=
|
stats.gp +=
|
||||||
if task.streak
|
if task.streak
|
||||||
streakBonus = task.streak / 100 + 1 # eg, 1-day streak is 1.1, 2-day is 1.2, etc
|
currStreak = if direction is 'down' then task.streak-1 else task.streak
|
||||||
|
streakBonus = currStreak / 100 + 1 # eg, 1-day streak is 1.01, 2-day is 1.02, etc
|
||||||
afterStreak = gpMod * streakBonus
|
afterStreak = gpMod * streakBonus
|
||||||
user._tmp.streakBonus = afterStreak - gpMod if (gpMod > 0) # keep this on-hand for later, so we can notify streak-bonus
|
if currStreak > 0
|
||||||
|
user._tmp.streakBonus = afterStreak - gpMod if (gpMod > 0) # keep this on-hand for later, so we can notify streak-bonus
|
||||||
afterStreak
|
afterStreak
|
||||||
else gpMod
|
else gpMod
|
||||||
|
|
||||||
|
|
@ -925,7 +968,7 @@ api.wrap = (user, main=true) ->
|
||||||
|
|
||||||
switch task.type
|
switch task.type
|
||||||
when 'habit'
|
when 'habit'
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
# Add habit value to habit-history (if different)
|
# Add habit value to habit-history (if different)
|
||||||
if (delta > 0) then addPoints() else subtractPoints()
|
if (delta > 0) then addPoints() else subtractPoints()
|
||||||
|
|
||||||
|
|
@ -939,11 +982,13 @@ api.wrap = (user, main=true) ->
|
||||||
|
|
||||||
when 'daily'
|
when 'daily'
|
||||||
if options.cron
|
if options.cron
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
subtractPoints()
|
subtractPoints()
|
||||||
task.streak = 0 unless user.stats.buffs.streaks
|
task.streak = 0 unless user.stats.buffs.streaks
|
||||||
else
|
else
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
|
if direction is 'down'
|
||||||
|
delta = calculateDelta() # recalculate delta for unchecking so the gp and exp come out correctly
|
||||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||||
if direction is 'up'
|
if direction is 'up'
|
||||||
task.streak = if task.streak then task.streak + 1 else 1
|
task.streak = if task.streak then task.streak + 1 else 1
|
||||||
|
|
@ -958,11 +1003,13 @@ api.wrap = (user, main=true) ->
|
||||||
|
|
||||||
when 'todo'
|
when 'todo'
|
||||||
if options.cron
|
if options.cron
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
#don't touch stats on cron
|
#don't touch stats on cron
|
||||||
else
|
else
|
||||||
task.dateCompleted = if direction is 'up' then new Date else undefined
|
task.dateCompleted = if direction is 'up' then new Date else undefined
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
|
if direction is 'down'
|
||||||
|
delta = calculateDelta() # recalculate delta for unchecking so the gp and exp come out correctly
|
||||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||||
# MP++ per checklist item in ToDo, bonus per CLI
|
# MP++ per checklist item in ToDo, bonus per CLI
|
||||||
multiplier = _.max([(_.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),1)),1])
|
multiplier = _.max([(_.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),1)),1])
|
||||||
|
|
@ -975,7 +1022,7 @@ api.wrap = (user, main=true) ->
|
||||||
|
|
||||||
when 'reward'
|
when 'reward'
|
||||||
# Don't adjust values for rewards
|
# Don't adjust values for rewards
|
||||||
calculateDelta()
|
changeTaskValue()
|
||||||
# purchase item
|
# purchase item
|
||||||
stats.gp -= Math.abs(task.value)
|
stats.gp -= Math.abs(task.value)
|
||||||
num = parseFloat(task.value).toFixed(2)
|
num = parseFloat(task.value).toFixed(2)
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,11 @@ expectNoChange = (before,after) ->
|
||||||
_.each $w('stats items gear dailys todos rewards flags preferences'), (attr)->
|
_.each $w('stats items gear dailys todos rewards flags preferences'), (attr)->
|
||||||
expect(after[attr]).to.eql before[attr]
|
expect(after[attr]).to.eql before[attr]
|
||||||
|
|
||||||
|
expectClosePoints = (before, after, taskType) ->
|
||||||
|
expect( Math.abs(after.stats.exp - before.stats.exp) ).to.be.lessThan 0.0001
|
||||||
|
expect( Math.abs(after.stats.gp - before.stats.gp) ).to.be.lessThan 0.0001
|
||||||
|
expect( Math.abs(after["#{taskType}s"][0].value - before["#{taskType}s"][0].value) ).to.be.lessThan 0.0001
|
||||||
|
|
||||||
expectDayResetNoDamage = (b,a) ->
|
expectDayResetNoDamage = (b,a) ->
|
||||||
[before,after] = [_.cloneDeep(b), _.cloneDeep(a)]
|
[before,after] = [_.cloneDeep(b), _.cloneDeep(a)]
|
||||||
_.each after.dailys, (task,i) ->
|
_.each after.dailys, (task,i) ->
|
||||||
|
|
@ -379,10 +384,20 @@ describe 'Simple Scoring', ->
|
||||||
@after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}}
|
@after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}}
|
||||||
expectGainedPoints(@before, @after,'daily')
|
expectGainedPoints(@before, @after,'daily')
|
||||||
|
|
||||||
|
it 'Dailys : Up, Down', ->
|
||||||
|
@after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}}
|
||||||
|
@after.ops.score {params: {id: @after.dailys[0].id, direction: 'down'}}
|
||||||
|
expectClosePoints(@before, @after, 'daily')
|
||||||
|
|
||||||
it 'Todos : Up', ->
|
it 'Todos : Up', ->
|
||||||
@after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}}
|
@after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}}
|
||||||
expectGainedPoints(@before, @after,'todo')
|
expectGainedPoints(@before, @after,'todo')
|
||||||
|
|
||||||
|
it 'Todos : Up, Down', ->
|
||||||
|
@after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}}
|
||||||
|
@after.ops.score {params: {id: @after.todos[0].id, direction: 'down'}}
|
||||||
|
expectClosePoints(@before, @after, 'todo')
|
||||||
|
|
||||||
describe 'Cron', ->
|
describe 'Cron', ->
|
||||||
|
|
||||||
it 'computes shouldCron', ->
|
it 'computes shouldCron', ->
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue