Merge remote-tracking branch 'upstream/develop' into ui-tweaks
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Make sure people aren't overflowing their exp with the new system
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
function oldTnl(level) {
|
||||
return (Math.pow(level,2)*10)+(level*10)+80
|
||||
}
|
||||
|
||||
function newTnl(level) {
|
||||
var value = 0;
|
||||
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
|
||||
}
|
||||
|
||||
var newTnl = newTnl(user.stats.lvl);
|
||||
if (user.stats.exp > newTnl) {
|
||||
var percent = user.stats.exp / oldTnl(user.stats.lvl);
|
||||
percent = (percent>1) ? 1 : percent;
|
||||
user.stats.exp = newTnl * percent;
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set: {'stats.exp': user.stats.exp}},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Users were experiencing a lot of extreme Exp multiplication (https://github.com/lefnire/habitrpg/issues/594).
|
||||
* This sets things straight, and in preparation for another algorithm overhaul
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
if (user.stats.exp >= 3580) {
|
||||
user.stats.exp = 0;
|
||||
}
|
||||
|
||||
if (user.stats.lvl > 100) {
|
||||
user.stats.lvl = 100;
|
||||
}
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
// remove corrupt tasks
|
||||
if (!task) {
|
||||
delete user.tasks[key];
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix busted values
|
||||
if (task.value > 21.27) {
|
||||
task.value = 21.27;
|
||||
}
|
||||
else if (task.value < -47.27) {
|
||||
task.value = -47.27;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{
|
||||
'stats.lvl': user.stats.lvl,
|
||||
'stats.exp': user.stats.exp,
|
||||
'tasks' : user.tasks
|
||||
}
|
||||
},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/find_unique_user.js
|
||||
|
||||
/**
|
||||
* There are some rare instances of lost user accounts, due to a corrupt user auth variable (see https://github.com/lefnire/habitrpg/wiki/User-ID)
|
||||
* Past in the text of a unique habit here to find the user, then you can restore their UUID
|
||||
*/
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
var found = _.findWhere(user.tasks, {text: "Replace Me"})
|
||||
if (found) printjson({id:user._id, auth:user.auth});
|
||||
})
|
||||
+26
-20
@@ -1,5 +1,5 @@
|
||||
|
||||
MODIFIER = .02
|
||||
XP = 15
|
||||
HP = 2
|
||||
|
||||
priorityValue = (priority='!') ->
|
||||
switch priority
|
||||
@@ -9,7 +9,11 @@ priorityValue = (priority='!') ->
|
||||
else 1
|
||||
|
||||
module.exports.tnl = (level) ->
|
||||
return (Math.pow(level,2)*10)+(level*10)+80
|
||||
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
|
||||
@@ -18,11 +22,12 @@ module.exports.tnl = (level) ->
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.expModifier = (value, weaponStrength, level, priority='!') ->
|
||||
levelModifier = (level-1) * MODIFIER
|
||||
weaponModifier = weaponStrength / 100
|
||||
strength = 1 + weaponModifier + levelModifier
|
||||
return value * strength * priorityValue(priority)
|
||||
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
|
||||
@@ -32,11 +37,12 @@ module.exports.expModifier = (value, weaponStrength, level, priority='!') ->
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.hpModifier = (value, armorDefense, helmDefense, shieldDefense, level, priority='!') ->
|
||||
levelModifier = (level-1) * MODIFIER
|
||||
armorModifier = (armorDefense + helmDefense + shieldDefense) / 100
|
||||
defense = 1 - levelModifier + armorModifier
|
||||
return value * defense * priorityValue(priority)
|
||||
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
|
||||
@@ -52,10 +58,10 @@ module.exports.gpModifier = (value, modifier, priority='!') ->
|
||||
{direction} up or down
|
||||
###
|
||||
module.exports.taskDeltaFormula = (currentValue, direction) ->
|
||||
if direction is 'up'
|
||||
delta = Math.max(Math.pow(0.95,currentValue),0.25)
|
||||
else
|
||||
delta = -Math.min(Math.pow(0.95,currentValue),5)
|
||||
#console.log("CV = " + currentValue + " Dir = " + direction + " delta = " + delta)
|
||||
delta = 20 if delta > 20
|
||||
return delta
|
||||
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
|
||||
|
||||
|
||||
|
||||
+13
-9
@@ -9,7 +9,10 @@ restoreRefs = module.exports.restoreRefs = (model) ->
|
||||
# see https://github.com/lefnire/habitrpg/issues/4
|
||||
# also update in scoring.coffee. TODO create a function accessible in both locations
|
||||
#TODO find a method of calling algos.tnl()
|
||||
10*Math.pow(lvl,2)+(lvl*10)+80
|
||||
if lvl==100
|
||||
0
|
||||
else
|
||||
Math.round(((Math.pow(lvl,2)*0.25)+(10 * lvl) + 139.75)/10)*10
|
||||
|
||||
#refLists
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
@@ -171,13 +174,14 @@ setupGrowlNotifications = (model) ->
|
||||
else if num > 0
|
||||
statsNotification "<i class='icon-heart'></i> + #{rounded} HP", 'hp' # gained hp from potion/level?
|
||||
|
||||
user.on 'set', 'stats.exp', (captures, args, isLocal, silent) ->
|
||||
num = captures - args
|
||||
rounded = Math.abs(num.toFixed(1))
|
||||
if num < 0 and not silent
|
||||
statsNotification "<i class='icon-star'></i> - #{rounded} XP", 'xp'
|
||||
else if num > 0
|
||||
statsNotification "<i class='icon-star'></i> + #{rounded} XP", 'xp'
|
||||
user.on 'set', 'stats.exp', (captures, args, isLocal, silent=false) ->
|
||||
# unless silent
|
||||
num = captures - args
|
||||
rounded = Math.abs(num.toFixed(1))
|
||||
if num < 0 and num > -100 # TODO fix hackey negative notification supress
|
||||
statsNotification "<i class='icon-star'></i> - #{rounded} XP", 'xp'
|
||||
else if num > 0
|
||||
statsNotification "<i class='icon-star'></i> + #{rounded} XP", 'xp'
|
||||
|
||||
user.on 'set', 'stats.gp', (captures, args) ->
|
||||
num = captures - args
|
||||
@@ -195,7 +199,7 @@ setupGrowlNotifications = (model) ->
|
||||
user.on 'set', 'stats.lvl', (captures, args) ->
|
||||
if captures > args
|
||||
if captures is 1 and args is 0
|
||||
statsNotification '<i class="icon-death"></i> You died!', 'death'
|
||||
statsNotification '<i class="icon-death"></i> You died! Game over.', 'death'
|
||||
else
|
||||
statsNotification '<i class="icon-chevron-up"></i> Level Up!', 'lvl'
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ module.exports.app = (appExports, model) ->
|
||||
batch = new BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
$('#restore-form input').each ->
|
||||
batch.set $(this).attr('data-for'), parseInt($(this).val())
|
||||
batch.set $(this).attr('data-for'), parseInt($(this).val() || 1)
|
||||
batch.commit()
|
||||
|
||||
user.on 'set', 'flags.customizationsNotification', (captures, args) ->
|
||||
|
||||
@@ -8,8 +8,13 @@ module.exports.app = (appExports, model) ->
|
||||
user.set 'lastCron', yesterday
|
||||
window.location.reload()
|
||||
|
||||
appExports.emulateTenDays = ->
|
||||
yesterday = +moment().subtract('days', 10).toDate()
|
||||
user.set 'lastCron', yesterday
|
||||
window.location.reload()
|
||||
|
||||
appExports.cheat = ->
|
||||
user.incr 'stats.exp', 20
|
||||
user.incr 'stats.exp', model.get '_tnl'
|
||||
user.incr 'stats.gp', 1000
|
||||
|
||||
appExports.reset = ->
|
||||
|
||||
@@ -19,6 +19,9 @@ module.exports.viewHelpers = (view) ->
|
||||
|
||||
view.fn "floor", (num) ->
|
||||
Math.floor num
|
||||
|
||||
view.fn "ceil", (num) ->
|
||||
Math.ceil num
|
||||
|
||||
view.fn "lt", (a, b) ->
|
||||
a < b
|
||||
|
||||
+27
-18
@@ -46,14 +46,13 @@ score = (model, taskId, direction, times, batch, cron) ->
|
||||
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
||||
# rather than iteratively
|
||||
nextDelta = algos.taskDeltaFormula(value, direction)
|
||||
value = Math.max(value + nextDelta, -31) if adjustvalue #cap values so we don't get silly values
|
||||
value += nextDelta if adjustvalue
|
||||
delta += nextDelta
|
||||
|
||||
addPoints = ->
|
||||
level = user.get('stats.lvl')
|
||||
weaponStrength = items.items.weapon[user.get('items.weapon')].strength
|
||||
modified = algos.expModifier(delta,weaponStrength,level, priority)
|
||||
exp += modified*10
|
||||
exp += algos.expModifier(delta,weaponStrength,level, priority)
|
||||
gp += algos.gpModifier(delta, 1, priority)
|
||||
|
||||
subtractPoints = ->
|
||||
@@ -61,8 +60,7 @@ score = (model, taskId, direction, times, batch, cron) ->
|
||||
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
|
||||
modified = algos.hpModifier(delta,armorDefense,helmDefense,shieldDefense,level, priority)
|
||||
hp += modified
|
||||
hp += algos.hpModifier(delta,armorDefense,helmDefense,shieldDefense,level, priority)
|
||||
|
||||
switch type
|
||||
when 'habit'
|
||||
@@ -81,7 +79,8 @@ score = (model, taskId, direction, times, batch, cron) ->
|
||||
subtractPoints()
|
||||
else
|
||||
calculateDelta(false)
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
if delta != 0
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'todo'
|
||||
if cron? #cron
|
||||
@@ -137,20 +136,30 @@ updateStats = (model, newStats, batch) ->
|
||||
obj.stats.hp = newStats.hp
|
||||
|
||||
if newStats.exp?
|
||||
# level up & carry-over exp
|
||||
tnl = model.get '_tnl'
|
||||
silent = false
|
||||
if newStats.exp >= tnl
|
||||
silent = true
|
||||
user.set('stats.exp', newStats.exp)
|
||||
while newStats.exp >= tnl # keep levelling up
|
||||
newStats.exp -= tnl
|
||||
obj.stats.lvl++
|
||||
tnl = algos.tnl(obj.stats.lvl)
|
||||
obj.stats.hp = 50
|
||||
#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
|
||||
user.pass(silent:true).set('stats.exp', obj.stats.exp) if silent
|
||||
#if silent
|
||||
#console.log("pushing silent :" + obj.stats.exp)
|
||||
#user.pass(true).set('stats.exp', obj.stats.exp)
|
||||
|
||||
# Set flags when they unlock features
|
||||
if !obj.flags.customizationsNotification and (obj.stats.exp > 10 or obj.stats.lvl > 1)
|
||||
@@ -218,7 +227,7 @@ cron = (model) ->
|
||||
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' #reset 'onlies' value to 0
|
||||
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.02
|
||||
batch.set "tasks.#{taskObj.id}.value", 0
|
||||
|
||||
@@ -3,6 +3,7 @@ router = new express.Router()
|
||||
|
||||
scoring = require '../app/scoring'
|
||||
_ = require 'underscore'
|
||||
{ tnl } = require '../app/algos'
|
||||
validator = require 'derby-auth/node_modules/validator'
|
||||
check = validator.check
|
||||
sanitize = validator.sanitize
|
||||
@@ -41,6 +42,9 @@ auth = (req, res, next) ->
|
||||
router.get '/user', auth, (req, res) ->
|
||||
user = req.userObj
|
||||
|
||||
user.stats.toNextLevel = tnl user.stats.lvl
|
||||
user.stats.maxHealth = 50
|
||||
|
||||
delete user.apiToken
|
||||
|
||||
res.json user
|
||||
|
||||
@@ -57,6 +57,7 @@ auth.store(store, habitrpgStore.customAccessControl)
|
||||
|
||||
mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
||||
expressApp
|
||||
.use(middleware.allowCrossDomain)
|
||||
.use(express.favicon("#{publicPath}/favicon.ico"))
|
||||
# Gzip static files and serve from memory
|
||||
.use(gzippo.staticGzip(publicPath, maxAge: ONE_YEAR))
|
||||
@@ -74,7 +75,6 @@ mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
||||
)
|
||||
# Adds req.getModel method
|
||||
.use(store.modelMiddleware())
|
||||
.use(middleware.allowCrossDomain)
|
||||
# API should be hit before all other routes
|
||||
.use('/api/v1', require('./api').middleware)
|
||||
.use(require('./deprecated').middleware)
|
||||
|
||||
@@ -15,7 +15,11 @@ module.exports.view = (req, res, next) ->
|
||||
|
||||
#CORS middleware
|
||||
module.exports.allowCrossDomain = (req, res, next) ->
|
||||
res.header "Access-Control-Allow-Origin", '*'
|
||||
res.header "Access-Control-Allow-Methods", "GET,PUT,POST,DELETE"
|
||||
res.header "Access-Control-Allow-Headers", "Content-Type"
|
||||
next()
|
||||
res.header "Access-Control-Allow-Origin", (req.headers.origin || "*")
|
||||
res.header "Access-Control-Allow-Methods", "OPTIONS,GET,POST,PUT,HEAD,DELETE"
|
||||
res.header "Access-Control-Allow-Headers", "Content-Type,X-Requested-With,x-api-user,x-api-key"
|
||||
|
||||
if req.method is 'OPTIONS'
|
||||
res.send(200);
|
||||
else
|
||||
next()
|
||||
@@ -100,6 +100,8 @@ describe 'API', ->
|
||||
expect(res.body.id).not.to.be.empty()
|
||||
self = _.clone(currentUser)
|
||||
delete self.apiToken
|
||||
self.stats.toNextLevel = 150
|
||||
self.stats.maxHealth = 50
|
||||
|
||||
expect(res.body).to.eql self
|
||||
done()
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
{else}
|
||||
<div class='pull-right'>
|
||||
<button class='btn' x-bind="click:emulateNextDay">Emulate Next Day</button>
|
||||
<button class='btn' x-bind="click:cheat">Add GP & Exp</button>
|
||||
<button class='btn' x-bind="click:emulateTenDays">Emulate 10 Days</button>
|
||||
<button class='btn' x-bind="click:cheat">Insta Level</button>
|
||||
<button class='btn' x-bind='click:reset'>Reset Level</button>
|
||||
</div>
|
||||
{/}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<div class="progress-bars">
|
||||
<div class="progress progress-danger" rel=tooltip data-placement=bottom title="Health">
|
||||
<div class="bar" style="width: {percent(_user.stats.hp, 50)}%;"></div>
|
||||
<span class="progress-text"><i class=icon-heart></i> {round(_user.stats.hp)} / 50</span>
|
||||
<span class="progress-text"><i class=icon-heart></i> {ceil(_user.stats.hp)} / 50</span>
|
||||
</div>
|
||||
|
||||
<div class="progress progress-warning" rel=tooltip data-placement=bottom title="Experience">
|
||||
|
||||
Reference in New Issue
Block a user