don't need public/private paths after all, can check path requests in store accessControl callbacks

This commit is contained in:
Tyler Renelle
2013-02-04 22:35:07 -05:00
parent 6966f6a439
commit 9723f3c37a
10 changed files with 186 additions and 188 deletions

View File

@@ -52,42 +52,40 @@ db.users.find().forEach(function(user){
// Note 'public' and 'private' are reserved words // Note 'public' and 'private' are reserved words
var newUser = { var newUser = {
auth: user.auth, // we need this top-level due to derby-auth auth: user.auth, // we need this top-level due to derby-auth
pub:{ apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid()
party: null, preferences: {
invitations: [], armorSet: user.preferences.armorSet || 'v1',
items: { gender: user.preferences.gender || 'm'
armor: user.items.armor || 0,
weapon: user.items.weapon || 0
}, },
balance: user.balance || 2,
lastCron: user.lastCron || +new Date,
history: user.history || [],
stats: { stats: {
gp: user.stats.money || 0, gp: user.stats.money || 0,
hp: user.stats.hp || 50, hp: user.stats.hp || 50,
exp: user.stats.exp || 0, exp: user.stats.exp || 0,
lvl: user.stats.lvl || 1 lvl: user.stats.lvl || 1
}, },
preferences: { items: {
armorSet: user.preferences.armorSet || 'v1', armor: user.items.armor || 0,
gender: user.preferences.gender || 'm' weapon: user.items.weapon || 0
}
}, },
priv: {
balance: user.balance || 2,
lastCron: user.lastCron || +new Date,
tasks: user.tasks || {}, tasks: user.tasks || {},
history: user.history || [],
apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid()
idLists: { idLists: {
habit:user.habitList || [], habit: user.habitIds || [],
daily:user.dailyList || [], daily: user.dailyIds || [],
todo:user.todoList || [], todo: user.todoIds || [],
reward:user.rewardList || [] reward: user.rewardIds || []
}, },
flags: { flags: {
partyEnabled: false, partyEnabled: false,
itemsEnabled: user.items.itemsEnabled || false, itemsEnabled: user.items.itemsEnabled || false,
kickstarter: user.notifications.kickstarter || 'show', kickstarter: user.notifications.kickstarter || 'show',
ads: user.flags.ads || null // null because it's set on registration ads: user.flags.ads || null // null because it's set on registration
} },
party: {
current: null,
invitation: null
} }
}; };

View File

@@ -85,7 +85,7 @@ module.exports.setupGrowlNotifications = (model) ->
statsNotification = (html, type) -> statsNotification = (html, type) ->
#don't show notifications if user dead #don't show notifications if user dead
return if user.get('pub.stats.lvl') == 0 return if user.get('stats.lvl') == 0
$.bootstrapGrowl html, $.bootstrapGrowl html,
type: type # (null, 'info', 'error', 'success') type: type # (null, 'info', 'error', 'success')
top_offset: 20 top_offset: 20
@@ -95,7 +95,7 @@ module.exports.setupGrowlNotifications = (model) ->
allow_dismiss: true allow_dismiss: true
stackup_spacing: 10 # spacing between consecutive stacecked growls. stackup_spacing: 10 # spacing between consecutive stacecked growls.
user.on 'set', 'priv.flags.itemsEnabled', (captures, args) -> user.on 'set', 'flags.itemsEnabled', (captures, args) ->
return unless captures == true return unless captures == true
message = "Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information." message = "Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information."
$('ul.items').popover $('ul.items').popover
@@ -109,7 +109,7 @@ module.exports.setupGrowlNotifications = (model) ->
</div>" </div>"
$('ul.items').popover 'show' $('ul.items').popover 'show'
user.on 'set', 'priv.flags.partyEnabled', (captures, args) -> user.on 'set', 'flags.partyEnabled', (captures, args) ->
return unless captures == true return unless captures == true
message = "Congratulations, you have unlocked the Party System! You can now group with your friends by adding their User Ids." message = "Congratulations, you have unlocked the Party System! You can now group with your friends by adding their User Ids."
$('#add-party-button').popover $('#add-party-button').popover
@@ -125,13 +125,13 @@ module.exports.setupGrowlNotifications = (model) ->
# Setup listeners which trigger notifications # Setup listeners which trigger notifications
user.on 'set', 'pub.stats.hp', (captures, args) -> user.on 'set', 'stats.hp', (captures, args) ->
num = captures - args num = captures - args
rounded = Math.abs(num.toFixed(1)) rounded = Math.abs(num.toFixed(1))
if num < 0 if num < 0
statsNotification "<i class='icon-heart'></i>HP -#{rounded}", 'error' # lost hp from purchase statsNotification "<i class='icon-heart'></i>HP -#{rounded}", 'error' # lost hp from purchase
user.on 'set', 'pub.stats.gp', (captures, args) -> user.on 'set', 'stats.gp', (captures, args) ->
num = captures - args num = captures - args
rounded = Math.abs(num.toFixed(1)) rounded = Math.abs(num.toFixed(1))
# made purchase # made purchase
@@ -143,6 +143,6 @@ module.exports.setupGrowlNotifications = (model) ->
num = Math.abs(num) num = Math.abs(num)
statsNotification "<i class='icon-star'></i>Exp,GP +#{rounded}", 'success' statsNotification "<i class='icon-star'></i>Exp,GP +#{rounded}", 'success'
user.on 'set', 'pub.stats.lvl', (captures, args) -> user.on 'set', 'stats.lvl', (captures, args) ->
if captures > args if captures > args
statsNotification('<i class="icon-chevron-up"></i> Level Up!', 'info') statsNotification('<i class="icon-chevron-up"></i> Level Up!', 'info')

View File

@@ -57,9 +57,9 @@ module.exports.viewHelpers = (view) ->
return gp/0.25 return gp/0.25
view.fn 'currentArmor', (user) -> view.fn 'currentArmor', (user) ->
user = { pub:{ items: {armor:0}, preferences: {gender:'m', armorSet:'v1'}}} unless user? user = { items: {armor:0}, preferences: {gender:'m', armorSet:'v1'}} unless user?
armor = user.pub.items.armor armor = user.items.armor
{gender, armorSet} = user.pub.items {gender, armorSet} = user.preferences
if gender == 'f' if gender == 'f'
str = "armor#{armor}_f" str = "armor#{armor}_f"
if parseInt(armor) > 1 if parseInt(armor) > 1

View File

@@ -16,10 +16,10 @@ _ = require('underscore')
setupListReferences = (model) -> setupListReferences = (model) ->
taskTypes = ['habit', 'daily', 'todo', 'reward'] taskTypes = ['habit', 'daily', 'todo', 'reward']
_.each taskTypes, (type) -> model.refList "_#{type}List", "_user.priv.tasks", "_user.priv.idLists.#{type}" _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.tasks", "_user.idLists.#{type}"
setupModelFns = (model) -> setupModelFns = (model) ->
model.fn '_tnl', '_user.pub.stats.lvl', (lvl) -> model.fn '_tnl', '_user.stats.lvl', (lvl) ->
# see https://github.com/lefnire/habitrpg/issues/4 # see https://github.com/lefnire/habitrpg/issues/4
# also update in scoring.coffee. TODO create a function accessible in both locations # also update in scoring.coffee. TODO create a function accessible in both locations
(lvl*100)/5 (lvl*100)/5
@@ -51,8 +51,8 @@ get '/', (page, model, next) ->
# Setup Item Store # Setup Item Store
_view.items = _view.items =
armor: content.items.armor[parseInt(obj.pub.items?.armor || 0) + 1] armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1]
weapon: content.items.weapon[parseInt(obj.pub.items?.weapon || 0) + 1] weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1]
potion: content.items.potion potion: content.items.potion
reroll: content.items.reroll reroll: content.items.reroll
@@ -65,8 +65,8 @@ get '/', (page, model, next) ->
setupModelFns(model) setupModelFns(model)
# Subscribe to friends # Subscribe to friends
if !_.isEmpty(obj.pub.party) if !_.isEmpty(obj.party)
model.subscribe model.query('users').party(obj.pub.party), (err, party) -> model.subscribe model.query('users').party(obj.party), (err, party) ->
model.ref '_party', party model.ref '_party', party
page.render() page.render()
@@ -83,8 +83,8 @@ ready (model) ->
scoring.setModel(model) scoring.setModel(model)
#set cron immediately #set cron immediately
lastCron = user.get('priv.lastCron') lastCron = user.get('lastCron')
user.set('priv.lastCron', +new Date) if (!lastCron? or lastCron == 'new') user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new')
# Setup model in scoring functions # Setup model in scoring functions
scoring.cron(resetDom) scoring.cron(resetDom)
@@ -98,7 +98,7 @@ ready (model) ->
require('../server/private').app(exports, model) require('../server/private').app(exports, model)
user.on 'set', 'priv.tasks.*.completed', (i, completed, previous, isLocal, passed) -> user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) ->
return if passed? && passed.cron # Don't do this stuff on cron return if passed? && passed.cron # Don't do this stuff on cron
direction = () -> direction = () ->
return 'up' if completed==true and previous == false return 'up' if completed==true and previous == false
@@ -106,7 +106,7 @@ ready (model) ->
throw new Error("Direction neither 'up' nor 'down' on checkbox set.") throw new Error("Direction neither 'up' nor 'down' on checkbox set.")
# Score the user based on todo task # Score the user based on todo task
task = user.at("priv.tasks.#{i}") task = user.at("tasks.#{i}")
scoring.score(i, direction()) scoring.score(i, direction())
exports.addTask = (e, el, next) -> exports.addTask = (e, el, next) ->
@@ -145,10 +145,10 @@ ready (model) ->
id = $(e.target).parents('li.task').attr('data-id') id = $(e.target).parents('li.task').attr('data-id')
return unless id? return unless id?
task = user.at "priv.tasks.#{id}" task = user.at "tasks.#{id}"
type = task.get('type') type = task.get('type')
history = task.get('priv.history') history = task.get('history')
if history and history.length>2 if history and history.length>2
# prevent delete-and-recreate hack on red tasks # prevent delete-and-recreate hack on red tasks
if task.get('value') < 0 if task.get('value') < 0
@@ -168,22 +168,22 @@ ready (model) ->
# fix when query subscriptions implemented properly # fix when query subscriptions implemented properly
$('[rel=tooltip]').tooltip('hide') $('[rel=tooltip]').tooltip('hide')
ids = user.get("priv.idLists.#{type}") ids = user.get("idLists.#{type}")
ids.splice(ids.indexOf(id),1) ids.splice(ids.indexOf(id),1)
user.del('priv.tasks.'+id) user.del('tasks.'+id)
user.set("priv.idLists.#{type}", ids) user.set("idLists.#{type}", ids)
exports.clearCompleted = (e, el) -> exports.clearCompleted = (e, el) ->
todoIds = user.get('priv.idLists.todo') todoIds = user.get('idLists.todo')
removed = false removed = false
_.each model.get('_todoList'), (task) -> _.each model.get('_todoList'), (task) ->
if task.completed if task.completed
removed = true removed = true
user.del('priv.tasks.'+task.id) user.del('tasks.'+task.id)
todoIds.splice(todoIds.indexOf(task.id), 1) todoIds.splice(todoIds.indexOf(task.id), 1)
if removed if removed
user.set('priv.idLists.todo', todoIds) user.set('idLists.todo', todoIds)
exports.toggleDay = (e, el) -> exports.toggleDay = (e, el) ->
task = model.at(e.target) task = model.at(e.target)
@@ -226,22 +226,22 @@ ready (model) ->
#TODO: this should be working but it's not. so instead, i'm passing all needed values as data-attrs #TODO: this should be working but it's not. so instead, i'm passing all needed values as data-attrs
# item = model.at(e.target) # item = model.at(e.target)
gp = user.get 'pub.stats.gp' gp = user.get 'stats.gp'
[type, value, index] = [ $(el).attr('data-type'), $(el).attr('data-value'), $(el).attr('data-index') ] [type, value, index] = [ $(el).attr('data-type'), $(el).attr('data-value'), $(el).attr('data-index') ]
return if gp < value return if gp < value
user.set 'pub.stats.gp', gp - value user.set 'stats.gp', gp - value
if type == 'armor' if type == 'armor'
user.set 'pub.items.armor', index user.set 'items.armor', index
model.set '_view.items.armor', content.items.armor[parseInt(index) + 1] model.set '_view.items.armor', content.items.armor[parseInt(index) + 1]
else if type == 'weapon' else if type == 'weapon'
user.set 'pub.items.weapon', index user.set 'items.weapon', index
model.set '_view.items.weapon', content.items.weapon[parseInt(index) + 1] model.set '_view.items.weapon', content.items.weapon[parseInt(index) + 1]
else if type == 'potion' else if type == 'potion'
hp = user.get 'pub.stats.hp' hp = user.get 'stats.hp'
hp += 15 hp += 15
hp = 50 if hp > 50 hp = 50 if hp > 50
user.set 'pub.stats.hp', hp user.set 'stats.hp', hp
exports.score = (e, el, next) -> exports.score = (e, el, next) ->
direction = $(el).attr('data-direction') direction = $(el).attr('data-direction')
@@ -252,14 +252,14 @@ ready (model) ->
revive = (batch) -> revive = (batch) ->
# Reset stats # Reset stats
batch.set 'pub.stats.hp', 50 batch.set 'stats.hp', 50
batch.set 'pub.stats.lvl', 1 batch.set 'stats.lvl', 1
batch.set 'pub.stats.gp', 0 batch.set 'stats.gp', 0
batch.set 'pub.stats.exp', 0 batch.set 'stats.exp', 0
# Reset items # Reset items
batch.set 'pub.items.armor', 0 batch.set 'items.armor', 0
batch.set 'pub.items.weapon', 0 batch.set 'items.weapon', 0
# Reset item store # Reset item store
model.set '_view.items.armor', content.items.armor[1] model.set '_view.items.armor', content.items.armor[1]
@@ -275,33 +275,33 @@ ready (model) ->
batch = new schema.BatchUpdate(model) batch = new schema.BatchUpdate(model)
batch.startTransaction() batch.startTransaction()
taskTypes = ['habit', 'daily', 'todo', 'reward'] taskTypes = ['habit', 'daily', 'todo', 'reward']
batch.set 'priv.tasks', {} batch.set 'tasks', {}
_.each taskTypes, (type) -> batch.set "priv.idLists.#{type}", [] _.each taskTypes, (type) -> batch.set "idLists.#{type}", []
batch.set 'priv.balance', 2 if user.get('priv.balance') < 2 #only if they haven't manually bought tokens batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens
revive(batch, true) revive(batch, true)
batch.commit() batch.commit()
resetDom(model) resetDom(model)
exports.closeKickstarterNofitication = (e, el) -> exports.closeKickstarterNofitication = (e, el) ->
user.set('priv.flags.kickstarter', 'hide') user.set('flags.kickstarter', 'hide')
exports.setMale = -> user.set('pub.preferences.gender', 'm') exports.setMale = -> user.set('preferences.gender', 'm')
exports.setFemale = -> user.set('pub.preferences.gender', 'f') exports.setFemale = -> user.set('preferences.gender', 'f')
exports.setArmorsetV1 = -> user.set('pub.preferences.armorSet', 'v1') exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1')
exports.setArmorsetV2 = -> user.set('pub.preferences.armorSet', 'v2') exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2')
exports.addParty = -> exports.addParty = ->
id = model.get('_newPartyMember').replace(/[\s"]/g, '') id = model.get('_newPartyMember').replace(/[\s"]/g, '')
debugger debugger
return if _.isEmpty(id) return if _.isEmpty(id)
if user.get('pub.party').indexOf(id) != -1 if user.get('party').indexOf(id) != -1
model.set "_view.addPartyError", "#{id} already in party." model.set "_view.addPartyError", "#{id} already in party."
return return
query = model.query('users').party([id]) query = model.query('users').party([id])
model.fetch query, (err, users) -> model.fetch query, (err, users) ->
partyMember = users.at(0).get() partyMember = users.at(0).get()
if partyMember?.id? if partyMember?.id?
user.push('pub.party', id) user.push('party', id)
$('#add-party-modal').modal('hide') $('#add-party-modal').modal('hide')
window.location.reload() #TODO break old subscription, setup new subscript, remove this reload window.location.reload() #TODO break old subscription, setup new subscript, remove this reload
model.set '_newPartyMember', '' model.set '_newPartyMember', ''
@@ -310,6 +310,6 @@ ready (model) ->
exports.emulateNextDay = -> exports.emulateNextDay = ->
yesterday = +moment().subtract('days', 1).toDate() yesterday = +moment().subtract('days', 1).toDate()
user.set 'priv.lastCron', yesterday user.set 'lastCron', yesterday
window.location.reload() window.location.reload()

