diff --git a/common/script/index.coffee b/common/script/index.coffee index 8d71e8e3d6..27b44392f1 100644 --- a/common/script/index.coffee +++ b/common/script/index.coffee @@ -1722,12 +1722,16 @@ api.wrap = (user, main=true) -> daily.completed = false return + multiDaysCountAsOneDay = true + # If the user does not log in for two or more days, cron (mostly) acts as if it were only one day. + # When site-wide difficulty settings are introduced, this can be a user preference option. + # Tally each task todoTally = 0 user.todos.forEach (task) -> # make uncompleted todos redder return unless task {id, completed} = task - delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(daysMissed), cron:true}}) + delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(multiDaysCountAsOneDay ? 1 : daysMissed), cron:true}}) absVal = if (completed) then Math.abs(task.value) else task.value todoTally += absVal @@ -1738,7 +1742,7 @@ api.wrap = (user, main=true) -> return unless task {id, completed} = task - # Deduct points for missed Daily tasks, but not for Todos (just increase todo's value) + # Deduct points for missed Daily tasks EvadeTask = 0 scheduleMisses = daysMissed if completed @@ -1746,13 +1750,15 @@ api.wrap = (user, main=true) -> else # dailys repeat, so need to calculate how many they've missed according to their own schedule scheduleMisses = 0 - _.times daysMissed, (n) -> + for n in [0...daysMissed] thatDay = moment(now).subtract({days: n + 1}) if api.shouldDo(thatDay.toDate(), task, user.preferences) scheduleMisses++ if user.stats.buffs.stealth user.stats.buffs.stealth-- EvadeTask++ + if multiDaysCountAsOneDay + break if scheduleMisses > EvadeTask perfect = false @@ -1762,7 +1768,7 @@ api.wrap = (user, main=true) -> dailyChecked += fractionChecked else dailyDueUnchecked += 1 - delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(scheduleMisses-EvadeTask), cron:true}}) + delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(multiDaysCountAsOneDay ? 1 : (scheduleMisses-EvadeTask)), cron:true}}) # Apply damage from a boss, less damage for Trivial priority (difficulty) user.party.quest.progress.down += delta * (if task.priority < 1 then task.priority else 1) diff --git a/test/common/algos.mocha.coffee b/test/common/algos.mocha.coffee index 3173ba5e55..5be39a7ae3 100644 --- a/test/common/algos.mocha.coffee +++ b/test/common/algos.mocha.coffee @@ -644,7 +644,7 @@ describe 'Cron', -> # paths = {};algos.cron user, {paths} # expect(paths.lastCron).to.be true # busted cron (was set to after today's date) - it 'only dailies & todos are effected', -> + it 'only dailies & todos are affected', -> {before,after} = beforeAfter({daysAgo:1}) before.dailys = before.todos = after.dailys = after.todos = [] after.fns.cron() @@ -732,9 +732,19 @@ describe 'Cron', -> expect(after).toHaveGP 0 # but they devalue - expect(after.todos[0].value).to.be.lessThan before.todos[0].value + expect(before.todos[0].value).to.be 0 # sanity check for task setup + expect(after.todos[0].value).to.be -1 # the actual test expect(after.history.todos).to.have.length 1 + it '2 days missed', -> + {before,after} = beforeAfter({daysAgo:2}) + before.dailys = after.dailys = [] + after.fns.cron() + + # todos devalue by only one day's worth of devaluation + expect(before.todos[0].value).to.be 0 # sanity check for task setup + expect(after.todos[0].value).to.be -1 # the actual test + # I used hard-coded dates here instead of 'now' so the tests don't fail # when you run them between midnight and dayStart. Nothing worse than # intermittent failures. diff --git a/test/common/dailies.coffee b/test/common/dailies.coffee index fc88adfbf3..d06c291f27 100644 --- a/test/common/dailies.coffee +++ b/test/common/dailies.coffee @@ -49,8 +49,8 @@ newUser = (addTasks=true)-> user.ops.addTask {body: {type: task, id: shared.uuid()}} user -cron = (usr) -> - usr.lastCron = moment().subtract(1,'days') +cron = (usr, missedDays=1) -> + usr.lastCron = moment().subtract(missedDays,'days') usr.fns.cron() describe 'daily/weekly that repeats everyday (default)', -> @@ -59,7 +59,6 @@ describe 'daily/weekly that repeats everyday (default)', -> weekly = null describe 'when startDate is in the future', -> - beforeEach -> user = newUser() user.dailys = [ @@ -141,19 +140,17 @@ describe 'daily/weekly that repeats everyday (default)', -> it 'is due on startDate', -> daily_due_today = shared.shouldDo moment(), daily daily_due_on_start_date = shared.shouldDo moment().add(7, 'days'), daily - + expect(daily_due_today).to.be false expect(daily_due_on_start_date).to.be true weekly_due_today = shared.shouldDo moment(), weekly weekly_due_on_start_date = shared.shouldDo moment().add(7, 'days'), weekly - + expect(weekly_due_today).to.be false expect(weekly_due_on_start_date).to.be true describe 'when startDate is in the past', -> - completeDaily = null - beforeEach -> user = newUser() user.dailys = [ @@ -168,9 +165,14 @@ describe 'daily/weekly that repeats everyday (default)', -> expect(user.stats.hp).to.be.lessThan 50 it 'decreases value on cron if daily is incomplete', -> - cron(user) - expect(daily.value).to.be.lessThan 0 - expect(weekly.value).to.be.lessThan 0 + cron(user, 1) + expect(daily.value).to.be -1 + expect(weekly.value).to.be -1 + + it 'decreases value on cron once only if daily is incomplete and multiple days are missed', -> + cron(user, 7) + expect(daily.value).to.be -1 + expect(weekly.value).to.be -1 it 'resets checklists if daily is not marked as complete', -> checklist = [ @@ -196,7 +198,7 @@ describe 'daily/weekly that repeats everyday (default)', -> _.each daily.checklist, (box)-> expect(box.completed).to.be false - + _.each weekly.checklist, (box)-> expect(box.completed).to.be false @@ -231,8 +233,6 @@ describe 'daily/weekly that repeats everyday (default)', -> expect(box.completed).to.be false describe 'when startDate is today', -> - completeDaily = null - beforeEach -> user = newUser() user.dailys = [ @@ -276,7 +276,7 @@ describe 'daily/weekly that repeats everyday (default)', -> _.each daily.checklist, (box)-> expect(box.completed).to.be false - + _.each weekly.checklist, (box)-> expect(box.completed).to.be false @@ -328,3 +328,91 @@ describe 'daily that repeats every x days', -> isDue = shared.shouldDo moment().add(day, 'days'), daily expect(isDue).to.be true if day % due == 0 expect(isDue).to.be false if day % due != 0 + +describe 'daily that repeats every X days when multiple days are missed', -> + everyX = 3 + startDateDaysAgo = everyX * 3 + user = null + daily = null + + describe 'including missing a due date', -> + missedDays = everyX * 2 + 1 + + beforeEach -> + user = newUser() + user.dailys = [ + shared.taskDefaults({type:'daily', startDate: moment().subtract(startDateDaysAgo, 'days'), frequency: 'daily', everyX: everyX}) + ] + daily = user.dailys[0] + + it 'decreases value on cron once only if daily is incomplete', -> + cron(user, missedDays) + expect(daily.value).to.be -1 + + it 'resets checklists if daily is incomplete', -> + checklist = [ + { + 'text' : '1', + 'id' : 'checklist-one', + 'completed' : true + } + ] + daily.checklist = checklist + cron(user, missedDays) + _.each daily.checklist, (box)-> + expect(box.completed).to.be false + + it 'resets checklists if daily is marked as complete', -> + checklist = [ + { + 'text' : '1', + 'id' : 'checklist-one', + 'completed' : true + } + ] + daily.checklist = checklist + daily.completed = true + cron(user, missedDays) + _.each daily.checklist, (box)-> + expect(box.completed).to.be false + + describe 'but not missing a due date', -> + missedDays = everyX - 1 + + beforeEach -> + user = newUser() + user.dailys = [ + shared.taskDefaults({type:'daily', startDate: moment().subtract(startDateDaysAgo, 'days'), frequency: 'daily', everyX: everyX}) + ] + daily = user.dailys[0] + + it 'does not decrease value on cron', -> + cron(user, missedDays) + expect(daily.value).to.be 0 + + it 'does not reset checklists if daily is incomplete', -> + checklist = [ + { + 'text' : '1', + 'id' : 'checklist-one', + 'completed' : true + } + ] + daily.checklist = checklist + cron(user, missedDays) + _.each daily.checklist, (box)-> + expect(box.completed).to.be true + + it 'resets checklists if daily is marked as complete', -> + checklist = [ + { + 'text' : '1', + 'id' : 'checklist-one', + 'completed' : true + } + ] + daily.checklist = checklist + daily.completed = true + cron(user, missedDays) + _.each daily.checklist, (box)-> + expect(box.completed).to.be false