Merge branch 'develop' into challenges-tmp
Conflicts: src/app/helpers.coffee src/app/index.coffee
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
XP = 15
|
||||
HP = 2
|
||||
|
||||
priorityValue = module.exports.priorityValue = (priority='!') ->
|
||||
switch priority
|
||||
when '!' then 1
|
||||
when '!!' then 1.5
|
||||
when '!!!' then 2
|
||||
else 1
|
||||
|
||||
module.exports.tnl = (level) ->
|
||||
if level >= 100
|
||||
value = 0
|
||||
else
|
||||
value = Math.round(((Math.pow(level,2)*0.25)+(10 * level) + 139.75)/10)*10 # round to nearest 10
|
||||
return value
|
||||
|
||||
###
|
||||
Calculates Exp modificaiton based on level and weapon strength
|
||||
{value} task.value for exp gain
|
||||
{weaponStrength) weapon strength
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.expModifier = (value, weaponStr, level, priority='!') ->
|
||||
str = (level-1) / 2 # ultimately get this from user
|
||||
totalStr = (str + weaponStr) / 100
|
||||
strMod = 1 + totalStr
|
||||
exp = value * XP * strMod * priorityValue(priority)
|
||||
return Math.round(exp)
|
||||
|
||||
###
|
||||
Calculates HP modification based on level and armor defence
|
||||
{value} task.value for hp loss
|
||||
{armorDefense} defense from armor
|
||||
{helmDefense} defense from helm
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.hpModifier = (value, armorDef, helmDef, shieldDef, level, priority='!') ->
|
||||
def = (level-1) / 2 # ultimately get this from user?
|
||||
totalDef = (def + armorDef + helmDef + shieldDef) / 100 #ultimate get this from user
|
||||
defMod = 1 - totalDef
|
||||
hp = value * HP * defMod * priorityValue(priority)
|
||||
return Math.round(hp * 10)/10 # round to 1dp
|
||||
|
||||
###
|
||||
Future use
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.gpModifier = (value, modifier, priority='!', streak, model) ->
|
||||
val = value * modifier * priorityValue(priority)
|
||||
if streak and model
|
||||
streakBonus = streak / 100 + 1 # eg, 1-day streak is 1.1, 2-day is 1.2, etc
|
||||
afterStreak = val * streakBonus
|
||||
model.set('_streakBonus', afterStreak - val) if (val > 0) # can we do this without model? just global emit?
|
||||
return afterStreak
|
||||
else
|
||||
return val
|
||||
|
||||
###
|
||||
Calculates the next task.value based on direction
|
||||
Uses a capped inverse log y=.95^x, y>= -5
|
||||
{currentValue} the current value of the task
|
||||
{direction} up or down
|
||||
###
|
||||
module.exports.taskDeltaFormula = (currentValue, direction) ->
|
||||
if currentValue < -47.27 then currentValue = -47.27
|
||||
else if currentValue > 21.27 then currentValue = 21.27
|
||||
delta = Math.pow(0.9747,currentValue)
|
||||
return delta if direction is 'up'
|
||||
return -delta
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_ = require 'underscore'
|
||||
_ = require 'lodash'
|
||||
moment = require 'moment'
|
||||
|
||||
###
|
||||
@@ -35,7 +35,7 @@ loadJavaScripts = (model) ->
|
||||
###
|
||||
setupSortable = (model) ->
|
||||
unless (model.get('_mobileDevice') is true) #don't do sortable on mobile
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
['habit', 'daily', 'todo', 'reward'].forEach (type) ->
|
||||
$("ul.#{type}s").sortable
|
||||
dropOnEmpty: false
|
||||
cursor: "move"
|
||||
@@ -109,8 +109,7 @@ setupTour = (model) ->
|
||||
|
||||
$('.main-herobox').popover('destroy') #remove previous popovers
|
||||
tour = new Tour()
|
||||
_.each tourSteps, (step) ->
|
||||
tour.addStep _.defaults step, {html:true}
|
||||
tourSteps.forEach (step) -> tour.addStep _.defaults step, {html:true}
|
||||
tour._current = 0 if isNaN(tour._current) #bootstrap-tour bug
|
||||
tour.start()
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
_ = require 'underscore'
|
||||
lodash = require 'lodash'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
browser = require './browser'
|
||||
helpers = require './helpers'
|
||||
|
||||
user = model.at '_user'
|
||||
|
||||
appExports.challengeCreate = ->
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
browser = require './browser'
|
||||
items = require './items'
|
||||
algos = require './algos'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
|
||||
moment = require 'moment'
|
||||
_ = require 'underscore'
|
||||
lodash = require 'lodash'
|
||||
_ = require 'lodash'
|
||||
derby = require 'derby'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
@@ -22,7 +21,6 @@ module.exports.app = (appExports, model) ->
|
||||
owned = user.get('items')
|
||||
# unless they're already at 0-everything
|
||||
if parseInt(owned.armor)>0 or parseInt(owned.head)>0 or parseInt(owned.shield)>0 or parseInt(owned.weapon)>0
|
||||
console.log 'test'
|
||||
# find a random item to lose
|
||||
until loseThisItem
|
||||
#candidate = {0:'items.armor', 1:'items.head', 2:'items.shield', 3:'items.weapon', 4:'stats.gp'}[Math.random()*5|0]
|
||||
@@ -37,8 +35,8 @@ module.exports.app = (appExports, model) ->
|
||||
batch.startTransaction()
|
||||
taskTypes = ['habit', 'daily', 'todo', 'reward']
|
||||
batch.set 'tasks', {}
|
||||
_.each taskTypes, (type) -> batch.set "#{type}Ids", []
|
||||
batch.set 'balance', 1 if user.get('balance') < 1 #only if they haven't manually bought gems
|
||||
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
|
||||
@@ -84,68 +82,6 @@ module.exports.app = (appExports, model) ->
|
||||
model.del "users.#{user.get('id')}", ->
|
||||
window.location.href = "/logout"
|
||||
|
||||
userSchema =
|
||||
# _id
|
||||
stats: { gp: 0, exp: 0, lvl: 1, hp: 50 }
|
||||
party: { current: null, invitation: null }
|
||||
items: { weapon: 0, armor: 0, head: 0, shield: 0 }
|
||||
preferences: { gender: 'm', skin: 'white', hair: 'blond', armorSet: 'v1', dayStart:0, showHelm: true }
|
||||
habitIds: []
|
||||
dailyIds: []
|
||||
todoIds: []
|
||||
rewardIds: []
|
||||
apiToken: null # set in newUserObject below
|
||||
lastCron: 'new' #this will be replaced with `+new Date` on first run
|
||||
balance: 0
|
||||
tasks: {}
|
||||
flags:
|
||||
partyEnabled: false
|
||||
itemsEnabled: false
|
||||
tags: []
|
||||
# ads: 'show' # added on registration
|
||||
|
||||
module.exports.newUserObject = ->
|
||||
# deep clone, else further new users get duplicate objects
|
||||
newUser = lodash.cloneDeep userSchema
|
||||
newUser.apiToken = derby.uuid()
|
||||
|
||||
repeat = {m:true,t:true,w:true,th:true,f:true,s:true,su:true}
|
||||
defaultTasks = [
|
||||
{type: 'habit', text: '1h Productive Work', notes: '-- Habits: Constantly Track --\nFor some habits, it only makes sense to *gain* points (like this one).', value: 0, up: true, down: false }
|
||||
{type: 'habit', text: 'Eat Junk Food', notes: 'For others, it only makes sense to *lose* points', value: 0, up: false, down: true}
|
||||
{type: 'habit', text: 'Take The Stairs', notes: 'For the rest, both + and - make sense (stairs = gain, elevator = lose)', value: 0, up: true, down: true}
|
||||
|
||||
{type: 'daily', text: '1h Personal Project', notes: '-- Dailies: Complete Once a Day --\nAt the end of each day, non-completed Dailies dock you points.', value: 0, completed: false, repeat: repeat }
|
||||
{type: 'daily', text: 'Exercise', notes: "If you are doing well, they turn green and are less valuable (experience, gold) and less damaging (HP). This means you can ease up on them for a bit.", value: 3, completed: false, repeat: repeat }
|
||||
{type: 'daily', text: '45m Reading', notes: 'But if you are doing poorly, they turn red. The worse you do, the more valuable (exp, gold) and more damaging (HP) these goals become. This encourages you to focus on your shortcomings, the reds.', value: -10, completed: false, repeat: repeat }
|
||||
|
||||
{type: 'todo', text: 'Call Mom', notes: "-- Todos: Complete Eventually --\nNon-completed Todos won't hurt you, but they will become more valuable over time. This will encourage you to wrap up stale Todos.", value: -3, completed: false }
|
||||
|
||||
{type: 'reward', text: '1 Episode of Game of Thrones', notes: '-- Rewards: Treat Yourself! --\nAs you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits.', value: 20 }
|
||||
{type: 'reward', text: 'Cake', notes: 'But only buy if you have enough gold - you lose HP otherwise.', value: 10 }
|
||||
]
|
||||
|
||||
defaultTags = [
|
||||
{name: 'morning'}
|
||||
{name: 'afternoon'}
|
||||
{name: 'evening'}
|
||||
]
|
||||
|
||||
for task in defaultTasks
|
||||
guid = task.id = derby.uuid()
|
||||
newUser.tasks[guid] = task
|
||||
switch task.type
|
||||
when 'habit' then newUser.habitIds.push guid
|
||||
when 'daily' then newUser.dailyIds.push guid
|
||||
when 'todo' then newUser.todoIds.push guid
|
||||
when 'reward' then newUser.rewardIds.push guid
|
||||
|
||||
for tag in defaultTags
|
||||
tag.id = derby.uuid()
|
||||
newUser.tags.push tag
|
||||
|
||||
return newUser
|
||||
|
||||
module.exports.BatchUpdate = BatchUpdate = (model) ->
|
||||
user = model.at("_user")
|
||||
transactionInProgress = false
|
||||
@@ -181,7 +117,7 @@ module.exports.BatchUpdate = BatchUpdate = (model) ->
|
||||
setStats: (stats) ->
|
||||
stats ?= obj.stats
|
||||
that = @
|
||||
_.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]
|
||||
_.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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
moment = require 'moment'
|
||||
algos = require './algos'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_ = require 'underscore'
|
||||
_ = require 'lodash'
|
||||
browser = require './browser'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
@@ -37,5 +37,5 @@ module.exports.app = (appExports, model) ->
|
||||
tag.remove()
|
||||
|
||||
# remove tag from all tasks
|
||||
_.each user.get("tasks"), (task) -> user.del "tasks.#{task.id}.tags.#{tagId}"
|
||||
_.each user.get("tasks"), (task) -> user.del "tasks.#{task.id}.tags.#{tagId}"; true
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
moment = require 'moment'
|
||||
_ = require 'underscore'
|
||||
relative = require 'relative-date'
|
||||
algos = require './algos'
|
||||
items = require('./items').items
|
||||
|
||||
sod = (timestamp, dayStart=0) ->
|
||||
#sanity-check reset-time (is it 24h time?)
|
||||
dayStart = 0 unless (dayStart = parseInt(dayStart)) and (0 <= dayStart <= 24)
|
||||
moment(timestamp).startOf('day').add('h', dayStart)
|
||||
|
||||
# Absolute diff between two dates
|
||||
daysBetween = (yesterday, now, dayStart) -> Math.abs sod(yesterday, dayStart).diff(now, 'days')
|
||||
|
||||
dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s'}
|
||||
|
||||
shouldDo = (day, repeat, dayStart=0) ->
|
||||
return false unless repeat
|
||||
now = +new Date
|
||||
selected = repeat[dayMapping[sod(day, dayStart).day()]]
|
||||
return selected unless moment(day).isSame(now,'d')
|
||||
if dayStart <= moment(now).hour() # we're past the dayStart mark, is it due today?
|
||||
return selected
|
||||
else # we're not past dayStart mark, check if it was due "yesterday"
|
||||
yesterday = moment(now).subtract(1,'d').day()
|
||||
return repeat[dayMapping[yesterday]]
|
||||
|
||||
# http://stackoverflow.com/questions/2532218/pick-random-property-from-a-javascript-object
|
||||
# obj: object
|
||||
# returns random property (the value)
|
||||
randomVal = (obj) ->
|
||||
result = undefined
|
||||
count = 0
|
||||
for key, val of obj
|
||||
result = val if Math.random() < (1 / ++count)
|
||||
result
|
||||
|
||||
removeWhitespace = (str) ->
|
||||
return '' unless str
|
||||
str.replace /\s/g, ''
|
||||
|
||||
username = (auth, override) ->
|
||||
#some people define custom profile name in Avatar -> Profile
|
||||
return override if override
|
||||
|
||||
if auth?.facebook?.displayName?
|
||||
auth.facebook.displayName
|
||||
else if auth?.facebook?
|
||||
fb = auth.facebook
|
||||
if fb._raw then "#{fb.name.givenName} #{fb.name.familyName}" else fb.name
|
||||
else if auth?.local?
|
||||
auth.local.username
|
||||
else
|
||||
'Anonymous'
|
||||
|
||||
viewHelpers = (view) ->
|
||||
view.fn "percent", (x, y) ->
|
||||
x=1 if x==0
|
||||
Math.round(x/y*100)
|
||||
|
||||
view.fn "round", Math.round
|
||||
view.fn "floor", Math.floor
|
||||
view.fn "ceil", Math.ceil
|
||||
view.fn "lt", (a, b) -> a < b
|
||||
view.fn 'gt', (a, b) -> a > b
|
||||
view.fn "mod", (a, b) -> parseInt(a) % parseInt(b) == 0
|
||||
view.fn 'removeWhitespace', removeWhitespace
|
||||
view.fn "notEqual", (a, b) -> (a != b)
|
||||
view.fn "and", -> _.reduce arguments, (cumm, curr) -> cumm && curr
|
||||
view.fn "or", -> _.reduce arguments, (cumm, curr) -> cumm || curr
|
||||
view.fn "truarr", (num) -> num-1
|
||||
view.fn 'count', (arr) -> arr?.length or 0
|
||||
|
||||
view.fn "gems", (gp) -> return gp/0.25
|
||||
|
||||
view.fn "encodeiCalLink", (uid, apiToken) ->
|
||||
loc = window?.location.host or process.env.BASE_URL
|
||||
encodeURIComponent "http://#{loc}/v1/users/#{uid}/calendar.ics?apiToken=#{apiToken}"
|
||||
|
||||
|
||||
###
|
||||
User
|
||||
###
|
||||
view.fn "username", (auth, override) -> username(auth, override)
|
||||
view.fn "tnl", algos.tnl
|
||||
|
||||
###
|
||||
Items
|
||||
###
|
||||
view.fn 'equipped', (type, item=0, preferences={gender:'m', armorSet:'v1'}, backerTier=0) ->
|
||||
{gender, armorSet} = preferences
|
||||
item = parseInt(item)
|
||||
backerTier = parseInt(backerTier)
|
||||
|
||||
switch type
|
||||
when'armor'
|
||||
if item > 5
|
||||
return 'armor_6' if backerTier >= 45
|
||||
item = 5 # set them back if they're trying to cheat
|
||||
if gender is 'f'
|
||||
return if (item is 0) then "f_armor_#{item}_#{armorSet}" else "f_armor_#{item}"
|
||||
else
|
||||
return "m_armor_#{item}"
|
||||
|
||||
when 'head'
|
||||
if item > 5
|
||||
return 'head_6' if backerTier >= 45
|
||||
item = 5
|
||||
if gender is 'f'
|
||||
return if (item > 1) then "f_head_#{item}_#{armorSet}" else "f_head_#{item}"
|
||||
else
|
||||
return "m_head_#{item}"
|
||||
|
||||
when 'shield'
|
||||
if item > 5
|
||||
return 'shield_6' if backerTier >= 45
|
||||
item = 5
|
||||
return "#{preferences.gender}_shield_#{item}"
|
||||
|
||||
when 'weapon'
|
||||
if item > 6
|
||||
return 'weapon_7' if backerTier >= 70
|
||||
item = 6
|
||||
return "#{preferences.gender}_weapon_#{item}"
|
||||
|
||||
view.fn "gold", (num) ->
|
||||
if num
|
||||
return (num).toFixed(1).split('.')[0]
|
||||
else
|
||||
return "0"
|
||||
|
||||
view.fn "silver", (num) ->
|
||||
if num
|
||||
(num).toFixed(2).split('.')[1]
|
||||
else
|
||||
return "00"
|
||||
|
||||
###
|
||||
Tasks
|
||||
###
|
||||
view.fn 'taskClasses', (task, filters, dayStart, lastCron, showCompleted=false, main) ->
|
||||
return unless task
|
||||
{type, completed, value, repeat} = task
|
||||
|
||||
# completed / remaining toggle
|
||||
return 'hidden' if (type is 'todo') and (completed != showCompleted)
|
||||
|
||||
# Filters
|
||||
if main # only show when on your own list
|
||||
for filter, enabled of filters
|
||||
if enabled and not task.tags?[filter]
|
||||
# All the other classes don't matter
|
||||
return 'hidden'
|
||||
|
||||
classes = type
|
||||
|
||||
# show as completed if completed (naturally) or not required for today
|
||||
if type in ['todo', 'daily']
|
||||
if completed or (type is 'daily' and !shouldDo(+new Date, task.repeat, dayStart))
|
||||
classes += " completed"
|
||||
else
|
||||
classes += " uncompleted"
|
||||
else if type is 'habit'
|
||||
classes += ' habit-wide' if task.down and task.up
|
||||
|
||||
if value < -20
|
||||
classes += ' color-worst'
|
||||
else if value < -10
|
||||
classes += ' color-worse'
|
||||
else if value < -1
|
||||
classes += ' color-bad'
|
||||
else if value < 1
|
||||
classes += ' color-neutral'
|
||||
else if value < 5
|
||||
classes += ' color-good'
|
||||
else if value < 10
|
||||
classes += ' color-better'
|
||||
else
|
||||
classes += ' color-best'
|
||||
return classes
|
||||
|
||||
view.fn 'ownsPet', (pet, userPets) -> _.isArray(userPets) and userPets.indexOf(pet) != -1
|
||||
|
||||
view.fn 'friendlyTimestamp', (timestamp) -> moment(timestamp).format('MM/DD h:mm:ss a')
|
||||
|
||||
view.fn 'newChatMessages', (messages, lastMessageSeen) ->
|
||||
return false unless messages?.length > 0
|
||||
messages?[0] and (messages[0].id != lastMessageSeen)
|
||||
|
||||
view.fn 'indexOf', (str1, str2) ->
|
||||
return false unless str1 && str2
|
||||
str1.indexOf(str2) != -1
|
||||
|
||||
view.fn 'relativeDate', relative
|
||||
|
||||
view.fn 'noTags', (tags) ->
|
||||
_.isEmpty(tags) or _.isEmpty(_.filter( tags, (t) -> t ) )
|
||||
|
||||
view.fn 'appliedTags', (userTags, taskTags) ->
|
||||
arr = []
|
||||
_.each userTags, (t) ->
|
||||
return unless t?
|
||||
arr.push(t.name) if taskTags?[t.id]
|
||||
arr.join(', ')
|
||||
|
||||
view.fn 'userStr', (level) ->
|
||||
str = (level-1) / 2
|
||||
view.fn 'totalStr', (level, weapon=0) ->
|
||||
str = (level-1) / 2
|
||||
totalStr = (str + items.weapon[weapon].strength)
|
||||
view.fn 'userDef', (level) ->
|
||||
def = (level-1) / 2
|
||||
view.fn 'totalDef', (level, armor=0, helm=0, shield=0) ->
|
||||
def = (level-1) / 2
|
||||
totalDef = (def + items.armor[armor].defense + items.head[helm].defense + items.shield[shield].defense)
|
||||
view.fn 'itemText', (type, item=0) -> items[type][parseInt(item)].text
|
||||
view.fn 'itemStat', (type, item=0) -> if type is 'weapon' then items[type][parseInt(item)].strength else items[type][parseInt(item)].defense
|
||||
|
||||
# view.fn 'activeFilters', (filters) ->
|
||||
# debugger
|
||||
# (_.find filters, (f) -> f)?
|
||||
|
||||
|
||||
|
||||
module.exports = { viewHelpers, removeWhitespace, randomVal, daysBetween, shouldDo, username }
|
||||
+29
-12
@@ -17,10 +17,12 @@ i18n.localize app,
|
||||
urlScheme: false
|
||||
checkHeader: true
|
||||
|
||||
helpers = require './helpers'
|
||||
helpers.viewHelpers view
|
||||
misc = require('./misc')
|
||||
misc.viewHelpers view
|
||||
|
||||
_ = require('underscore')
|
||||
_ = require('lodash')
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
|
||||
###
|
||||
Cleanup task-corruption (null tasks, rogue/invisible tasks, etc)
|
||||
@@ -36,11 +38,12 @@ cleanupCorruptTasks = (model) ->
|
||||
unless task?.id? and task?.type?
|
||||
user.del("tasks.#{key}")
|
||||
delete tasks[key]
|
||||
true
|
||||
|
||||
batch = null
|
||||
|
||||
## Task List Cleanup
|
||||
_.each ['habit','daily','todo','reward'], (type) ->
|
||||
['habit','daily','todo','reward'].forEach (type) ->
|
||||
|
||||
# 1. remove duplicates
|
||||
# 2. restore missing zombie tasks back into list
|
||||
@@ -58,6 +61,7 @@ cleanupCorruptTasks = (model) ->
|
||||
batch.startTransaction()
|
||||
batch.set("#{type}Ids", preened)
|
||||
console.error user.get('id') + "'s #{type}s were corrupt."
|
||||
true
|
||||
|
||||
batch.commit() if batch?
|
||||
|
||||
@@ -69,17 +73,16 @@ setupSubscriptions = (page, model, params, next, cb) ->
|
||||
selfQ = model.query('users').withId(uuid) #keep this for later
|
||||
groupsQ = model.query('groups').withMember(uuid)
|
||||
|
||||
groupsQ.fetch (err, groups, extra) ->
|
||||
groupsQ.fetch (err, groups) ->
|
||||
return next(err) if err
|
||||
finished = (descriptors, paths) ->
|
||||
model.subscribe.apply model, descriptors.concat ->
|
||||
[err, refs] = [arguments[0], arguments]
|
||||
return next(err) if err
|
||||
_.each paths, (path, idx) -> model.ref path, refs[idx+1]
|
||||
_.each paths, (path, idx) -> model.ref path, refs[idx+1]; true
|
||||
unless model.get('_user')
|
||||
console.error "User not found - this shouldn't be happening!"
|
||||
return page.redirect('/logout') #delete model.session.userId
|
||||
extra(arguments) if extra
|
||||
return cb()
|
||||
|
||||
groupsObj = groups.get()
|
||||
@@ -88,6 +91,7 @@ setupSubscriptions = (page, model, params, next, cb) ->
|
||||
return finished([selfQ, "groups.habitrpg"], ['_user', '_habitRPG']) if _.isEmpty(groupsObj)
|
||||
|
||||
## (2) Party or Guild has members, fetch those users too
|
||||
# Subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild).
|
||||
groupsInfo = _.reduce groupsObj, ((m,g)->
|
||||
if g.type is 'guild' then m.guildIds.push(g.id) else m.partyId = g.id
|
||||
m.members = m.members.concat(g.members)
|
||||
@@ -101,7 +105,6 @@ setupSubscriptions = (page, model, params, next, cb) ->
|
||||
mObj = members.get()
|
||||
model.set "_members", _.object(_.pluck(mObj,'id'), mObj)
|
||||
|
||||
## Then subscribe to the groups themselves. We separate them by _party, _guilds, and _habitRPG (the "global" guild).
|
||||
# Note - selfQ *must* come after membersQ in subscribe, otherwise _user will only get the fields restricted by party-members in store.coffee. Strang bug, but easy to get around
|
||||
partyQ = model.query('groups').withIds(groupsInfo.partyId)
|
||||
if _.isEmpty(groupsInfo.guildIds)
|
||||
@@ -124,6 +127,7 @@ get '/', (page, model, params, next) ->
|
||||
#refLists
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
true
|
||||
page.render()
|
||||
|
||||
|
||||
@@ -131,9 +135,7 @@ get '/', (page, model, params, next) ->
|
||||
|
||||
ready (model) ->
|
||||
user = model.at('_user')
|
||||
model.setNull '_user.apiToken', derby.uuid()
|
||||
|
||||
require('./scoring').cron(model)
|
||||
browser = require './browser'
|
||||
|
||||
require('./character').app(exports, model)
|
||||
require('./tasks').app(exports, model)
|
||||
@@ -143,7 +145,22 @@ ready (model) ->
|
||||
require('./pets').app(exports, model)
|
||||
require('../server/private').app(exports, model)
|
||||
require('./debug').app(exports, model) if model.flags.nodeEnv != 'production'
|
||||
require('./browser').app(exports, model, app)
|
||||
browser.app(exports, model, app)
|
||||
require('./unlock').app(exports, model)
|
||||
require('./filters').app(exports, model)
|
||||
require('./challenges').app(exports, model)
|
||||
|
||||
uObj = misc.hydrate(user.get())
|
||||
cron = ->
|
||||
#return setTimeout(cron, 1) if model._txnQueue.length > 0
|
||||
# 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})
|
||||
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
|
||||
browser.resetDom(model)
|
||||
setTimeout (-> user.set('stats.hp', uObj.stats.hp)), 750
|
||||
cron() if algos.shouldCron(uObj)
|
||||
|
||||
+11
-123
@@ -1,84 +1,11 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
items = module.exports.items =
|
||||
weapon: [
|
||||
{index: 0, text: "Training Sword", classes: "weapon_0", notes:'Training weapon.', strength: 0, value:0}
|
||||
{index: 1, text: "Sword", classes:'weapon_1', notes:'Increases experience gain by 3%.', strength: 3, value:20}
|
||||
{index: 2, text: "Axe", classes:'weapon_2', notes:'Increases experience gain by 6%.', strength: 6, value:30}
|
||||
{index: 3, text: "Morningstar", classes:'weapon_3', notes:'Increases experience gain by 9%.', strength: 9, value:45}
|
||||
{index: 4, text: "Blue Sword", classes:'weapon_4', notes:'Increases experience gain by 12%.', strength: 12, value:65}
|
||||
{index: 5, text: "Red Sword", classes:'weapon_5', notes:'Increases experience gain by 15%.', strength: 15, value:90}
|
||||
{index: 6, text: "Golden Sword", classes:'weapon_6', notes:'Increases experience gain by 18%.', strength: 18, value:120}
|
||||
{index: 7, text: "Dark Souls Blade", classes:'weapon_7', notes:'Increases experience gain by 21%.', strength: 21, value:150}
|
||||
]
|
||||
armor: [
|
||||
{index: 0, text: "Cloth Armor", classes: 'armor_0', notes:'Training armor.', defense: 0, value:0}
|
||||
{index: 1, text: "Leather Armor", classes: 'armor_1', notes:'Decreases HP loss by 4%.', defense: 4, value:30}
|
||||
{index: 2, text: "Chain Mail", classes: 'armor_2', notes:'Decreases HP loss by 6%.', defense: 6, value:45}
|
||||
{index: 3, text: "Plate Mail", classes: 'armor_3', notes:'Decreases HP loss by 7%.', defense: 7, value:65}
|
||||
{index: 4, text: "Red Armor", classes: 'armor_4', notes:'Decreases HP loss by 8%.', defense: 8, value:90}
|
||||
{index: 5, text: "Golden Armor", classes: 'armor_5', notes:'Decreases HP loss by 10%.', defense: 10, value:120}
|
||||
{index: 6, text: "Shade Armor", classes: 'armor_6', notes:'Decreases HP loss by 12%.', defense: 12, value:150}
|
||||
]
|
||||
head: [
|
||||
{index: 0, text: "No Helm", classes: 'head_0', notes:'Training helm.', defense: 0, value:0}
|
||||
{index: 1, text: "Leather Helm", classes: 'head_1', notes:'Decreases HP loss by 2%.', defense: 2, value:15}
|
||||
{index: 2, text: "Chain Coif", classes: 'head_2', notes:'Decreases HP loss by 3%.', defense: 3, value:25}
|
||||
{index: 3, text: "Plate Helm", classes: 'head_3', notes:'Decreases HP loss by 4%.', defense: 4, value:45}
|
||||
{index: 4, text: "Red Helm", classes: 'head_4', notes:'Decreases HP loss by 5%.', defense: 5, value:60}
|
||||
{index: 5, text: "Golden Helm", classes: 'head_5', notes:'Decreases HP loss by 6%.', defense: 6, value:80}
|
||||
{index: 6, text: "Shade Helm", classes: 'head_6', notes:'Decreases HP loss by 7%.', defense: 7, value:100}
|
||||
]
|
||||
shield: [
|
||||
{index: 0, text: "No Shield", classes: 'shield_0', notes:'No Shield.', defense: 0, value:0}
|
||||
{index: 1, text: "Wooden Shield", classes: 'shield_1', notes:'Decreases HP loss by 3%', defense: 3, value:20}
|
||||
{index: 2, text: "Buckler", classes: 'shield_2', notes:'Decreases HP loss by 4%.', defense: 4, value:35}
|
||||
{index: 3, text: "Enforced Shield", classes: 'shield_3', notes:'Decreases HP loss by 5%.', defense: 5, value:55}
|
||||
{index: 4, text: "Red Shield", classes: 'shield_4', notes:'Decreases HP loss by 7%.', defense: 7, value:70}
|
||||
{index: 5, text: "Golden Shield", classes: 'shield_5', notes:'Decreases HP loss by 8%.', defense: 8, value:90}
|
||||
{index: 6, text: "Tormented Skull", classes: 'shield_6', notes:'Decreases HP loss by 9%.', defense: 9, value:120}
|
||||
]
|
||||
potion: {type: 'potion', text: "Potion", notes: "Recover 15 HP", value: 25, classes: 'potion'}
|
||||
reroll: {type: 'reroll', text: "Re-Roll", classes: 'reroll', notes: "Resets your task values back to 0 (yellow). Useful when everything's red and it's hard to stay alive.", value:0 }
|
||||
|
||||
pets: [
|
||||
{text: 'Wolf', name: 'Wolf', value: 3}
|
||||
{text: 'Tiger Cub', name: 'TigerCub', value: 3}
|
||||
#{text: 'Polar Bear Cub', name: 'PolarBearCub', value: 3} #commented out because there are no polarbear modifiers yet, special drop?
|
||||
{text: 'Panda Cub', name: 'PandaCub', value: 3}
|
||||
{text: 'Lion Cub', name: 'LionCub', value: 3}
|
||||
{text: 'Fox', name: 'Fox', value: 3}
|
||||
{text: 'Flying Pig', name: 'FlyingPig', value: 3}
|
||||
{text: 'Dragon', name: 'Dragon', value: 3}
|
||||
{text: 'Cactus', name: 'Cactus', value: 3}
|
||||
{text: 'Bear Cub', name: 'BearCub', value: 3}
|
||||
]
|
||||
|
||||
hatchingPotions: [
|
||||
{text: 'Base', name: 'Base', notes: "Hatches your pet in it's base form.", value: 1}
|
||||
{text: 'White', name: 'White', notes: 'Turns your animal into a White pet.', value: 2}
|
||||
{text: 'Desert', name: 'Desert', notes: 'Turns your animal into a Desert pet.', value: 2}
|
||||
{text: 'Red', name: 'Red', notes: 'Turns your animal into a Red pet.', value: 3}
|
||||
{text: 'Shade', name: 'Shade', notes: 'Turns your animal into a Shade pet.', value: 3}
|
||||
{text: 'Skeleton', name: 'Skeleton', notes: 'Turns your animal into a Skeleton.', value: 3}
|
||||
{text: 'Zombie', name: 'Zombie', notes: 'Turns your animal into a Zombie.', value: 4}
|
||||
{text: 'Cotton Candy Pink', name: 'CottonCandyPink', notes: 'Turns your animal into a Cotton Candy Pink pet.', value: 4}
|
||||
{text: 'Cotton Candy Blue', name: 'CottonCandyBlue', notes: 'Turns your animal into a Cotton Candy Blue pet.', value: 4}
|
||||
{text: 'Golden', name: 'Golden', notes: 'Turns your animal into a Golden pet.', value: 5}
|
||||
]
|
||||
|
||||
# add "type" to each item, so we can reference that as "weapon" or "armor" in the html
|
||||
_.each ['weapon', 'armor', 'head', 'shield'], (key) ->
|
||||
_.each items[key], (item) -> item.type = key
|
||||
|
||||
_.each items.pets, (pet) -> pet.notes = 'Find a hatching potion to pour on this egg, and one day it will hatch into a loyal pet.'
|
||||
_.each items.hatchingPotions, (hatchingPotion) -> hatchingPotion.notes = "Pour this on an egg, and it will hatch as a #{hatchingPotion.text} pet."
|
||||
items = require 'habitrpg-shared/script/items'
|
||||
_ = require 'lodash'
|
||||
|
||||
###
|
||||
server exports
|
||||
###
|
||||
module.exports.server = (model) ->
|
||||
model.set '_items', items
|
||||
model.set '_items', items.items
|
||||
updateStore(model)
|
||||
|
||||
###
|
||||
@@ -87,35 +14,11 @@ module.exports.server = (model) ->
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at '_user'
|
||||
|
||||
appExports.buyItem = (e, el, next) ->
|
||||
user = model.at '_user'
|
||||
#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)
|
||||
|
||||
gp = user.get 'stats.gp'
|
||||
appExports.buyItem = (e, el) ->
|
||||
[type, value, index] = [ $(el).attr('data-type'), $(el).attr('data-value'), $(el).attr('data-index') ]
|
||||
|
||||
return if gp < value
|
||||
# make sure deduction doesn't happen unless purchase was successful, see https://github.com/lefnire/habitrpg/issues/233
|
||||
deductGP = -> user.set 'stats.gp', gp - value
|
||||
if type == 'weapon'
|
||||
user.set 'items.weapon', index, deductGP
|
||||
updateStore model
|
||||
else if type == 'armor'
|
||||
user.set 'items.armor', index, deductGP
|
||||
updateStore model
|
||||
else if type == 'head'
|
||||
user.set 'items.head', index, deductGP
|
||||
updateStore model
|
||||
else if type == 'shield'
|
||||
user.set 'items.shield', index, deductGP
|
||||
updateStore model
|
||||
else if type == 'potion'
|
||||
hp = user.get 'stats.hp'
|
||||
hp += 15
|
||||
hp = 50 if hp > 50
|
||||
user.set 'stats.hp', hp, deductGP
|
||||
|
||||
if changes = items.buyItem(user.get(), type, value, index)
|
||||
_.each changes, (v,k) -> user.set k,v; true
|
||||
updateStore(model)
|
||||
|
||||
appExports.activateRewardsTab = ->
|
||||
model.set '_activeTabRewards', true
|
||||
@@ -124,26 +27,11 @@ module.exports.app = (appExports, model) ->
|
||||
model.set '_activeTabPets', true
|
||||
model.set '_activeTabRewards', false
|
||||
|
||||
###
|
||||
update store
|
||||
###
|
||||
module.exports.updateStore = updateStore = (model) ->
|
||||
model.setNull '_items.next', {}
|
||||
user = model.at('_user')
|
||||
equipped = user.get('items')
|
||||
|
||||
_.each ['weapon', 'armor', 'shield', 'head'], (type) ->
|
||||
i = parseInt(equipped?[type] || 0) + 1
|
||||
showNext = true
|
||||
if i is items[type].length - 1
|
||||
if (type in ['armor', 'shield', 'head'])
|
||||
showNext = user.get('backer.tier') >= 45 # backer armor
|
||||
else
|
||||
showNext = user.get('backer.tier') >= 70 # backer weapon
|
||||
else if i is items[type].length
|
||||
showNext = false
|
||||
|
||||
model.set "_items.next.#{type}", if showNext then items[type][i] else {hide:true}
|
||||
nextItems = items.updateStore(model.get('_user'))
|
||||
_.each nextItems, (v,k) -> model.set("_items.next.#{k}",v); true
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
_ = require 'lodash'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
items = require('habitrpg-shared/script/items').items
|
||||
helpers = require('habitrpg-shared/script/helpers')
|
||||
|
||||
###
|
||||
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) ->
|
||||
#return setTimeout( (-> score(taskId, direction)), 500) if model._txnQueue.length > 0
|
||||
user = model.at("_user")
|
||||
|
||||
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}
|
||||
|
||||
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
|
||||
|
||||
###
|
||||
Make sure model.get() returns all properties, see https://github.com/codeparty/racer/issues/116
|
||||
###
|
||||
module.exports.hydrate = hydrate = (spec) ->
|
||||
if _.isObject(spec) and !_.isArray(spec)
|
||||
hydrated = {}
|
||||
keys = _.keys(spec).concat(_.keys(spec.__proto__))
|
||||
keys.forEach (k) -> hydrated[k] = hydrate(spec[k])
|
||||
hydrated
|
||||
else spec
|
||||
|
||||
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 "lt", (a, b) -> a < b
|
||||
view.fn 'gt', (a, b) -> a > b
|
||||
view.fn "mod", (a, b) -> parseInt(a) % parseInt(b) == 0
|
||||
view.fn "notEqual", (a, b) -> (a != b)
|
||||
view.fn "and", -> _.reduce arguments, (cumm, curr) -> cumm && curr
|
||||
view.fn "or", -> _.reduce arguments, (cumm, curr) -> cumm || curr
|
||||
view.fn "truarr", (num) -> num-1
|
||||
view.fn 'count', (arr) -> arr?.length or 0
|
||||
view.fn 'int',
|
||||
get: (num) -> num
|
||||
set: (num) -> [parseInt(num)]
|
||||
|
||||
#iCal
|
||||
view.fn "encodeiCalLink", helpers.encodeiCalLink
|
||||
|
||||
#User
|
||||
view.fn "gems", (balance) -> return balance/0.25
|
||||
view.fn "username", helpers.username
|
||||
view.fn "tnl", algos.tnl
|
||||
view.fn 'equipped', helpers.equipped
|
||||
view.fn "gold", helpers.gold
|
||||
view.fn "silver", helpers.silver
|
||||
|
||||
#Stats
|
||||
view.fn 'userStr', helpers.userStr
|
||||
view.fn 'totalStr', helpers.totalStr
|
||||
view.fn 'userDef', helpers.userDef
|
||||
view.fn 'totalDef', helpers.totalDef
|
||||
view.fn 'itemText', helpers.itemText
|
||||
view.fn 'itemStat', helpers.itemStat
|
||||
|
||||
#Pets
|
||||
view.fn 'ownsPet', helpers.ownsPet
|
||||
|
||||
#Tasks
|
||||
view.fn 'taskClasses', helpers.taskClasses
|
||||
|
||||
#Chat
|
||||
view.fn 'friendlyTimestamp',helpers.friendlyTimestamp
|
||||
view.fn 'newChatMessages', helpers.newChatMessages
|
||||
view.fn 'relativeDate', helpers.relativeDate
|
||||
|
||||
#Tags
|
||||
view.fn 'noTags', helpers.noTags
|
||||
view.fn 'appliedTags', helpers.appliedTags
|
||||
@@ -1,10 +1,9 @@
|
||||
_ = require('underscore')
|
||||
helpers = require './helpers'
|
||||
_ = require('lodash')
|
||||
helpers = require('habitrpg-shared/script/helpers')
|
||||
|
||||
module.exports.app = (appExports, model, app) ->
|
||||
character = require './character'
|
||||
browser = require './browser'
|
||||
helpers = require './helpers'
|
||||
|
||||
_currentTime = model.at '_currentTime'
|
||||
|
||||
@@ -134,7 +133,9 @@ module.exports.app = (appExports, model, app) ->
|
||||
return next() unless e.keyCode is 13
|
||||
appExports.tavernSendChat()
|
||||
|
||||
appExports.deleteChatMessage = (e) -> e.at().remove() #requires the {#with}
|
||||
appExports.deleteChatMessage = (e) ->
|
||||
if confirm("Delete chat message?") is true
|
||||
e.at().remove() #requires the {#with}
|
||||
|
||||
app.on 'render', (ctx) ->
|
||||
$('#party-tab-link').on 'shown', (e) ->
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
_ = require 'underscore'
|
||||
{ randomVal } = require './helpers'
|
||||
{ pets, hatchingPotions } = require('./items').items
|
||||
_ = require 'lodash'
|
||||
{ randomVal } = require 'habitrpg-shared/script/helpers'
|
||||
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
|
||||
|
||||
###
|
||||
app exports
|
||||
@@ -46,14 +46,14 @@ module.exports.app = (appExports, model) ->
|
||||
return user.set 'items.currentPet', {} if user.get('items.currentPet.str') is petStr
|
||||
|
||||
[name, modifier] = petStr.split('-')
|
||||
pet = _.findWhere pets, name: name
|
||||
pet = _.find pets, {name: name}
|
||||
pet.modifier = modifier
|
||||
pet.str = petStr
|
||||
user.set 'items.currentPet', pet
|
||||
|
||||
appExports.buyHatchingPotion = (e, el) ->
|
||||
name = $(el).attr 'data-hatchingPotion'
|
||||
newHatchingPotion = _.findWhere hatchingPotions, name: name
|
||||
newHatchingPotion = _.find hatchingPotions, {name: name}
|
||||
gems = user.get('balance') * 4
|
||||
if gems >= newHatchingPotion.value
|
||||
if confirm "Buy this hatching potion with #{newHatchingPotion.value} of your #{gems} Gems?"
|
||||
@@ -64,7 +64,7 @@ module.exports.app = (appExports, model) ->
|
||||
|
||||
appExports.buyEgg = (e, el) ->
|
||||
name = $(el).attr 'data-egg'
|
||||
newEgg = _.findWhere pets, name: name
|
||||
newEgg = _.find pets, {name: name}
|
||||
gems = user.get('balance') * 4
|
||||
if gems >= newEgg.value
|
||||
if confirm "Buy this egg with #{newEgg.value} of your #{gems} Gems?"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
character = require './character'
|
||||
browser = require './browser'
|
||||
helpers = require './helpers'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
moment = require 'moment'
|
||||
_ = require 'underscore'
|
||||
{ randomVal } = helpers = require './helpers'
|
||||
browser = require './browser'
|
||||
character = require './character'
|
||||
items = require './items'
|
||||
{ pets, hatchingPotions } = items.items
|
||||
algos = require './algos'
|
||||
|
||||
MODIFIER = algos.MODIFIER # each new level, armor, weapon add 2% modifier (this mechanism will change)
|
||||
|
||||
###
|
||||
Drop System
|
||||
###
|
||||
randomDrop = (model, delta, priority, streak=0) ->
|
||||
user = model.at('_user')
|
||||
|
||||
# limit drops to 2 / day
|
||||
user.setNull 'items.lastDrop',
|
||||
date: +moment().subtract('d',1) # trick - set it to yesterday on first run, that way they can get drops today
|
||||
count: 0
|
||||
reachedDropLimit = (helpers.daysBetween(user.get('items.lastDrop.date'), +new Date, user.get('preferences.dayStart')) is 0) and user.get('items.lastDrop.count') >= 2
|
||||
return if reachedDropLimit
|
||||
|
||||
# % chance of getting a pet or meat
|
||||
chanceMultiplier = Math.abs(delta)
|
||||
chanceMultiplier *= algos.priorityValue(priority) # multiply chance by reddness
|
||||
chanceMultiplier += streak # streak bonus
|
||||
|
||||
if user.get('flags.dropsEnabled') and Math.random() < (.05 * chanceMultiplier)
|
||||
# current breakdown - 3% (adjustable) chance on drop
|
||||
# If they got a drop: 50% chance of egg, 50% Hatching Potion. If hatchingPotion, broken down further even further
|
||||
rarity = Math.random()
|
||||
|
||||
# Egg, 40% chance
|
||||
if rarity > .6
|
||||
drop = randomVal(pets)
|
||||
user.push 'items.eggs', drop
|
||||
drop.type = 'Egg'
|
||||
drop.dialog = "You've found a #{drop.text} Egg! #{drop.notes}"
|
||||
|
||||
# Hatching Potion, 60% chance - break down by rarity even more. FIXME this may not be the best method, so revisit
|
||||
else
|
||||
acceptableDrops = []
|
||||
|
||||
# Tier 5 (Blue Moon Rare)
|
||||
if rarity < .1
|
||||
acceptableDrops = ['Base', 'White', 'Desert', 'Red', 'Shade', 'Skeleton', 'Zombie', 'CottonCandyPink', 'CottonCandyBlue', 'Golden']
|
||||
|
||||
# Tier 4 (Very Rare)
|
||||
else if rarity < .2
|
||||
acceptableDrops = ['Base', 'White', 'Desert', 'Red', 'Shade', 'Skeleton', 'Zombie', 'CottonCandyPink', 'CottonCandyBlue']
|
||||
|
||||
# Tier 3 (Rare)
|
||||
else if rarity < .3
|
||||
acceptableDrops = ['Base', 'White', 'Desert', 'Red', 'Shade', 'Skeleton']
|
||||
|
||||
# Tier 2 (Scarce)
|
||||
else if rarity < .4
|
||||
acceptableDrops = ['Base', 'White', 'Desert']
|
||||
# Tier 1 (Common)
|
||||
else
|
||||
acceptableDrops = ['Base']
|
||||
|
||||
acceptableDrops = _.filter(hatchingPotions, (hatchingPotion) -> hatchingPotion.name in acceptableDrops)
|
||||
drop = randomVal acceptableDrops
|
||||
user.push 'items.hatchingPotions', drop.name
|
||||
drop.type = 'HatchingPotion'
|
||||
drop.dialog = "You've found a #{drop.text} Hatching Potion! #{drop.notes}"
|
||||
|
||||
model.set '_drop', drop
|
||||
$('#item-dropped-modal').modal 'show'
|
||||
|
||||
user.set 'items.lastDrop.date', +new Date
|
||||
user.incr 'items.lastDrop.count'
|
||||
|
||||
# {taskId} task you want to score
|
||||
# {direction} 'up' or 'down'
|
||||
# {times} # times to call score on this task (1 unless cron, usually)
|
||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||
score = (model, taskId, direction, times, batch, cron) ->
|
||||
user = model.at '_user'
|
||||
|
||||
commit = false
|
||||
unless batch?
|
||||
commit = true
|
||||
batch = new character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
obj = batch.obj()
|
||||
|
||||
{gp, hp, exp, lvl} = obj.stats
|
||||
|
||||
taskPath = "tasks.#{taskId}"
|
||||
taskObj = obj.tasks[taskId]
|
||||
{type, value, streak} = taskObj
|
||||
priority = taskObj.priority or '!'
|
||||
|
||||
# If they're trying to purhcase a too-expensive reward, confirm they want to take a hit for it
|
||||
if taskObj.value > obj.stats.gp and taskObj.type is 'reward'
|
||||
r = confirm "Not enough GP to purchase this reward, buy anyway and lose HP? (Punishment for taking a reward you didn't earn)."
|
||||
unless r
|
||||
batch.commit()
|
||||
return
|
||||
|
||||
delta = 0
|
||||
times ?= 1
|
||||
calculateDelta = (adjustvalue=true) ->
|
||||
# If multiple days have passed, multiply times days missed
|
||||
_.times times, (n) ->
|
||||
# Each iteration calculate the delta (nextDelta), which is then accumulated in delta
|
||||
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
||||
# rather than iteratively
|
||||
nextDelta = algos.taskDeltaFormula(value, direction)
|
||||
value += nextDelta if adjustvalue
|
||||
delta += nextDelta
|
||||
|
||||
addPoints = ->
|
||||
level = user.get('stats.lvl')
|
||||
weaponStrength = items.items.weapon[user.get('items.weapon')].strength
|
||||
exp += algos.expModifier(delta,weaponStrength,level, priority) / 2 # / 2 hack for now bcause people leveling too fast
|
||||
if streak
|
||||
gp += algos.gpModifier(delta, 1, priority, streak, model)
|
||||
else
|
||||
gp += algos.gpModifier(delta, 1, priority)
|
||||
|
||||
subtractPoints = ->
|
||||
level = user.get('stats.lvl')
|
||||
armorDefense = items.items.armor[user.get('items.armor')].defense
|
||||
helmDefense = items.items.head[user.get('items.head')].defense
|
||||
shieldDefense = items.items.shield[user.get('items.shield')].defense
|
||||
hp += algos.hpModifier(delta,armorDefense,helmDefense,shieldDefense,level, priority)
|
||||
|
||||
switch type
|
||||
when 'habit'
|
||||
calculateDelta()
|
||||
# Add habit value to habit-history (if different)
|
||||
if (delta > 0) then addPoints() else subtractPoints()
|
||||
taskObj.history ?= []
|
||||
if taskObj.value != value
|
||||
historyEntry = { date: +new Date, value: value }
|
||||
taskObj.history.push historyEntry
|
||||
batch.set "#{taskPath}.history", taskObj.history
|
||||
|
||||
when 'daily'
|
||||
if cron? # cron
|
||||
calculateDelta()
|
||||
subtractPoints()
|
||||
batch.set "#{taskPath}.streak", 0
|
||||
else
|
||||
calculateDelta(false)
|
||||
if delta != 0
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
if direction is 'up'
|
||||
streak = if streak then streak + 1 else 1
|
||||
else
|
||||
streak = if streak then streak - 1 else 0
|
||||
batch.set "#{taskPath}.streak", streak
|
||||
taskObj.streak = streak
|
||||
|
||||
|
||||
when 'todo'
|
||||
if cron? #cron
|
||||
calculateDelta()
|
||||
#don't touch stats on cron
|
||||
else
|
||||
calculateDelta()
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'reward'
|
||||
# Don't adjust values for rewards
|
||||
calculateDelta(false)
|
||||
# purchase item
|
||||
gp -= Math.abs(taskObj.value)
|
||||
num = parseFloat(taskObj.value).toFixed(2)
|
||||
# if too expensive, reduce health & zero gp
|
||||
if gp < 0
|
||||
hp += gp # hp - gp difference
|
||||
gp = 0
|
||||
|
||||
taskObj.value = value
|
||||
batch.set "#{taskPath}.value", taskObj.value
|
||||
origStats = _.clone obj.stats
|
||||
updateStats model, { hp, exp, gp }, batch
|
||||
|
||||
# Commit
|
||||
if commit
|
||||
# newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*)
|
||||
newStats = _.clone batch.obj().stats
|
||||
_.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key]
|
||||
batch.setStats(newStats)
|
||||
# batch.setStats()
|
||||
batch.commit()
|
||||
|
||||
# Drop system
|
||||
randomDrop(model, delta, priority, streak) if direction is 'up'
|
||||
|
||||
return delta
|
||||
|
||||
###
|
||||
Updates user stats with new stats. Handles death, leveling up, etc
|
||||
{stats} new stats
|
||||
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
|
||||
###
|
||||
updateStats = (model, newStats, batch) ->
|
||||
user = model.at '_user'
|
||||
obj = batch.obj()
|
||||
|
||||
# if user is dead, dont do anything
|
||||
return if obj.stats.hp <= 0
|
||||
|
||||
if newStats.hp?
|
||||
# Game Over
|
||||
if newStats.hp <= 0
|
||||
obj.stats.hp = 0 # signifies dead
|
||||
return
|
||||
else
|
||||
obj.stats.hp = newStats.hp
|
||||
|
||||
if newStats.exp?
|
||||
tnl = algos.tnl(obj.stats.lvl)
|
||||
#silent = false
|
||||
# if we're at level 100, turn xp to gold
|
||||
if obj.stats.lvl >= 100
|
||||
newStats.gp += newStats.exp / 15
|
||||
newStats.exp = 0
|
||||
obj.stats.lvl = 100
|
||||
else
|
||||
# level up & carry-over exp
|
||||
if newStats.exp >= tnl
|
||||
#silent = true # push through the negative xp silently
|
||||
user.set('stats.exp', newStats.exp) # push normal + notification
|
||||
while newStats.exp >= tnl and obj.stats.lvl < 100 # keep levelling up
|
||||
newStats.exp -= tnl
|
||||
obj.stats.lvl++
|
||||
tnl = algos.tnl(obj.stats.lvl)
|
||||
if obj.stats.lvl== 100
|
||||
newStats.exp = 0
|
||||
obj.stats.hp = 50
|
||||
|
||||
obj.stats.exp = newStats.exp
|
||||
#if silent
|
||||
#console.log("pushing silent :" + obj.stats.exp)
|
||||
#user.pass(true).set('stats.exp', obj.stats.exp)
|
||||
|
||||
# Set flags when they unlock features
|
||||
# NOTE we have to first model.set() the flag to true, then AFTER that obj.flags.flag = true
|
||||
# The reason is model.on() listeners still track object references, so if obj.flags.flags = true and then we
|
||||
# model.set(), the second argument of .on() listeners will be true (in otherwords, before/after tests will fail)
|
||||
if !obj.flags.customizationsNotification and (obj.stats.exp > 10 or obj.stats.lvl > 1)
|
||||
batch.set 'flags.customizationsNotification', true
|
||||
obj.flags.customizationsNotification = true
|
||||
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
|
||||
batch.set 'flags.itemsEnabled', true
|
||||
obj.flags.itemsEnabled = true
|
||||
if !obj.flags.partyEnabled and obj.stats.lvl >= 3
|
||||
batch.set 'flags.partyEnabled', true
|
||||
obj.flags.partyEnabled = true
|
||||
if !obj.flags.dropsEnabled and obj.stats.lvl >= 4
|
||||
batch.set 'flags.dropsEnabled', true
|
||||
obj.flags.dropsEnabled = true
|
||||
|
||||
if newStats.gp?
|
||||
#FIXME what was I doing here? I can't remember, gp isn't defined
|
||||
gp = 0.0 if (!gp? or gp<0)
|
||||
obj.stats.gp = newStats.gp
|
||||
|
||||
###
|
||||
At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
||||
For incomplete Dailys, deduct experience
|
||||
###
|
||||
cron = (model) ->
|
||||
user = model.at '_user'
|
||||
today = +new Date
|
||||
|
||||
lastCron = user.get('lastCron')
|
||||
# New user (!lastCron, lastCron==new) or it got busted somehow, maybe they went to a different timezone
|
||||
if !lastCron? or lastCron is 'new' or moment(lastCron).isAfter(today)
|
||||
user.set('lastCron', +new Date)
|
||||
return
|
||||
|
||||
daysMissed = helpers.daysBetween(user.get('lastCron'), today, user.get('preferences.dayStart'))
|
||||
if daysMissed > 0
|
||||
|
||||
# User is resting at the inn. Used to be we un-checked each daily without performing calculation (see commits before fb29e35)
|
||||
# but to prevent abusing the inn (http://goo.gl/GDb9x) we now do *not* calculate dailies, and simply set lastCron to today
|
||||
if user.get('flags.rest') is true
|
||||
return user.set('lastCron', today)
|
||||
|
||||
batch = new character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
obj = batch.obj()
|
||||
batch.set 'lastCron', today
|
||||
|
||||
hpBefore = obj.stats.hp #we'll use this later so we can animate hp loss
|
||||
# Tally each task
|
||||
todoTally = 0
|
||||
_.each obj.tasks, (taskObj) ->
|
||||
{id, type, completed, repeat} = taskObj
|
||||
if type in ['todo', 'daily']
|
||||
# Deduct experience for missed Daily tasks, but not for Todos (just increase todo's value)
|
||||
unless completed
|
||||
scheduleMisses = daysMissed
|
||||
# for dailys which have repeat dates, need to calculate how many they've missed according to their own schedule
|
||||
if (type is 'daily') and repeat
|
||||
scheduleMisses = 0
|
||||
_.times daysMissed, (n) ->
|
||||
thatDay = moment(today).subtract('days', n+1)
|
||||
scheduleMisses++ if helpers.shouldDo(thatDay, repeat, obj.preferences?.dayStart) is true
|
||||
score(model, id, 'down', scheduleMisses, batch, true) if scheduleMisses > 0
|
||||
|
||||
if type == 'daily'
|
||||
if completed #set OHV for completed dailies
|
||||
newValue = taskObj.value + algos.taskDeltaFormula(taskObj.value,'up')
|
||||
batch.set "tasks.#{taskObj.id}.value", newValue
|
||||
|
||||
taskObj.history ?= []
|
||||
taskObj.history.push { date: +new Date, value: taskObj.value }
|
||||
batch.set "tasks.#{taskObj.id}.history", taskObj.history
|
||||
batch.set "tasks.#{taskObj.id}.completed", false
|
||||
else
|
||||
value = obj.tasks[taskObj.id].value #get updated value
|
||||
absVal = if (completed) then Math.abs(value) else value
|
||||
todoTally += absVal
|
||||
else if type is 'habit' # slowly reset 'onlies' value to 0
|
||||
if taskObj.up==false or taskObj.down==false
|
||||
if Math.abs(taskObj.value) < 0.1
|
||||
batch.set "tasks.#{taskObj.id}.value", 0
|
||||
else
|
||||
batch.set "tasks.#{taskObj.id}.value", taskObj.value / 2
|
||||
|
||||
# Finished tallying
|
||||
obj.history ?= {}; obj.history.todos ?= []; obj.history.exp ?= []
|
||||
obj.history.todos.push { date: today, value: todoTally }
|
||||
# tally experience
|
||||
expTally = obj.stats.exp
|
||||
lvl = 0 #iterator
|
||||
while lvl < (obj.stats.lvl-1)
|
||||
lvl++
|
||||
expTally += algos.tnl(lvl)
|
||||
obj.history.exp.push { date: today, value: expTally }
|
||||
|
||||
# Set the new user specs, and animate HP loss
|
||||
[hpAfter, obj.stats.hp] = [obj.stats.hp, hpBefore]
|
||||
batch.setStats()
|
||||
batch.set('history', obj.history)
|
||||
batch.commit()
|
||||
browser.resetDom(model)
|
||||
setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss
|
||||
|
||||
|
||||
module.exports = {
|
||||
score: score
|
||||
cron: cron
|
||||
|
||||
# testing stuff
|
||||
expModifier: algos.expModifier
|
||||
hpModifier: algos.hpModifier
|
||||
taskDeltaFormula: algos.taskDeltaFormula
|
||||
}
|
||||
+24
-38
@@ -1,10 +1,15 @@
|
||||
scoring = require './scoring'
|
||||
helpers = require './helpers'
|
||||
_ = require 'underscore'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
_ = require 'lodash'
|
||||
moment = require 'moment'
|
||||
character = require './character'
|
||||
misc = require './misc'
|
||||
|
||||
|
||||
###
|
||||
Make scoring functionality available to the app
|
||||
###
|
||||
module.exports.app = (appExports, model) ->
|
||||
character = require './character'
|
||||
user = model.at('_user')
|
||||
|
||||
appExports.addTask = (e, el) ->
|
||||
@@ -30,26 +35,24 @@ module.exports.app = (appExports, model) ->
|
||||
e.at().unshift newTask # e.at() in this case is the list, which was scoped here using {#with @list}...{/}
|
||||
newModel.set ''
|
||||
|
||||
appExports.del = (e, el) ->
|
||||
appExports.del = (e) ->
|
||||
# Derby extends model.at to support creation from DOM nodes
|
||||
task = e.at()
|
||||
id = task.get('id')
|
||||
|
||||
history = task.get('history')
|
||||
if history and history.length>2
|
||||
if history and history.length > 2
|
||||
# prevent delete-and-recreate hack on red tasks
|
||||
if task.get('value') < 0
|
||||
result = confirm("Are you sure? Deleting this task will hurt you (to prevent deleting, then re-creating red tasks).")
|
||||
if result != true
|
||||
return # Cancel. Don't delete, don't hurt user
|
||||
else
|
||||
if confirm("Are you sure? Deleting this task will hurt you (to prevent deleting, then re-creating red tasks).") is true
|
||||
task.set('type','habit') # hack to make sure it hits HP, instead of performing "undo checkbox"
|
||||
scoring.score(model, id, direction:'down')
|
||||
misc.score(model, id, 'down', true)
|
||||
else
|
||||
return # Cancel. Don't delete, don't hurt user
|
||||
|
||||
# prevent accidently deleting long-standing tasks
|
||||
else
|
||||
result = confirm("Are you sure you want to delete this task?")
|
||||
return if result != true
|
||||
return unless confirm("Are you sure you want to delete this task?") is true
|
||||
|
||||
#TODO bug where I have to delete from _users.tasks AND _{type}List,
|
||||
# fix when query subscriptions implemented properly
|
||||
@@ -63,7 +66,7 @@ module.exports.app = (appExports, model) ->
|
||||
completedIds = _.pluck( _.where(model.get('_todoList'), {completed:true}), 'id')
|
||||
todoIds = user.get('todoIds')
|
||||
|
||||
_.each completedIds, (id) -> user.del "tasks.#{id}"
|
||||
_.each completedIds, (id) -> user.del "tasks.#{id}"; true
|
||||
user.set 'todoIds', _.difference(todoIds, completedIds)
|
||||
|
||||
appExports.toggleDay = (e, el) ->
|
||||
@@ -104,40 +107,22 @@ module.exports.app = (appExports, model) ->
|
||||
appExports.todosShowRemaining = -> model.set '_showCompleted', false
|
||||
appExports.todosShowCompleted = -> model.set '_showCompleted', true
|
||||
|
||||
setUndo = (stats, task) ->
|
||||
previousUndo = model.get('_undo')
|
||||
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
|
||||
timeoutId = setTimeout (-> model.del('_undo')), 10000
|
||||
model.set '_undo', {stats:stats, task:task, timeoutId: timeoutId}
|
||||
|
||||
|
||||
###
|
||||
Call scoring functions for habits & rewards (todos & dailies handled below)
|
||||
###
|
||||
appExports.score = (e, el) ->
|
||||
task= model.at $(el).parents('li')[0]
|
||||
taskObj = task.get()
|
||||
id = $(el).parents('li').attr('data-id')
|
||||
direction = $(el).attr('data-direction')
|
||||
|
||||
# set previous state for undo
|
||||
setUndo _.clone(user.get('stats')), _.clone(taskObj)
|
||||
|
||||
scoring.score(model, taskObj.id, direction)
|
||||
misc.score(model, id, direction, true)
|
||||
|
||||
###
|
||||
This is how we handle appExports.score for todos & dailies. Due to Derby's special handling of `checked={:task.completd}`,
|
||||
the above function doesn't work so we need a listener here
|
||||
###
|
||||
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?.cron # Don't do this stuff on cron
|
||||
direction = if completed then 'up' else 'down'
|
||||
|
||||
# set previous state for undo
|
||||
taskObj = _.clone user.get("tasks.#{i}")
|
||||
taskObj.completed = previous
|
||||
setUndo _.clone(user.get('stats')), taskObj
|
||||
|
||||
scoring.score(model, i, direction)
|
||||
misc.score(model, i, direction, true)
|
||||
|
||||
###
|
||||
Undo
|
||||
@@ -148,14 +133,15 @@ module.exports.app = (appExports, model) ->
|
||||
batch = character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
model.del '_undo'
|
||||
_.each undo.stats, (val, key) -> batch.set "stats.#{key}", val
|
||||
_.each undo.stats, (val, key) -> batch.set "stats.#{key}", val; true
|
||||
taskPath = "tasks.#{undo.task.id}"
|
||||
_.each undo.task, (val, key) ->
|
||||
return if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
|
||||
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
|
||||
true
|
||||
batch.commit()
|
||||
|
||||
appExports.tasksToggleAdvanced = (e, el) ->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
_ = require 'underscore'
|
||||
{ randomVal } = require './helpers'
|
||||
{ pets, hatchingPotions } = require('./items').items
|
||||
_ = require 'lodash'
|
||||
{ randomVal } = require 'habitrpg-shared/script/helpers'
|
||||
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
|
||||
|
||||
###
|
||||
Listeners to enabled flags, set notifications to the user when they've unlocked features
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
express = require 'express'
|
||||
router = new express.Router()
|
||||
|
||||
scoring = require '../app/scoring'
|
||||
_ = require 'underscore'
|
||||
{ tnl } = require '../app/algos'
|
||||
_ = require 'lodash'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
validator = require 'derby-auth/node_modules/validator'
|
||||
check = validator.check
|
||||
sanitize = validator.sanitize
|
||||
misc = require '../app/misc'
|
||||
|
||||
NO_TOKEN_OR_UID = err: "You must include a token and uid (user id) in your request"
|
||||
NO_USER_FOUND = err: "No user found."
|
||||
@@ -51,7 +52,7 @@ auth = (req, res, next) ->
|
||||
router.get '/user', auth, (req, res) ->
|
||||
user = req.userObj
|
||||
|
||||
user.stats.toNextLevel = tnl user.stats.lvl
|
||||
user.stats.toNextLevel = algos.tnl user.stats.lvl
|
||||
user.stats.maxHealth = 50
|
||||
|
||||
delete user.apiToken
|
||||
@@ -83,7 +84,7 @@ router.put '/user', auth, (req, res) ->
|
||||
acceptableAttrs = ['flags', 'history', 'items', 'preferences', 'profile', 'stats']
|
||||
user.set 'lastCron', partialUser.lastCron if partialUser.lastCron?
|
||||
_.each acceptableAttrs, (attr) ->
|
||||
_.each partialUser[attr], (val, key) -> user.set("#{attr}.#{key}", val)
|
||||
_.each partialUser[attr], (val, key) -> user.set("#{attr}.#{key}", val);true
|
||||
|
||||
updateTasks partialUser.tasks, req.user, req.getModel() if partialUser.tasks?
|
||||
|
||||
@@ -255,7 +256,8 @@ scoreTask = (req, res, next) ->
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
model.at("_#{type}List").push task
|
||||
|
||||
delta = scoring.score(model, taskId, direction)
|
||||
#FIXME
|
||||
delta = misc.score(model, taskId, direction)
|
||||
result = model.get '_user.stats'
|
||||
result.delta = delta
|
||||
res.json result
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
express = require 'express'
|
||||
router = new express.Router()
|
||||
|
||||
scoring = require '../app/scoring'
|
||||
_ = require 'underscore'
|
||||
_ = require 'lodash'
|
||||
icalendar = require('icalendar')
|
||||
api = require './api'
|
||||
|
||||
@@ -45,6 +44,7 @@ router.get '/v1/users/:uid/calendar.ics', (req, res) ->
|
||||
d.date_only = true
|
||||
event.setDate d
|
||||
ical.addComponent event
|
||||
true
|
||||
res.type('text/calendar')
|
||||
formattedIcal = ical.toString().replace(/DTSTART\:/g, 'DTSTART;VALUE=DATE:')
|
||||
res.send(200, formattedIcal)
|
||||
|
||||
@@ -12,6 +12,8 @@ priv = require './private'
|
||||
habitrpgStore = require './store'
|
||||
middleware = require './middleware'
|
||||
|
||||
helpers = require("habitrpg-shared/script/helpers")
|
||||
|
||||
## RACER CONFIGURATION ##
|
||||
|
||||
#racer.io.set('transports', ['xhr-polling'])
|
||||
@@ -50,7 +52,7 @@ strategies =
|
||||
options =
|
||||
domain: process.env.BASE_URL || 'http://localhost:3000'
|
||||
allowPurl: true
|
||||
schema: require('../app/character').newUserObject()
|
||||
schema: helpers.newUser(true)
|
||||
|
||||
# This has to happen before our middleware stuff
|
||||
auth.store(store, habitrpgStore.customAccessControl)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_ = require 'underscore'
|
||||
_ = require 'lodash'
|
||||
character = require "../app/character"
|
||||
|
||||
module.exports.middleware = (req, res, next) ->
|
||||
@@ -37,7 +37,7 @@ module.exports.app = (appExports, model) ->
|
||||
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 == 'reward'
|
||||
_.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type is 'reward';true
|
||||
batch.commit()
|
||||
|
||||
module.exports.routes = (expressApp) ->
|
||||
|
||||
Reference in New Issue
Block a user