View File

@@ -6,13 +6,13 @@ derby = require 'derby'
userSchema = userSchema =
# _id # _id
pub:
stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } stats: { gp: 0, exp: 0, lvl: 1, hp: 50 }
party: null party: {
invitations: [] current: null
invitation: null
}
items: { armor: 0, weapon: 0 } items: { armor: 0, weapon: 0 }
preferences: { gender: 'm', armorSet: 'v1' } preferences: { gender: 'm', armorSet: 'v1' }
priv:
idLists: idLists:
habit: [] habit: []
daily: [] daily: []
@@ -31,37 +31,37 @@ userSchema =
module.exports.newUserObject = -> module.exports.newUserObject = ->
# deep clone, else further new users get duplicate objects # deep clone, else further new users get duplicate objects
newUser = lodash.cloneDeep userSchema newUser = lodash.cloneDeep userSchema
newUser.priv.apiToken = derby.uuid() newUser.apiToken = derby.uuid()
for task in content.defaultTasks for task in content.defaultTasks
guid = task.id = derby.uuid() guid = task.id = derby.uuid()
newUser.priv.tasks[guid] = task newUser.tasks[guid] = task
switch task.type switch task.type
when 'habit' then newUser.priv.idLists.habit.push guid when 'habit' then newUser.idLists.habit.push guid
when 'daily' then newUser.priv.idLists.daily.push guid when 'daily' then newUser.idLists.daily.push guid
when 'todo' then newUser.priv.idLists.todo.push guid when 'todo' then newUser.idLists.todo.push guid
when 'reward' then newUser.priv.idLists.reward.push guid when 'reward' then newUser.idLists.reward.push guid
return newUser return newUser
module.exports.updateUser = (batch) -> module.exports.updateUser = (batch) ->
user = batch.user user = batch.user
obj = batch.obj() obj = batch.obj()
batch.set('priv.apiToken', derby.uuid()) unless obj.priv.apiToken batch.set('apiToken', derby.uuid()) unless obj.apiToken
## Task List Cleanup ## Task List Cleanup
# FIXME temporary hack to fix lists (Need to figure out why these are happening) # FIXME temporary hack to fix lists (Need to figure out why these are happening)
tasks = obj.priv.tasks tasks = obj.tasks
_.each ['habit','daily','todo','reward'], (type) -> _.each ['habit','daily','todo','reward'], (type) ->
# 1. remove duplicates # 1. remove duplicates
# 2. restore missing zombie tasks back into list # 2. restore missing zombie tasks back into list
taskIds = _.pluck( _.where(tasks, {type:type}), 'id') taskIds = _.pluck( _.where(tasks, {type:type}), 'id')
union = _.union obj.priv.idLists[type], taskIds union = _.union obj.idLists[type], taskIds
# 2. remove empty (grey) tasks # 2. remove empty (grey) tasks
preened = _.filter(union, (val) -> _.contains(taskIds, val)) preened = _.filter(union, (val) -> _.contains(taskIds, val))
# There were indeed issues found, set the new list # There were indeed issues found, set the new list
batch.set("priv.idLists.#{type}", preened) # if _.difference(preened, userObj[path]).length != 0 batch.set("idLists.#{type}", preened) # if _.difference(preened, userObj[path]).length != 0
module.exports.BatchUpdate = BatchUpdate = (model) -> module.exports.BatchUpdate = BatchUpdate = (model) ->
user = model.at("_user") user = model.at("_user")
@@ -100,9 +100,9 @@ module.exports.BatchUpdate = BatchUpdate = (model) ->
eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok
### ###
setStats: (stats) -> setStats: (stats) ->
stats ?= obj.pub.stats stats ?= obj.stats
that = @ that = @
_.each Object.keys(stats), (key) -> that.set "pub.stats.#{key}", stats[key] _.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]
# queue: (path, val) -> # queue: (path, val) ->
# # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790 # # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790

