Merge branch 'develop' into challenges

Conflicts:
	src/app/index.coffee
This commit is contained in:
Tyler Renelle 2013-05-29 11:27:25 +01:00
commit 3cd23857ce
5 changed files with 84 additions and 148 deletions

View file

@ -1,6 +1,8 @@
browser = require './browser'
items = require './items'
algos = require 'habitrpg-shared/script/algos'
misc = require './misc'
helpers = require 'habitrpg-shared/script/helpers'
moment = require 'moment'
_ = require 'lodash'
@ -31,26 +33,12 @@ module.exports.app = (appExports, model) ->
items.updateStore(model)
appExports.reset = (e, el) ->
batch = new BatchUpdate(model)
batch.startTransaction()
taskTypes = ['habit', 'daily', 'todo', 'reward']
batch.set 'tasks', {}
taskTypes.forEach (type) -> batch.set "#{type}Ids", []
#batch.set 'balance', 1 if user.get('balance') < 1 #only if they haven't manually bought gems
# Reset stats
batch.set 'stats.hp', 50
batch.set 'stats.lvl', 1
batch.set 'stats.gp', 0
batch.set 'stats.exp', 0
# Reset items
batch.set 'items.armor', 0
batch.set 'items.weapon', 0
batch.set 'items.head', 0
batch.set 'items.shield', 0
misc.batchTxn model, (uObj, paths, batch) ->
batch.set 'tasks', {}
['habit', 'daily', 'todo', 'reward'].forEach (type) -> batch.set("#{type}Ids", [])
_.each {hp:50, lvl:1, gp:0, exp:0}, (v,k) -> batch.set("stats.#{k}",v)
_.each {armor:0, weapon:0, head:0, shield:0}, (v,k) -> batch.set("items.#{k}",v)
items.updateStore(model)
batch.commit()
browser.resetDom(model)
appExports.closeNewStuff = (e, el) ->
@ -68,71 +56,15 @@ module.exports.app = (appExports, model) ->
appExports.customizeArmorSet = (e, el) ->
user.set 'preferences.armorSet', $(el).attr('data-value')
appExports.restoreSave = (e, el) ->
batch = new BatchUpdate(model)
batch.startTransaction()
$('#restore-form input').each ->
batch.set $(this).attr('data-for'), parseInt($(this).val() || 1)
batch.commit()
appExports.restoreSave = ->
misc.batchTxn model, (uObj, paths, batch) ->
$('#restore-form input').each ->
[path, val] = [$(this).attr('data-for'), parseInt($(this).val() || 1)]
batch.set(path,val)
appExports.toggleHeader = (e, el) ->
user.set 'preferences.hideHeader', !user.get('preferences.hideHeader')
appExports.deleteAccount = (e, el) ->
model.del "users.#{user.get('id')}", ->
window.location.href = "/logout"
module.exports.BatchUpdate = BatchUpdate = (model) ->
user = model.at("_user")
transactionInProgress = false
obj = null
updates = {}
{
user: user
obj: ->
obj ?= model.get 'users.'+user.get('id')
return obj
startTransaction: ->
# start a batch transaction - nothing between now and @commit() will be set immediately
transactionInProgress = true
model._dontPersist = true
@obj()
###
Handles updating the user model. If this is an en-mass operation (eg, server cron), changes are queued
but not actually set to the model. It also modifies userObj in case you need to access properties manually later.
If transaction not in progress, it just runs standard model.set()
###
set: (path, val) ->
updates[path] = val if transactionInProgress
user.set(path, val)
###
Hack to get around dom bindings being lost if parent objects are replaced whole-sale
eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok
###
setStats: (stats) ->
stats ?= obj.stats
that = @
_.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]; true
# queue: (path, val) ->
# # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790
# arr = path.split('.')
# arr.reduce (curr, next, index) ->
# if (arr.length - 1) == index
# curr[next] = val
# curr[next]
# , obj
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 = {}
}
location.href = "/logout"

View file

@ -40,30 +40,25 @@ cleanupCorruptTasks = (model) ->
delete tasks[key]
true
batch = null
misc.batchTxn model, (uObj, paths, batch) ->
## Task List Cleanup
['habit','daily','todo','reward'].forEach (type) ->
## 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:type}), 'id')
union = _.union idList, taskIds
# 1. remove duplicates
# 2. restore missing zombie tasks back into list
idList = user.get("#{type}Ids")
taskIds = _.pluck( _.where(tasks, {type:type}), 'id')
union = _.union idList, taskIds
# 2. remove empty (grey) tasks
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
# 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
# There were indeed issues found, set the new list
if !_.isEqual(idList, preened)
unless batch?
batch = new require('./character').BatchUpdate(model)
batch.startTransaction()
batch.set("#{type}Ids", preened)
console.error user.get('id') + "'s #{type}s were corrupt."
true
batch.commit() if batch?
###
Subscribe to the user, the users's party (meta info like party name, member ids, etc), and the party's members. 3 subscriptions.
@ -137,6 +132,7 @@ get '/', (page, model, params, next) ->
ready (model) ->
user = model.at('_user')
browser = require './browser'
character = require './character'
require('./character').app(exports, model)
require('./tasks').app(exports, model)
@ -154,18 +150,14 @@ ready (model) ->
###
Cron
###
uObj = misc.hydrate(user.get())
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
_.each ['habit','daily','todo','reward'], (type) ->
uObj["#{type}s"] = _.where(uObj.tasks, {type:type}); true
paths = {}
algos.cron(uObj, {paths:paths})
if !_.isEmpty(paths)
if paths['lastCron'] and _.size(paths) is 1
user.set "lastCron", uObj.lastCron
else
lostHp = delete paths['stats.hp'] # we'll set this manually so we can get a cool animation
_.each paths, (v,k) -> user.pass({cron:true}).set(k,helpers.dotGet(k, uObj)); true
if lostHp
misc.batchTxn model, (uObj, paths) ->
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
_.each ['habit','daily','todo','reward'], (type) -> uObj["#{type}s"] = _.where(uObj.tasks, {type}); true
algos.cron uObj, {paths}
return if _.isEmpty(paths) or (paths['lastCron'] and _.size(paths) is 1)
if lostHp = delete paths['stats.hp'] # we'll set this manually so we can get a cool animation
setTimeout ->
browser.resetDom(model)
setTimeout (-> user.set('stats.hp', uObj.stats.hp)), 750
user.set 'stats.hp', uObj.stats.hp
, 750
,{cron:true}

