diff --git a/dist/habitrpg-shared.js b/dist/habitrpg-shared.js index 63fab329e1..1c81897873 100644 --- a/dist/habitrpg-shared.js +++ b/dist/habitrpg-shared.js @@ -10040,7 +10040,7 @@ var process=require("__browserify_process");(function() { content = require('./content.coffee'); - XP = 15; + XP = 7.5; HP = 2; @@ -10556,7 +10556,8 @@ var process=require("__browserify_process");(function() { }, update: function(req, cb) { _.each(req.body, function(v, k) { - return user.fns.dotSet(k, v); + user.fns.dotSet(k, v); + return true; }); return typeof cb === "function" ? cb(null, req) : void 0; }, @@ -10782,7 +10783,8 @@ var process=require("__browserify_process");(function() { } if (fullSet) { _.each(path.split(","), function(p) { - return user.fns.dotSet("purchased." + p, true); + user.fns.dotSet("purchased." + p, true); + return true; }); } else { if (user.fns.dotGet("purchased." + path) === true) { @@ -10816,8 +10818,9 @@ var process=require("__browserify_process");(function() { }); user.items.gear.equipped[type] = foundKey ? foundKey : type === "weapon" ? "weapon_" + klass + "_0" : type === "shield" && klass === "rogue" ? "shield_rogue_0" : "" + type + "_base_0"; if (type === "weapon" || (type === "shield" && klass === "rogue")) { - return user.items.gear.owned["" + type + "_" + klass + "_0"] = true; + user.items.gear.owned["" + type + "_" + klass + "_0"] = true; } + return true; }); } else { _.merge(user.stats, { @@ -10843,14 +10846,26 @@ var process=require("__browserify_process");(function() { return typeof cb === "function" ? cb(null, req) : void 0; }, score: function(req, cb) { - var addPoints, calculateDelta, cron, delta, direction, exp, gp, hp, id, lvl, num, priority, streak, subtractPoints, task, times, type, value, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + var addPoints, calculateDelta, delta, direction, id, num, options, stats, subtractPoints, task, _ref; _ref = req.params, id = _ref.id, direction = _ref.direction; task = user.tasks[id]; + options = req.query || {}; + _.defaults(options, { + times: 1, + cron: false + }); user._tmp = {}; - _ref1 = [+user.stats.gp, +user.stats.hp, +user.stats.exp, ~~user.stats.lvl], gp = _ref1[0], hp = _ref1[1], exp = _ref1[2], lvl = _ref1[3]; - _ref2 = [task.type, +task.value, ~~task.streak, +task.priority || 1], type = _ref2[0], value = _ref2[1], streak = _ref2[2], priority = _ref2[3]; - _ref5 = [((_ref3 = req.query) != null ? _ref3.times : void 0) || 1, ((_ref4 = req.query) != null ? _ref4.cron : void 0) || false], times = _ref5[0], cron = _ref5[1]; - if (task.value > user.stats.gp && task.type === 'reward') { + stats = { + gp: +user.stats.gp, + hp: +user.stats.hp, + exp: +user.stats.exp + }; + task.value = +task.value; + task.streak = ~~task.streak; + if (task.priority == null) { + task.priority = 1; + } + if (task.value > stats.gp && task.type === 'reward') { return cb('Not enough Gold'); } delta = 0; @@ -10858,33 +10873,33 @@ var process=require("__browserify_process");(function() { if (adjustvalue == null) { adjustvalue = true; } - return _.times(times, function() { - var nextDelta; - nextDelta = user.fns.taskDeltaFormula(value, direction); + return _.times(options.times, function() { + var currVal, nextDelta; + currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value; + nextDelta = Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); if (adjustvalue) { - value += nextDelta; + task.value += nextDelta; + if (direction === 'up' && task.type !== 'reward' && !(task.type === 'habit' && !task.down)) { + task.value += user._statsComputed.str * .25; + } } return delta += nextDelta; }); }; addPoints = function() { - var weaponStr; - weaponStr = user.fns.getItem('weapon').str; - exp += user.fns.expModifier(delta, weaponStr, user.stats.lvl, priority) / 2; - if (streak) { - return gp += user.fns.gpModifier(delta, 1, priority, streak); - } else { - return gp += user.fns.gpModifier(delta, 1, priority); - } + var afterStreak, gpMod, intMod, streakBonus; + intMod = 1 + (user._statsComputed.int / 100); + stats.exp += Math.round(delta * XP * intMod * task.priority); + gpMod = delta * task.priority; + gpMod *= user._statsComputed.per * .3; + return stats.gp += task.streak ? (streakBonus = task.streak / 100 + 1, afterStreak = gpMod * streakBonus, gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0, afterStreak) : gpMod; }; subtractPoints = function() { - var armorDef, headDef, shieldDef; - armorDef = user.fns.getItem('armor').con; - headDef = user.fns.getItem('head').con; - shieldDef = user.fns.getItem('shield').con; - return hp += user.fns.hpModifier(delta, armorDef, headDef, shieldDef, user.stats.lvl, priority); + var conMod; + conMod = 1 - (user._statsComputed.con / 100); + return stats.hp += Math.round(task.value * HP * conMod * task.priority); }; - switch (type) { + switch (task.type) { case 'habit': calculateDelta(); if (delta > 0) { @@ -10892,15 +10907,25 @@ var process=require("__browserify_process");(function() { } else { subtractPoints(); } - if (task.value !== value) { - (task.history != null ? task.history : task.history = []).push({ + if (task.history == null) { + task.history = []; + } + if (moment(task.history[task.history.length - 1].date).isSame(new Date, 'day')) { + task.history[task.history.length - 1].value = task.value; + } else { + task.history.push({ date: +(new Date), - value: value + value: task.value }); } + if (typeof user.markModified === "function") { + user.markModified("habits." + (_.findIndex(user.habits, { + id: task.id + })) + ".history"); + } break; case 'daily': - if (cron) { + if (options.cron) { calculateDelta(); subtractPoints(); task.streak = 0; @@ -10908,21 +10933,20 @@ var process=require("__browserify_process");(function() { calculateDelta(); addPoints(); if (direction === 'up') { - streak = streak ? streak + 1 : 1; - if ((streak % 21) === 0) { + task.streak = task.streak ? task.streak + 1 : 1; + if ((task.streak % 21) === 0) { user.achievements.streak = user.achievements.streak ? user.achievements.streak + 1 : 1; } } else { - if ((streak % 21) === 0) { + if ((task.streak % 21) === 0) { user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0; } - streak = streak ? streak - 1 : 0; + task.streak = task.streak ? task.streak - 1 : 0; } - task.streak = streak; } break; case 'todo': - if (cron) { + if (options.cron) { calculateDelta(); } else { calculateDelta(); @@ -10931,25 +10955,21 @@ var process=require("__browserify_process");(function() { break; case 'reward': calculateDelta(false); - gp -= Math.abs(task.value); + stats.gp -= Math.abs(task.value); num = parseFloat(task.value).toFixed(2); - if (gp < 0) { - hp += gp; - gp = 0; + if (stats.gp < 0) { + stats.hp += stats.gp; + stats.gp = 0; } } - task.value = value; - updateStats(user, { - hp: hp, - exp: exp, - gp: gp - }); + user.fns.updateStats(stats); if (typeof window === 'undefined') { if (direction === 'up') { - randomDrop(user, { + user.fns.randomDrop({ + task: task, delta: delta, - priority: priority, - streak: streak + priority: task.priority, + streak: task.streak }); } } @@ -10977,8 +10997,9 @@ var process=require("__browserify_process");(function() { return !user.items.gear.owned[item.key]; }); if (found) { - return changes.push(found); + changes.push(found); } + return true; }); _.defaults(changes, _.transform(_.where(content.gear.flat, { klass: 'special' @@ -11086,152 +11107,57 @@ var process=require("__browserify_process");(function() { return user.items.lastDrop.count++; } }, - /* - 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 - */ - - expModifier: function(value, weaponStr, level, priority) { - var exp, str, strMod, totalStr; - if (priority == null) { - priority = 1; - } - str = (level - 1) / 2; - totalStr = (str + weaponStr) / 100; - strMod = 1 + totalStr; - exp = value * XP * strMod * 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 - */ - - hpModifier: function(value, armorDef, helmDef, shieldDef, level, priority) { - var def, defMod, hp, totalDef; - if (priority == null) { - priority = 1; - } - def = (level - 1) / 2; - totalDef = (def + armorDef + helmDef + shieldDef) / 100; - defMod = 1 - totalDef; - hp = value * HP * defMod * priority; - return Math.round(hp * 10) / 10; - }, - /* - Future use - {priority} user-defined priority multiplier - */ - - gpModifier: function(value, modifier, priority, streak) { - var afterStreak, streakBonus, val; - if (priority == null) { - priority = 1; - } - val = value * modifier * priority; - if (streak && user) { - streakBonus = streak / 100 + 1; - afterStreak = val * streakBonus; - if (val > 0) { - user._tmp.streakBonus = afterStreak - val; - } - 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 - */ - - taskDeltaFormula: function(currentValue, direction) { - var delta; - if (currentValue < -47.27) { - currentValue = -47.27; - } else if (currentValue > 21.27) { - currentValue = 21.27; - } - delta = Math.pow(0.9747, currentValue); - if (direction === 'up') { - return delta; - } - 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: function(newStats) { - var gp, tnl; - if (user.stats.hp <= 0) { - return; + updateStats: function(stats) { + var tnl; + if (stats.hp <= 0) { + return user.stats.hp = 0; } - if (newStats.hp != null) { - if (newStats.hp <= 0) { - user.stats.hp = 0; - return; - } else { - user.stats.hp = newStats.hp; - } - } - if (newStats.exp != null) { - tnl = api.tnl(user.stats.lvl); - if (user.stats.lvl >= 100) { - newStats.gp += newStats.exp / 15; - newStats.exp = 0; - user.stats.lvl = 100; - } else { - if (newStats.exp >= tnl) { - user.stats.exp = newStats.exp; - while (newStats.exp >= tnl && user.stats.lvl < 100) { - newStats.exp -= tnl; - user.stats.lvl++; - tnl = api.tnl(user.stats.lvl); - } - if (user.stats.lvl === 100) { - newStats.exp = 0; - } - user.stats.hp = 50; + user.stats.hp = stats.hp; + user.stats.gp = stats.gp >= 0 ? stats.gp : 0; + tnl = api.tnl(user.stats.lvl); + if (user.stats.lvl >= 100) { + stats.gp += stats.exp / 15; + stats.exp = 0; + user.stats.lvl = 100; + } else { + if (stats.exp >= tnl) { + user.stats.exp = stats.exp; + while (stats.exp >= tnl && user.stats.lvl < 100) { + stats.exp -= tnl; + user.stats.lvl++; + tnl = api.tnl(user.stats.lvl); } - } - user.stats.exp = newStats.exp; - if (user.flags == null) { - user.flags = {}; - } - if (!user.flags.customizationsNotification && (user.stats.exp > 10 || user.stats.lvl > 1)) { - user.flags.customizationsNotification = true; - } - if (!user.flags.itemsEnabled && user.stats.lvl >= 2) { - user.flags.itemsEnabled = true; - } - if (!user.flags.partyEnabled && user.stats.lvl >= 3) { - user.flags.partyEnabled = true; - } - if (!user.flags.dropsEnabled && user.stats.lvl >= 4) { - user.flags.dropsEnabled = true; - user.items.eggs["Wolf"] = 1; - } - if (!user.flags.classSelected && user.stats.lvl >= 5) { - user.flags.classSelected; + if (user.stats.lvl === 100) { + stats.exp = 0; + } + user.stats.hp = 50; } } - if (newStats.gp != null) { - if ((typeof gp === "undefined" || gp === null) || gp < 0) { - gp = 0.0; - } - return user.stats.gp = newStats.gp; + user.stats.exp = stats.exp; + if (user.flags == null) { + user.flags = {}; + } + if (!user.flags.customizationsNotification && (user.stats.exp > 10 || user.stats.lvl > 1)) { + user.flags.customizationsNotification = true; + } + if (!user.flags.itemsEnabled && user.stats.lvl >= 2) { + user.flags.itemsEnabled = true; + } + if (!user.flags.partyEnabled && user.stats.lvl >= 3) { + user.flags.partyEnabled = true; + } + if (!user.flags.dropsEnabled && user.stats.lvl >= 4) { + user.flags.dropsEnabled = true; + user.items.eggs["Wolf"] = 1; + } + if (!user.flags.classSelected && user.stats.lvl >= 5) { + return user.flags.classSelected; } }, /* @@ -11339,6 +11265,12 @@ var process=require("__browserify_process");(function() { value: expTally }); user.fns.preenUserHistory(); + if (typeof user.markModified === "function") { + user.markModified('history'); + } + if (typeof user.markModified === "function") { + user.markModified('dailys'); + } return user; }, preenUserHistory: function(minHistLen) { @@ -11373,6 +11305,7 @@ var process=require("__browserify_process");(function() { val = user.fns.dotGet(path); return m2 + (~path.indexOf('items.gear') ? (+((_ref = content.gear.flat[val]) != null ? _ref[stat] : void 0) || 0) * (~(val != null ? val.indexOf(user.stats["class"]) : void 0) ? 1.5 : 1) : +val[stat] || 0); }, 0); + m[stat] += (user.stats.lvl - 1) / 2; return m; }, {}); } diff --git a/script/index.coffee b/script/index.coffee index 57473281f3..1e45790c5f 100644 --- a/script/index.coffee +++ b/script/index.coffee @@ -2,7 +2,7 @@ moment = require('moment') _ = require('lodash') content = require('./content.coffee') -XP = 15 +XP = 7.5 # originally 15, users were complaining that they gained exp too fast HP = 2 api = module.exports = {} @@ -342,7 +342,7 @@ api.wrap = (user) -> update: (req, cb) -> _.each req.body, (v,k) -> - user.fns.dotSet(k,v) + user.fns.dotSet(k,v);true cb? null, req sortTask: (req, cb) -> @@ -478,7 +478,7 @@ api.wrap = (user) -> return cb?({code:401, err: "Not enough gems"}) if user.balance < cost if fullSet _.each path.split(","), (p) -> - user.fns.dotSet "purchased." + p, true + user.fns.dotSet("purchased." + p, true);true else if user.fns.dotGet("purchased." + path) is true user.preferences[path.split(".")[0]] = path.split(".")[1] @@ -512,6 +512,7 @@ api.wrap = (user) -> # Grant them their new class's gear user.items.gear.owned["#{type}_#{klass}_0"] = true if type is "weapon" or (type is "shield" and klass is "rogue") + true else # Null ?class value means "reset class" _.merge user.stats, {str: 0, def: 0, per: 0, int: 0} @@ -527,58 +528,83 @@ api.wrap = (user) -> user.stats.mp++ if stat is 'int' #increase their MP along with their max MP cb? null, req - score: (req, cb) -> {id, direction} = req.params # up or down task = user.tasks[id] + options = req.query or {}; _.defaults(options, {times:1, cron:false}) # This is for setting one-time temporary flags, such as streakBonus or itemDropped. Useful for notifying # the API consumer, then cleared afterwards user._tmp = {} - [gp, hp, exp, lvl] = [+user.stats.gp, +user.stats.hp, +user.stats.exp, ~~user.stats.lvl] - [type, value, streak, priority] = [task.type, +task.value, ~~task.streak, +task.priority or 1] - [times, cron] = [req.query?.times or 1, req.query?.cron or false] + # TODO do we need this fail-safe casting anymore? Are we safe now we're off Derby? + stats = {gp: +user.stats.gp, hp: +user.stats.hp, exp: +user.stats.exp} + task.value = +task.value; task.streak = ~~task.streak; task.priority ?= 1 # If they're trying to purhcase a too-expensive reward, don't allow them to do that. - if task.value > user.stats.gp and task.type is 'reward' + if task.value > stats.gp and task.type is 'reward' return cb('Not enough Gold'); delta = 0 - calculateDelta = (adjustvalue = true) -> + + calculateDelta = (adjustvalue=true) -> # If multiple days have passed, multiply times days missed - _.times times, -> - # 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 = user.fns.taskDeltaFormula(value, direction) - value += nextDelta if adjustvalue + _.times options.times, -> + # Each iteration calculate the nextDelta, which is then accumulated in the total delta. + # Calculates the next task.value based on direction + # Uses a capped inverse log y=.95^x, y>= -5 + + # Min/max on task redness + currVal = + if task.value < -47.27 then -47.27 + else if task.value > 21.27 then 21.27 + else task.value + nextDelta = Math.pow(0.9747, currVal) * (if direction is 'down' then -1 else 1) + if adjustvalue + task.value += nextDelta + # Factor in STR. Only for up-scoring, ignore up-onlies and rewards + if direction is 'up' and task.type != 'reward' and !(task.type is 'habit' and !task.down) + task.value += user._statsComputed.str * .25 delta += nextDelta addPoints = -> - weaponStr = user.fns.getItem('weapon').str - exp += user.fns.expModifier(delta, weaponStr, user.stats.lvl, priority) / 2 # /2 hack for now, people leveling too fast - if streak - gp += user.fns.gpModifier(delta, 1, priority, streak) - else - gp += user.fns.gpModifier(delta, 1, priority) + # Exp Modifier + intMod = 1 + (user._statsComputed.int / 100) + stats.exp += Math.round(delta * XP * intMod * task.priority) + # GP modifier + gpMod = delta * task.priority + gpMod *= (user._statsComputed.per *.3) # Factor in PER + stats.gp += + if task.streak + streakBonus = task.streak / 100 + 1 # eg, 1-day streak is 1.1, 2-day is 1.2, etc + afterStreak = gpMod * streakBonus + user._tmp.streakBonus = afterStreak - gpMod if (gpMod > 0) # keep this on-hand for later, so we can notify streak-bonus + afterStreak + else gpMod + + # HP modifier subtractPoints = -> - armorDef = user.fns.getItem('armor').con - headDef = user.fns.getItem('head').con - shieldDef = user.fns.getItem('shield').con - hp += user.fns.hpModifier(delta, armorDef, headDef, shieldDef, user.stats.lvl, priority) + conMod = 1 - (user._statsComputed.con / 100) + stats.hp += Math.round(task.value * HP * conMod * task.priority) + # round to 1dp - switch type + switch task.type when 'habit' calculateDelta() # Add habit value to habit-history (if different) if (delta > 0) then addPoints() else subtractPoints() - if task.value != value - (task.history ?= []).push { date: +new Date, value: value } + + # History + task.history ?= [] + if moment(task.history[task.history.length-1].date).isSame(new Date, 'day') + task.history[task.history.length-1].value = task.value + else + task.history.push {date: +new Date, value: task.value} + user.markModified? "habits.#{_.findIndex(user.habits, {id:task.id})}.history" when 'daily' - if cron + if options.cron calculateDelta() subtractPoints() task.streak = 0 @@ -586,22 +612,18 @@ api.wrap = (user) -> calculateDelta() 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 - + task.streak = if task.streak then task.streak + 1 else 1 # Give a streak achievement when the streak is a multiple of 21 - if (streak % 21) is 0 + if (task.streak % 21) is 0 user.achievements.streak = if user.achievements.streak then user.achievements.streak + 1 else 1 - else # Remove a streak achievement if streak was a multiple of 21 and the daily was undone - if (streak % 21) is 0 + if (task.streak % 21) is 0 user.achievements.streak = if user.achievements.streak then user.achievements.streak - 1 else 0 - - streak = if streak then streak - 1 else 0 - task.streak = streak + task.streak = if task.streak then task.streak - 1 else 0 when 'todo' - if cron + if options.cron calculateDelta() #don't touch stats on cron else @@ -612,20 +634,19 @@ api.wrap = (user) -> # Don't adjust values for rewards calculateDelta(false) # purchase item - gp -= Math.abs(task.value) + stats.gp -= Math.abs(task.value) num = parseFloat(task.value).toFixed(2) # if too expensive, reduce health & zero gp - if gp < 0 - hp += gp + if stats.gp < 0 # hp - gp difference - gp = 0 + stats.hp += stats.gp + stats.gp = 0 - task.value = value - updateStats user, { hp, exp, gp } + user.fns.updateStats stats # Drop system (don't run on the client, as it would only be discarded since ops are sent to the API, not the results) if typeof window is 'undefined' - randomDrop(user, {delta, priority, streak}) if direction is 'up' + user.fns.randomDrop({task, delta, priority:task.priority, streak:task.streak}) if direction is 'up' cb? null, req return delta @@ -635,6 +656,7 @@ api.wrap = (user) -> # ---------------------------------------------------------------------- user.fns = + getItem: (type) -> item = content.gear.flat[user.items.gear.equipped[type]] return content.gear.flat["#{type}_base_0"] unless item @@ -646,6 +668,7 @@ api.wrap = (user) -> found = _.find content.gear.tree[type][user.stats.class], (item) -> !user.items.gear.owned[item.key] changes.push(found) if found + true # Add special items (contrib gear, backer gear, etc) _.defaults changes, _.transform _.where(content.gear.flat, {klass:'special'}), (m,v) -> m.push v if v.canOwn?(user) && !user.items.gear.owned[v.key] @@ -756,125 +779,51 @@ api.wrap = (user) -> user.items.lastDrop.date = +new Date user.items.lastDrop.count++ - ### - 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 - ### - expModifier: (value, weaponStr, level, priority=1) -> - str = (level - 1) / 2 - # ultimately get this from user - totalStr = (str + weaponStr) / 100 - strMod = 1 + totalStr - exp = value * XP * strMod * 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 - ### - hpModifier: (value, armorDef, helmDef, shieldDef, level, priority=1) -> - 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 * priority - return Math.round(hp * 10) / 10 - # round to 1dp - - ### - Future use - {priority} user-defined priority multiplier - ### - gpModifier: (value, modifier, priority=1, streak) -> - val = value * modifier * priority - if streak and user - streakBonus = streak / 100 + 1 # eg, 1-day streak is 1.1, 2-day is 1.2, etc - afterStreak = val * streakBonus - user._tmp.streakBonus = afterStreak - val if (val > 0) # keep this on-hand for later, so we can notify streak-bonus - 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 - ### - 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 - ### 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: (newStats) -> - # if user is dead, dont do anything - return if user.stats.hp <= 0 + updateStats: (stats) -> + # Game Over + return user.stats.hp=0 if stats.hp <= 0 - if newStats.hp? - # Game Over - if newStats.hp <= 0 - user.stats.hp = 0 - return - else - user.stats.hp = newStats.hp + user.stats.hp = stats.hp + user.stats.gp = if stats.gp >= 0 then stats.gp else 0 - if newStats.exp? - tnl = api.tnl(user.stats.lvl) - #silent = false - # if we're at level 100, turn xp to gold - if user.stats.lvl >= 100 - newStats.gp += newStats.exp / 15 - newStats.exp = 0 - user.stats.lvl = 100 - else - # level up & carry-over exp - if newStats.exp >= tnl - #silent = true # push through the negative xp silently - user.stats.exp = newStats.exp # push normal + notification - while newStats.exp >= tnl and user.stats.lvl < 100 # keep levelling up - newStats.exp -= tnl - user.stats.lvl++ - tnl = api.tnl(user.stats.lvl) - if user.stats.lvl == 100 - newStats.exp = 0 - user.stats.hp = 50 - - user.stats.exp = newStats.exp - - # Set flags when they unlock features - user.flags ?= {} - if !user.flags.customizationsNotification and (user.stats.exp > 10 or user.stats.lvl > 1) - user.flags.customizationsNotification = true - if !user.flags.itemsEnabled and user.stats.lvl >= 2 - user.flags.itemsEnabled = true - if !user.flags.partyEnabled and user.stats.lvl >= 3 - user.flags.partyEnabled = true - if !user.flags.dropsEnabled and user.stats.lvl >= 4 - user.flags.dropsEnabled = true - user.items.eggs["Wolf"] = 1 - if !user.flags.classSelected and user.stats.lvl >= 5 - user.flags.classSelected - - 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) - user.stats.gp = newStats.gp + tnl = api.tnl(user.stats.lvl) + # if we're at level 100, turn xp to gold + if user.stats.lvl >= 100 + stats.gp += stats.exp / 15 + stats.exp = 0 + user.stats.lvl = 100 + else + # level up & carry-over exp + if stats.exp >= tnl + #silent = true # push through the negative xp silently + user.stats.exp = stats.exp # push normal + notification + while stats.exp >= tnl and user.stats.lvl < 100 # keep levelling up + stats.exp -= tnl + user.stats.lvl++ + tnl = api.tnl(user.stats.lvl) + if user.stats.lvl == 100 + stats.exp = 0 + user.stats.hp = 50 + user.stats.exp = stats.exp + # Set flags when they unlock features + user.flags ?= {} + if !user.flags.customizationsNotification and (user.stats.exp > 10 or user.stats.lvl > 1) + user.flags.customizationsNotification = true + if !user.flags.itemsEnabled and user.stats.lvl >= 2 + user.flags.itemsEnabled = true + if !user.flags.partyEnabled and user.stats.lvl >= 3 + user.flags.partyEnabled = true + if !user.flags.dropsEnabled and user.stats.lvl >= 4 + user.flags.dropsEnabled = true + user.items.eggs["Wolf"] = 1 + if !user.flags.classSelected and user.stats.lvl >= 5 + user.flags.classSelected ### ------------------------------------------------------ @@ -963,6 +912,8 @@ api.wrap = (user) -> expTally += api.tnl(lvl) (user.history.exp ?= []).push { date: now, value: expTally } user.fns.preenUserHistory() + user.markModified? 'history' + user.markModified? 'dailys' # covers dailys.*.history user # Registered users with some history @@ -974,9 +925,9 @@ api.wrap = (user) -> _.defaults user.history, {todos:[], exp: []} user.history.exp = preenHistory(user.history.exp) if user.history.exp.length > minHistLen user.history.todos = preenHistory(user.history.todos) if user.history.todos.length > minHistLen - #user.markModified('history') - #user.markModified('habits') - #user.markModified('dailys') + #user.markModified? 'history' + #user.markModified? 'habits' + #user.markModified? 'dailys' # ---------------------------------------------------------------------- # Virtual Attributes @@ -994,7 +945,9 @@ api.wrap = (user) -> (+content.gear.flat[val]?[stat] or 0) * (if ~val?.indexOf(user.stats.class) then 1.5 else 1) else +val[stat] or 0 - , 0); m + , 0) + m[stat] += (user.stats.lvl - 1) / 2 + m , {}) Object.defineProperty user, 'tasks', get: ->