View File

@@ -20,8 +20,8 @@ setModel = (m) ->
{modifiers} may manually pass in stats as {weapon, exp}. This is used for testing {modifiers} may manually pass in stats as {weapon, exp}. This is used for testing
### ###
expModifier = (value, modifiers = {}) -> expModifier = (value, modifiers = {}) ->
weapon = modifiers.weapon || user.get('pub.items.weapon') weapon = modifiers.weapon || user.get('items.weapon')
lvl = modifiers.lvl || user.get('pub.stats.lvl') lvl = modifiers.lvl || user.get('stats.lvl')
dmg = weapon * MODIFIER # each new weapon increases exp gain dmg = weapon * MODIFIER # each new weapon increases exp gain
dmg += (lvl-1) * MODIFIER # same for lvls dmg += (lvl-1) * MODIFIER # same for lvls
modified = value + (value * dmg) modified = value + (value * dmg)
@@ -33,8 +33,8 @@ expModifier = (value, modifiers = {}) ->
{modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing {modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing
### ###
hpModifier = (value, modifiers = {}) -> hpModifier = (value, modifiers = {}) ->
armor = modifiers.armor || user.get('pub.items.armor') armor = modifiers.armor || user.get('items.armor')
lvl = modifiers.lvl || user.get('pub.stats.lvl') lvl = modifiers.lvl || user.get('stats.lvl')
ac = armor * MODIFIER # each new armor decreases HP loss ac = armor * MODIFIER # each new armor decreases HP loss
ac += (lvl-1) * MODIFIER # same for lvls ac += (lvl-1) * MODIFIER # same for lvls
modified = value - (value * ac) modified = value - (value * ac)
@@ -62,37 +62,37 @@ updateStats = (newStats, batch) ->
obj = batch.obj() obj = batch.obj()
# if user is dead, dont do anything # if user is dead, dont do anything
return if obj.pub.stats.lvl == 0 return if obj.stats.lvl == 0
if newStats.hp? if newStats.hp?
# Game Over # Game Over
if newStats.hp <= 0 if newStats.hp <= 0
obj.pub.stats.lvl = 0 # signifies dead obj.stats.lvl = 0 # signifies dead
obj.pub.stats.hp = 0 obj.stats.hp = 0
return return
else else
obj.pub.stats.hp = newStats.hp obj.stats.hp = newStats.hp
if newStats.exp? if newStats.exp?
# level up & carry-over exp # level up & carry-over exp
tnl = model.get '_tnl' tnl = model.get '_tnl'
if newStats.exp >= tnl if newStats.exp >= tnl
newStats.exp -= tnl newStats.exp -= tnl
obj.pub.stats.lvl++ obj.stats.lvl++
obj.pub.stats.hp = 50 obj.stats.hp = 50
if !obj.priv.flags.itemsEnabled and obj.pub.stats.lvl >= 2 if !obj.flags.itemsEnabled and obj.stats.lvl >= 2
# Set to object, then also send to browser right away to get model.on() subscription notification # Set to object, then also send to browser right away to get model.on() subscription notification
batch.set 'priv.flags.itemsEnabled', true batch.set 'flags.itemsEnabled', true
obj.priv.flags.itemsEnabled = true obj.flags.itemsEnabled = true
if !obj.priv.flags.partyEnabled and obj.pub.stats.lvl >= 3 if !obj.flags.partyEnabled and obj.stats.lvl >= 3
batch.set 'priv.flags.partyEnabled', true batch.set 'flags.partyEnabled', true
obj.priv.flags.partyEnabled = true obj.flags.partyEnabled = true
obj.pub.stats.exp = newStats.exp obj.stats.exp = newStats.exp
if newStats.gp? if newStats.gp?
#FIXME what was I doing here? I can't remember, gp isn't defined #FIXME what was I doing here? I can't remember, gp isn't defined
gp = 0.0 if (!gp? or gp<0) gp = 0.0 if (!gp? or gp<0)
obj.pub.stats.gp = newStats.gp obj.stats.gp = newStats.gp
# {taskId} task you want to score # {taskId} task you want to score
# {direction} 'up' or 'down' # {direction} 'up' or 'down'
@@ -106,10 +106,10 @@ score = (taskId, direction, times, batch, cron) ->
batch.startTransaction() batch.startTransaction()
obj = batch.obj() obj = batch.obj()
{gp, hp, exp, lvl} = obj.pub.stats {gp, hp, exp, lvl} = obj.stats
taskPath = "priv.tasks.#{taskId}" taskPath = "tasks.#{taskId}"
taskObj = obj.priv.tasks[taskId] taskObj = obj.tasks[taskId]
{type, value} = taskObj {type, value} = taskObj
delta = 0 delta = 0
@@ -171,12 +171,12 @@ score = (taskId, direction, times, batch, cron) ->
taskObj.value = value taskObj.value = value
batch.set "#{taskPath}.value", taskObj.value batch.set "#{taskPath}.value", taskObj.value
origStats = _.clone obj.pub.stats origStats = _.clone obj.stats
updateStats {hp: hp, exp: exp, gp: gp}, batch updateStats {hp: hp, exp: exp, gp: gp}, batch
if commit if commit
# newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*) # newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*)
newStats = _.clone batch.obj().pub.stats newStats = _.clone batch.obj().stats
_.each Object.keys(origStats), (key) -> obj.pub.stats[key] = origStats[key] _.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key]
batch.setStats(newStats) batch.setStats(newStats)
# batch.setStats() # batch.setStats()
batch.commit() batch.commit()
@@ -192,12 +192,12 @@ cron = (resetDom_cb) ->
if daysPassed > 0 if daysPassed > 0
batch = new schema.BatchUpdate(model) batch = new schema.BatchUpdate(model)
batch.startTransaction() batch.startTransaction()
batch.set 'priv.lastCron', today batch.set 'lastCron', today
obj = batch.obj() obj = batch.obj()
hpBefore = obj.pub.stats.hp #we'll use this later so we can animate hp loss hpBefore = obj.stats.hp #we'll use this later so we can animate hp loss
# Tally each task # Tally each task
todoTally = 0 todoTally = 0
_.each obj.priv.tasks, (taskObj) -> _.each obj.tasks, (taskObj) ->
unless taskObj.id? unless taskObj.id?
console.error "a task had a null id during cron, this should not be happening" console.error "a task had a null id during cron, this should not be happening"
return return
@@ -222,31 +222,31 @@ cron = (resetDom_cb) ->
if type == 'daily' if type == 'daily'
taskObj.history ?= [] taskObj.history ?= []
taskObj.history.push { date: +new Date, value: value } taskObj.history.push { date: +new Date, value: value }
batch.set "priv.tasks.#{taskObj.id}.history", taskObj.history batch.set "tasks.#{taskObj.id}.history", taskObj.history
batch.set "priv.tasks.#{taskObj.id}.completed", false batch.set "tasks.#{taskObj.id}.completed", false
else else
value = obj.priv.tasks[taskObj.id].value #get updated value value = obj.tasks[taskObj.id].value #get updated value
absVal = if (completed) then Math.abs(value) else value absVal = if (completed) then Math.abs(value) else value
todoTally += absVal todoTally += absVal
# Finished tallying # Finished tallying
obj.priv.history ?= {}; obj.priv.history.todos ?= []; obj.priv.history.exp ?= [] obj.history ?= {}; obj.history.todos ?= []; obj.history.exp ?= []
obj.priv.history.todos.push { date: today, value: todoTally } obj.history.todos.push { date: today, value: todoTally }
# tally experience # tally experience
expTally = obj.pub.stats.exp expTally = obj.stats.exp
lvl = 0 #iterator lvl = 0 #iterator
while lvl < (obj.pub.stats.lvl-1) while lvl < (obj.stats.lvl-1)
lvl++ lvl++
expTally += (lvl*100)/5 expTally += (lvl*100)/5
obj.priv.history.exp.push { date: today, value: expTally } obj.history.exp.push { date: today, value: expTally }
# Set the new user specs, and animate HP loss # Set the new user specs, and animate HP loss
[hpAfter, obj.pub.stats.hp] = [obj.pub.stats.hp, hpBefore] [hpAfter, obj.stats.hp] = [obj.stats.hp, hpBefore]
batch.setStats() batch.setStats()
batch.set('history', obj.priv.history) batch.set('history', obj.history)
batch.commit() batch.commit()
resetDom_cb(model) resetDom_cb(model)
setTimeout (-> user.set 'pub.stats.hp', hpAfter), 1000 # animate hp loss setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss
module.exports = { module.exports = {

View File

@@ -31,8 +31,8 @@ module.exports.deleteStaleAccounts = ->
collection.findEach un_registered, (err, user) -> collection.findEach un_registered, (err, user) ->
throw err if err throw err if err
return unless user? #why does this happen sometimes? return unless user? #why does this happen sometimes?
if !!user.priv.lastCron # for now ignore missing crons, still looking into why this is happening if !!user.lastCron # for now ignore missing crons, still looking into why this is happening
lastCron = new Date(user.priv.lastCron) lastCron = new Date(user.lastCron)
diff = Math.abs(moment(today).sod().diff(moment(lastCron).sod(), "days")) diff = Math.abs(moment(today).sod().diff(moment(lastCron).sod(), "days"))
if diff > 10 if diff > 10
removeAccount(collection, user._id) removeAccount(collection, user._id)

View File

@@ -35,8 +35,8 @@ module.exports.app = (appExports, model) ->
appExports.buyReroll = (e, el, next) -> appExports.buyReroll = (e, el, next) ->
batch = new schema.BatchUpdate(model) batch = new schema.BatchUpdate(model)
obj = model.get('_user') obj = model.get('_user')
batch.set 'priv.balance', obj.priv.balance-1 batch.set 'balance', obj.balance-1
_.each obj.priv.tasks, (task) -> batch.set("priv.tasks.#{task.id}.value", 0) unless task.type == 'reward' _.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type == 'reward'
batch.commit() batch.commit()
module.exports.routes = (expressApp) -> module.exports.routes = (expressApp) ->
@@ -53,8 +53,8 @@ module.exports.routes = (expressApp) ->
userId = model.session.userId userId = model.session.userId
model.fetch "users.#{userId}", (err, user) -> model.fetch "users.#{userId}", (err, user) ->
model.ref '_user', "users.#{userId}" model.ref '_user', "users.#{userId}"
model.set('_user.priv.balance', model.get('_user.priv.balance')+5) model.set('_user.balance', model.get('_user.balance')+5)
model.set('_user.priv.flags.ads','hide') model.set('_user.flags.ads','hide')
return res.send(200) return res.send(200)
api_key = process.env.STRIPE_API_KEY # secret stripe API key api_key = process.env.STRIPE_API_KEY # secret stripe API key

View File

@@ -49,8 +49,8 @@ module.exports = (expressApp, root, derby) ->
# Create task if doesn't exist # Create task if doesn't exist
# TODO add service & icon to task # TODO add service & icon to task
unless model.get("_user.priv.tasks.#{taskId}") unless model.get("_user.tasks.#{taskId}")
model.refList "_habitList", "_user.priv.tasks", "_user.priv.idLists.habit" model.refList "_habitList", "_user.tasks", "_user.idLists.habit"
model.at('_habitList').push { model.at('_habitList').push {
id: taskId id: taskId
type: 'habit' type: 'habit'

View File

@@ -14,28 +14,28 @@
<pre class=prettyprint>{_user.id}</pre> <pre class=prettyprint>{_user.id}</pre>
<h6>API Token</h6> <h6>API Token</h6>
<pre class=prettyprint>{_user.priv.apiToken}</pre> <pre class=prettyprint>{_user.apiToken}</pre>
<hr/> <hr/>
<h4>Gender</h4> <h4>Gender</h4>
<label class="radio"> <label class="radio">
<input type="radio" name="genderRadios" value="m" x-bind="click:setMale" checked="{equal(_user.pub.preferences.gender,'m')}"> <input type="radio" name="genderRadios" value="m" x-bind="click:setMale" checked="{equal(_user.preferences.gender,'m')}">
Male Male
</label> </label>
<label class="radio"> <label class="radio">
<input type="radio" name="genderRadios" value="f" x-bind="click:setFemale" checked="{equal(_user.pub.preferences.gender,'f')}"> <input type="radio" name="genderRadios" value="f" x-bind="click:setFemale" checked="{equal(_user.preferences.gender,'f')}">
Female Female
</label> </label>
{#if equal(_user.pub.preferences.gender,'f')} {#if equal(_user.preferences.gender,'f')}
<h4>Armor Set</h4> <h4>Armor Set</h4>
<label class="radio"> <label class="radio">
<input type="radio" name="armorSet" value="v1" x-bind="click:setArmorsetV1" checked="{equal(_user.pub.preferences.armorSet,'v1')}"> <input type="radio" name="armorSet" value="v1" x-bind="click:setArmorsetV1" checked="{equal(_user.preferences.armorSet,'v1')}">
v1 v1
</label> </label>
<label class="radio"> <label class="radio">
<input type="radio" name="armorSet" value="v2" x-bind="click:setArmorsetV2" checked="{equal(_user.pub.preferences.armorSet,'v2')}"> <input type="radio" name="armorSet" value="v2" x-bind="click:setArmorsetV2" checked="{equal(_user.preferences.armorSet,'v2')}">
v2 v2
</label> </label>
{/} {/}
@@ -79,7 +79,7 @@
</app:myModal> </app:myModal>
{/} {/}
<!-- Game Over Modal --> <!-- Game Over Modal -->
<div style="{#unless equal(_user.pub.stats.lvl,0)}display:none;{/}"> <div style="{#unless equal(_user.stats.lvl,0)}display:none;{/}">
<app:myModal noDismiss=true modalId='dead-modal'> <app:myModal noDismiss=true modalId='dead-modal'>
<table> <table>
<tr> <tr>
@@ -100,7 +100,7 @@
<app:userTokens/> <app:userTokens/>
<p>Highly discouraged because red tasks provide good incentive to improve (<a target="_blank" href="https://github.com/lefnire/habitrpg#all-my-tasks-are-red-im-dying-too-fast">read more</a>). However, this becomes necessary after long bouts of bad habits.</p> <p>Highly discouraged because red tasks provide good incentive to improve (<a target="_blank" href="https://github.com/lefnire/habitrpg#all-my-tasks-are-red-im-dying-too-fast">read more</a>). However, this becomes necessary after long bouts of bad habits.</p>
<@footer> <@footer>
{#if lessThan(_user.priv.balance,1)} {#if lessThan(_user.balance,1)}
<a data-dismiss="modal" x-bind="click:showStripe" class="btn btn-danger btn-large">Buy More Tokens</a><span class='token-cost'>Not enough tokens</span> <a data-dismiss="modal" x-bind="click:showStripe" class="btn btn-danger btn-large">Buy More Tokens</a><span class='token-cost'>Not enough tokens</span>
{else} {else}
<a data-dismiss="modal" x-bind=click:buyReroll class="btn btn-danger btn-large">Re-Roll</a><span class='token-cost'>4 Tokens</span> <a data-dismiss="modal" x-bind=click:buyReroll class="btn btn-danger btn-large">Re-Roll</a><span class='token-cost'>4 Tokens</span>
@@ -125,7 +125,7 @@
</ul> </ul>
{/} {/}
{#if equal(_user.priv.flags.kickstarter,'show')} {#if equal(_user.flags.kickstarter,'show')}
<div class='alert alert-success'> <div class='alert alert-success'>
<a x-bind="click:closeKickstarterNofitication" class='pull-right'>Dismiss</a> <a x-bind="click:closeKickstarterNofitication" class='pull-right'>Dismiss</a>
Help Habit by backing the <strong><a href="http://kck.st/XoA3Yg">Kickstarter</a></strong>! Funds iPhone & Android apps, <a href="https://github.com/lefnire/habitrpg/issues?labels=critical&page=1&state=open">bug fixes</a>, and the <a href="https://github.com/lefnire/habitrpg/issues/58">Groups feature</a>. Help Habit by backing the <strong><a href="http://kck.st/XoA3Yg">Kickstarter</a></strong>! Funds iPhone & Android apps, <a href="https://github.com/lefnire/habitrpg/issues?labels=critical&page=1&state=open">bug fixes</a>, and the <a href="https://github.com/lefnire/habitrpg/issues/58">Groups feature</a>.
@@ -165,39 +165,39 @@
<!-- Avatar --> <!-- Avatar -->
<td class="avatar main-avatar"> <td class="avatar main-avatar">
<div class='avatar-sprites'> <div class='avatar-sprites'>
<img class='weapon weapon-{_user.pub.items.weapon}' src="/img/BrowserQuest/habitrpg_mods/weapon{_user.pub.items.weapon}.png" /> <img class='weapon weapon-{_user.items.weapon}' src="/img/BrowserQuest/habitrpg_mods/weapon{_user.items.weapon}.png" />
<img class='armor armor-{_user.pub.items.armor}' src="/img/BrowserQuest/habitrpg_mods/{currentArmor(_user)}" /> <img class='armor armor-{_user.items.armor}' src="/img/BrowserQuest/habitrpg_mods/{currentArmor(_user)}" />
</div> </div>
<div id="lvl"><span class="badge badge-info">Lvl {_user.pub.stats.lvl}</span></div> <div id="lvl"><span class="badge badge-info">Lvl {_user.stats.lvl}</span></div>
</td> </td>
<!-- Progress Bars --> <!-- Progress Bars -->
<td id="bars" style="width:{#if _party}70%{else}90%{/};"> <td id="bars" style="width:{#if _party}70%{else}90%{/};">
<div class="progress progress-danger" rel=tooltip data-placement=bottom title="Health"> <div class="progress progress-danger" rel=tooltip data-placement=bottom title="Health">
<div class="bar" style="width: {percent(_user.pub.stats.hp, 50)}%;"></div> <div class="bar" style="width: {percent(_user.stats.hp, 50)}%;"></div>
<span class="progress-text"><i class=icon-heart></i> {round(_user.pub.stats.hp)} / 50</span> <span class="progress-text"><i class=icon-heart></i> {round(_user.stats.hp)} / 50</span>
</div> </div>
<div class="progress progress-warning" rel=tooltip data-placement=bottom title="Experience"> <div class="progress progress-warning" rel=tooltip data-placement=bottom title="Experience">
<div class="bar" style="width: {percent(_user.pub.stats.exp,_tnl)}%;"></div> <div class="bar" style="width: {percent(_user.stats.exp,_tnl)}%;"></div>
<span class="progress-text"> <span class="progress-text">
{#if _user.history.exp} {#if _user.history.exp}
<a x-bind=click:toggleChart data-toggle-id="exp-chart" data-history-path="_user.history.exp" rel=tooltip title="Progress"><i class=icon-signal></i></a>&nbsp; <a x-bind=click:toggleChart data-toggle-id="exp-chart" data-history-path="_user.history.exp" rel=tooltip title="Progress"><i class=icon-signal></i></a>&nbsp;
{/} {/}
<i class=icon-star></i> {round(_user.pub.stats.exp)} / {_tnl} <i class=icon-star></i> {round(_user.stats.exp)} / {_tnl}
</span> </span>
</div> </div>
</td> </td>
<!-- Party --> <!-- Party -->
{#if _user.priv.flags.partyEnabled} {#if _user.flags.partyEnabled}
{#each _party as :member} {#each _party as :member}
<td class="avatar party-avatar" rel="tooltip" title="{username(:member.auth)}" data-placement="bottom" > <td class="avatar party-avatar" rel="tooltip" title="{username(:member.auth)}" data-placement="bottom" >
<div class='avatar-sprites'> <div class='avatar-sprites'>
<img class='weapon weapon-{:member.pub.items.weapon}' src="/img/BrowserQuest/habitrpg_mods/weapon{:member.pub.items.weapon}.png" /> <img class='weapon weapon-{:member.items.weapon}' src="/img/BrowserQuest/habitrpg_mods/weapon{:member.items.weapon}.png" />
<img class='armor armor-{:member.pub.items.armor}' src="/img/BrowserQuest/habitrpg_mods/{currentArmor(:member)}" /> <img class='armor armor-{:member.items.armor}' src="/img/BrowserQuest/habitrpg_mods/{currentArmor(:member)}" />
</div> </div>
<div id="lvl"><span class="badge badge-info">Lvl {:member.pub.stats.lvl}</span></div> <div id="lvl"><span class="badge badge-info">Lvl {:member.stats.lvl}</span></div>
</td> </td>
{/} {/}
<td><a class="btn" id="add-party-button" data-target="#add-party-modal" data-toggle="modal"><i class="icon-user"></i></a></td> <td><a class="btn" id="add-party-button" data-target="#add-party-modal" data-toggle="modal"><i class="icon-user"></i></a></td>
@@ -281,7 +281,7 @@
<!--Title --> <!--Title -->
<div class="row-fluid"> <div class="row-fluid">
<div class="span6"><h2>Rewards</h2></div> <div class="span6"><h2>Rewards</h2></div>
<div class="span6" id="money">{gold(_user.pub.stats.money)} <img src='/img/coin_single_gold.png'/> {silver(_user.pub.stats.money)} <img src='/img/coin_single_silver.png'/></div> <div class="span6" id="money">{gold(_user.stats.money)} <img src='/img/coin_single_gold.png'/> {silver(_user.stats.money)} <img src='/img/coin_single_silver.png'/></div>
</div> </div>
<!-- Content --> <!-- Content -->
@@ -289,7 +289,7 @@
{#each _rewardList as :task}<app:task />{/} {#each _rewardList as :task}<app:task />{/}
</ul> </ul>
{#if _user.priv.flags.itemsEnabled} {#if _user.flags.itemsEnabled}
<ul class='items'> <ul class='items'>
{#with _view.items.armor as :item}<app:item />{/} {#with _view.items.armor as :item}<app:item />{/}
{#with _view.items.weapon as :item}<app:item />{/} {#with _view.items.weapon as :item}<app:item />{/}
@@ -346,12 +346,12 @@
<userTokens:> <userTokens:>
<div class="pull-right"> <div class="pull-right">
<div class="input-append"> <div class="input-append">
<span class="uneditable-input" style="width:auto;">{tokens(_user.priv.balance)}</span> <span class="uneditable-input" style="width:auto;">{tokens(_user.balance)}</span>
<span class="add-on">Tokens</span> <span class="add-on">Tokens</span>
</div> </div>
<a class="label" data-toggle="modal" href='#reset-modal'><i class="icon-ban-circle"></i>Reset</a> <a class="label" data-toggle="modal" href='#reset-modal'><i class="icon-ban-circle"></i>Reset</a>
</div> </div>
<!--<span class="well pull-right">Tokens: {tokens(_user.priv.balance)}</span>--> <!--<span class="well pull-right">Tokens: {tokens(_user.balance)}</span>-->
<newTask: nonvoid> <newTask: nonvoid>
<form class="form-inline new-task-form" id=new-{{@type}} data-task-type={{@type}} x-bind=submit:addTask> <form class="form-inline new-task-form" id=new-{{@type}} data-task-type={{@type}} x-bind=submit:addTask>