mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-08-01 03:30:25 +00:00
Merge branch 'develop' into challenges
Conflicts: src/app/index.coffee
This commit is contained in:
commit
3cd23857ce
5 changed files with 84 additions and 148 deletions
|
|
@ -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"
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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) ->
|
||||
###
|
||||
|
|
|
|||
Loading…
Reference in a new issue