Files
habitica/archive/derby_controllers/misc.coffee
2013-09-01 18:40:08 -04:00

153 lines
5.7 KiB
CoffeeScript

_ = require 'lodash'
algos = require 'habitrpg-shared/script/algos'
items = require('habitrpg-shared/script/items').items
helpers = require('habitrpg-shared/script/helpers')
#TODO put this in habitrpg-shared
###
We can't always use refLists, but we often still need to get a positional path by id: eg, users.1234.tasks.5678.value
For arrays (which use indexes, not id-paths), here's a helper function so we can run indexedPath('users',:user.id,'tasks',:task.id,'value)
###
indexedPath = ->
_.reduce arguments, (m,v) =>
return v if !m #first iteration
return "#{m}.#{v}" if _.isString v #string paths
return "#{m}." + _.findIndex(@model.get(m),v)
, ''
taskInChallenge = (task) ->
return undefined unless task?.challenge
@model.at indexedPath.call(@, "groups.#{task.group.id}.challenges", {id:task.challenge}, "#{task.type}s", {id:task.id})
###
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
object properties, and it's very difficult to diff the two objects and find dot-separated paths to set. So we to first
clone our user object (if we don't do that, it screws with model.on() listeners, ping Tyler for an explaination),
perform the updates while tracking paths, then all the values at those paths
###
module.exports.score = (model, taskId, direction, allowUndo=false) ->
drop = undefined
delta = batchTxn model, (uObj, paths) ->
tObj = uObj.tasks[taskId]
# Stuff for undo
if allowUndo
tObjBefore = _.cloneDeep tObj
tObjBefore.completed = !tObjBefore.completed if tObjBefore.type in ['daily', 'todo']
previousUndo = model.get('_undo')
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
timeoutId = setTimeout (-> model.del('_undo')), 20000
model.set '_undo', {stats:_.cloneDeep(uObj.stats), task:tObjBefore, timeoutId: timeoutId}
delta = algos.score(uObj, tObj, direction, {paths})
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
drop = uObj._tmp?.drop
# Update challenge statistics
# FIXME put this in it's own batchTxn, make batchTxn model.at() ref aware (not just _user)
# FIXME use reflists for users & challenges
if (chalTask = taskInChallenge.call({model}, tObj)) and chalTask?.get()
model._dontPersist = false
chalTask.incr "value", delta
chal = model.at indexedPath.call({model}, "groups.#{tObj.group.id}.challenges", {id:tObj.challenge})
chalUser = -> indexedPath.call({model}, chal.path(), 'users', {id:uObj.id})
cu = model.at chalUser()
unless cu?.get()
chal.push "users", {id: uObj.id, name: helpers.username(uObj.auth, uObj.profile?.name)}
cu = model.at chalUser()
else
cu.set 'name', helpers.username(uObj.auth, uObj.profile?.name) # update their name incase it changed
cu.set "#{tObj.type}s.#{tObj.id}",
value: tObj.value
history: tObj.history
model._dontPersist = true
, done:->
if drop and $?
model.set '_drop', drop
$('#item-dropped-modal').modal 'show'
delta
###
Cleanup task-corruption (null tasks, rogue/invisible tasks, etc)
Obviously none of this should be happening, but we'll stop-gap until we can find & fix
Gotta love refLists! see https://github.com/lefnire/habitrpg/issues/803 & https://github.com/lefnire/habitrpg/issues/6343
###
module.exports.fixCorruptUser = (model) ->
user = model.at('_user')
tasks = user.get('tasks')
## Remove corrupted tasks
_.each tasks, (task, key) ->
unless task?.id? and task?.type?
user.del("tasks.#{key}")
delete tasks[key]
true
resetDom = false
batchTxn model, (uObj, paths, batch) ->
## fix https://github.com/lefnire/habitrpg/issues/1086
uniqPets = _.uniq(uObj.items.pets)
batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets)
if uObj.invitations?.guilds
uniqInvites = _.uniq(uObj.invitations.guilds)
batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations.guilds)
## Task List Cleanup
['habit','daily','todo','reward'].forEach (type) ->
# 1. remove duplicates
# 2. restore missing zombie tasks back into list
idList = uObj["#{type}Ids"]
taskIds = _.pluck( _.where(tasks, {type}), 'id')
union = _.union idList, taskIds
# 2. remove empty (grey) tasks
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
# There were indeed issues found, set the new list
if !_.isEqual(idList, preened)
batch.set("#{type}Ids", preened)
console.error uObj.id + "'s #{type}s were corrupt."
true
resetDom = !_.isEmpty(paths)
require('./browser').resetDom(model) if resetDom
module.exports.viewHelpers = (view) ->
#misc
view.fn "percent", (x, y) ->
x=1 if x==0
Math.round(x/y*100)
view.fn 'indexOf', (str1, str2) ->
return false unless str1 && str2
str1.indexOf(str2) != -1
view.fn "round", Math.round
view.fn "floor", Math.floor
view.fn "ceil", Math.ceil
view.fn "truarr", (num) -> num-1
view.fn 'count', (arr) -> arr?.length or 0
view.fn 'int',
get: (num) -> num
set: (num) -> [parseInt(num)]
view.fn 'indexedPath', indexedPath
#iCal
view.fn "encodeiCalLink", helpers.encodeiCalLink
#User
view.fn "gems", (balance) -> balance * 4
#Challenges
view.fn 'taskInChallenge', (task) ->
taskInChallenge.call(@,task)?.get()
view.fn 'taskAttrFromChallenge', (task, attr) ->
taskInChallenge.call(@,task)?.get(attr)
view.fn 'brokenChallengeLink', (task) ->
task?.challenge and !(taskInChallenge.call(@,task)?.get())
view.fn 'challengeMemberScore', (member, tType, tid) ->
Math.round(member["#{tType}s"]?[tid]?.value)