View file

@ -2,6 +2,24 @@ _ = require 'lodash'
algos = require 'habitrpg-shared/script/algos'
items = require('habitrpg-shared/script/items').items
helpers = require('habitrpg-shared/script/helpers')
character = require('./character')
module.exports.batchTxn = batchTxn = (model, cb, options) ->
user = model.at("_user")
uObj = hydrate(user.get()) # see https://github.com/codeparty/racer/issues/116
batch =
set: (k,v) -> helpers.dotSet(k,v,uObj); paths[k] = true
get: (k) -> helpers.dotGet(k,uObj)
paths = {}
model._dontPersist = true
cb uObj, paths, batch
_.each paths, (v,k) -> user.pass({cron:options?.cron}).set(k,helpers.dotGet(k, uObj));true
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
unless _.isEmpty paths
setOps = _.reduce paths, ((m,v,k)-> m[k] = helpers.dotGet(k,uObj);m), {}
user.set "update__", setOps
###
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
@ -11,28 +29,24 @@ helpers = require('habitrpg-shared/script/helpers')
###
module.exports.score = (model, taskId, direction, allowUndo=false) ->
#return setTimeout( (-> score(taskId, direction)), 500) if model._txnQueue.length > 0
user = model.at("_user")
batchTxn model, (uObj, paths) ->
tObj = uObj.tasks[taskId]
uObj = hydrate(user.get()) # see https://github.com/codeparty/racer/issues/116
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}
# 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}
paths = {}
delta = algos.score(uObj, tObj, direction, {paths})
_.each paths, (v,k) -> user.set(k,helpers.dotGet(k, uObj)); true
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
if uObj._tmp?.drop and $?
model.set '_drop', uObj._tmp.drop
$('#item-dropped-modal').modal 'show'
delta
delta = algos.score(uObj, tObj, direction, {paths})
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
if uObj._tmp?.drop and $?
model.set '_drop', uObj._tmp.drop
$('#item-dropped-modal').modal 'show'
delta
###
Make sure model.get() returns all properties, see https://github.com/codeparty/racer/issues/116
@ -118,4 +132,4 @@ module.exports.viewHelpers = (view) ->
if gType is 'party'
findAttr @model.get("_party.challenges")
else if gType is 'guild'
findAttr _.find(@model.get("_guilds"),{id:gid}).challenges
findAttr _.find(@model.get("_guilds"),{id:gid}).challenges

View file

@ -134,19 +134,16 @@ module.exports.app = (appExports, model) ->
appExports.undo = () ->
undo = model.get '_undo'
clearTimeout(undo.timeoutId) if undo?.timeoutId
batch = character.BatchUpdate(model)
batch.startTransaction()
model.del '_undo'
_.each undo.stats, (val, key) -> batch.set "stats.#{key}", val; true
_.each undo.stats, (val, key) -> user.set "stats.#{key}", val; true
taskPath = "tasks.#{undo.task.id}"
_.each undo.task, (val, key) ->
return true if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
if key is 'completed'
user.pass({cron:true}).set("#{taskPath}.completed",val)
else
batch.set "#{taskPath}.#{key}", val
user.set "#{taskPath}.#{key}", val
true
batch.commit()
appExports.tasksToggleAdvanced = (e, el) ->
$(el).next('.advanced-option').toggleClass('visuallyhidden')

View file

@ -1,5 +1,5 @@
_ = require 'lodash'
character = require "../app/character"
misc = require "../app/misc"
module.exports.middleware = (req, res, next) ->
model = req.getModel()
@ -33,12 +33,13 @@ module.exports.app = (appExports, model) ->
###
Buy Reroll Button
###
appExports.buyReroll = (e, el, next) ->
batch = new character.BatchUpdate(model)
obj = model.get('_user')
batch.set 'balance', obj.balance-1
_.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type is 'reward';true
batch.commit()
appExports.buyReroll = ->
misc.batchTxn model, (uObj, paths, batch) ->
uObj.balance -= 1; paths['balance'] =1
_.each uObj.tasks, (task) ->
batch.set("tasks.#{task.id}.value", 0) unless task.type is 'reward'
true
$('#reroll-modal').modal('hide')
module.exports.routes = (expressApp) ->
###