diff --git a/.eslintrc b/.eslintrc index 7613632120..870626d394 100644 --- a/.eslintrc +++ b/.eslintrc @@ -32,7 +32,6 @@ "no-octal": 2, "no-process-env": 2, "no-proto": 2, - "no-implied-eval": 2, "yoda": 2, "wrap-iife": 2, "radix": 2, @@ -59,7 +58,6 @@ "no-path-concat": 2, "arrow-spacing": 2, "constructor-super": 2, - "generator-star-spacing": 2, "no-arrow-condition": 2, "no-class-assign": 2, "no-const-assign": 2, diff --git a/common/script/index.js b/common/script/index.js index fb7d17df44..89cc2f2ceb 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -12,6 +12,8 @@ import { } from './constants'; import * as statHelpers from './statHelpers'; +import importedLibs from './libs'; + var $w, preenHistory, sortOrder, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -29,9 +31,7 @@ api.maxHealth = MAX_HEALTH; api.tnl = statHelpers.toNextLevel; api.diminishingReturns = statHelpers.diminishingReturns; -$w = api.$w = function(s) { - return s.split(' '); -}; +$w = api.$w = importedLibs.splitWhitespace; api.dotSet = function(obj, path, val) { var arr; @@ -55,27 +55,9 @@ api.dotGet = function(obj, path) { }; -/* - Reflists are arrays, but stored as objects. Mongoose has a helluvatime working with arrays (the main problem for our - syncing issues) - so the goal is to move away from arrays to objects, since mongoose can reference elements by ID - no problem. To maintain sorting, we use these helper functions: - */ +api.refPush = importedLibs.refPush; -api.refPush = function(reflist, item, prune) { - if (prune == null) { - prune = 0; - } - item.sort = _.isEmpty(reflist) ? 0 : _.max(reflist, 'sort').sort + 1; - if (!(item.id && !reflist[item.id])) { - item.id = api.uuid(); - } - return reflist[item.id] = item; -}; - -api.planGemLimits = { - convRate: 20, - convCap: 25 -}; +api.planGemLimits = importedLibs.planGemLimits; /* Preen history for users with > 7 history entries @@ -122,13 +104,7 @@ preenHistory = function(history) { Preen 3-day past-completed To-Dos from Angular & mobile app */ -api.preenTodos = function(tasks) { - return _.filter(tasks, function(t) { - return !t.completed || (t.challenge && t.challenge.id) || moment(t.dateCompleted).isAfter(moment().subtract({ - days: 3 - })); - }); -}; +api.preenTodos = importedLibs.preenTodos; /* Update the in-browser store with new gear. FIXME this was in user.fns, but it was causing strange issues there @@ -180,14 +156,7 @@ Misc Helpers ------------------------------------------------------ */ -api.uuid = function() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { - var r, v; - r = Math.random() * 16 | 0; - v = (c === "x" ? r : r & 0x3 | 0x8); - return v.toString(16); - }); -}; +api.uuid = importedLibs.uuid; api.countExists = function(items) { return _.reduce(items, (function(m, v) { @@ -195,73 +164,7 @@ api.countExists = function(items) { }), 0); }; - -/* -Even though Mongoose handles task defaults, we want to make sure defaults are set on the client-side before -sending up to the server for performance - */ - -api.taskDefaults = function(task) { - var defaults, ref, ref1, ref2; - if (task == null) { - task = {}; - } - if (!(task.type && ((ref = task.type) === 'habit' || ref === 'daily' || ref === 'todo' || ref === 'reward'))) { - task.type = 'habit'; - } - defaults = { - id: api.uuid(), - text: task.id != null ? task.id : '', - notes: '', - priority: 1, - challenge: {}, - attribute: 'str', - dateCreated: new Date() - }; - _.defaults(task, defaults); - if (task.type === 'habit') { - _.defaults(task, { - up: true, - down: true - }); - } - if ((ref1 = task.type) === 'habit' || ref1 === 'daily') { - _.defaults(task, { - history: [] - }); - } - if ((ref2 = task.type) === 'daily' || ref2 === 'todo') { - _.defaults(task, { - completed: false - }); - } - if (task.type === 'daily') { - _.defaults(task, { - streak: 0, - repeat: { - su: true, - m: true, - t: true, - w: true, - th: true, - f: true, - s: true - } - }, { - startDate: new Date(), - everyX: 1, - frequency: 'weekly' - }); - } - task._id = task.id; - if (task.value == null) { - task.value = task.type === 'reward' ? 10 : 0; - } - if (!_.isNumber(task.priority)) { - task.priority = 1; - } - return task; -}; +api.taskDefaults = importedLibs.taskDefaults; api.percent = function(x, y, dir) { var roundFn; @@ -522,1533 +425,51 @@ api.wrap = function(user, main) { user._wrapped = true; if (main) { user.ops = { - update: function(req, cb) { - _.each(req.body, function(v, k) { - user.fns.dotSet(k, v); - return true; - }); - return typeof cb === "function" ? cb(null, user) : void 0; - }, - sleep: function(req, cb) { - user.preferences.sleep = !user.preferences.sleep; - return typeof cb === "function" ? cb(null, {}) : void 0; - }, - revive: function(req, cb, analytics) { - var analyticsData, base, cl, gearOwned, item, losableItems, lostItem, lostStat; - if (!(user.stats.hp <= 0)) { - return typeof cb === "function" ? cb({ - code: 400, - message: "Cannot revive if not dead" - }) : void 0; - } - _.merge(user.stats, { - hp: 50, - exp: 0, - gp: 0 - }); - if (user.stats.lvl > 1) { - user.stats.lvl--; - } - lostStat = user.fns.randomVal(_.reduce(['str', 'con', 'per', 'int'], (function(m, k) { - if (user.stats[k]) { - m[k] = k; - } - return m; - }), {})); - if (lostStat) { - user.stats[lostStat]--; - } - cl = user.stats["class"]; - gearOwned = (typeof (base = user.items.gear.owned).toObject === "function" ? base.toObject() : void 0) || user.items.gear.owned; - losableItems = {}; - _.each(gearOwned, function(v, k) { - var itm; - if (v) { - itm = content.gear.flat['' + k]; - if (itm) { - if ((itm.value > 0 || k === 'weapon_warrior_0') && (itm.klass === cl || (itm.klass === 'special' && (!itm.specialClass || itm.specialClass === cl)) || itm.klass === 'armoire')) { - return losableItems['' + k] = '' + k; - } - } - } - }); - lostItem = user.fns.randomVal(losableItems); - if (item = content.gear.flat[lostItem]) { - user.items.gear.owned[lostItem] = false; - if (user.items.gear.equipped[item.type] === lostItem) { - user.items.gear.equipped[item.type] = item.type + "_base_0"; - } - if (user.items.gear.costume[item.type] === lostItem) { - user.items.gear.costume[item.type] = item.type + "_base_0"; - } - } - if (typeof user.markModified === "function") { - user.markModified('items.gear'); - } - analyticsData = { - uuid: user._id, - lostItem: lostItem, - gaLabel: lostItem, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('Death', analyticsData); - } - return typeof cb === "function" ? cb((item ? { - code: 200, - message: i18n.t('messageLostItem', { - itemText: item.text(req.language) - }, req.language) - } : null), user) : void 0; - }, - reset: function(req, cb) { - var gear; - user.habits = []; - user.dailys = []; - user.todos = []; - user.rewards = []; - user.stats.hp = 50; - user.stats.lvl = 1; - user.stats.gp = 0; - user.stats.exp = 0; - gear = user.items.gear; - _.each(['equipped', 'costume'], function(type) { - gear[type].armor = 'armor_base_0'; - gear[type].weapon = 'weapon_base_0'; - gear[type].head = 'head_base_0'; - return gear[type].shield = 'shield_base_0'; - }); - if (typeof gear.owned === 'undefined') { - gear.owned = {}; - } - _.each(gear.owned, function(v, k) { - if (gear.owned[k]) { - gear.owned[k] = false; - } - return true; - }); - gear.owned.weapon_warrior_0 = true; - if (typeof user.markModified === "function") { - user.markModified('items.gear.owned'); - } - user.preferences.costume = false; - return typeof cb === "function" ? cb(null, user) : void 0; - }, - reroll: function(req, cb, analytics) { - var analyticsData; - if (user.balance < 1) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } - user.balance--; - _.each(user.tasks, function(task) { - if (task.type !== 'reward') { - return task.value = 0; - } - }); - user.stats.hp = 50; - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 4, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('Fortify Potion', analyticsData); - } - return typeof cb === "function" ? cb(null, user) : void 0; - }, - rebirth: function(req, cb, analytics) { - var analyticsData, flags, gear, lvl, stats; - if (user.balance < 2 && user.stats.lvl < api.maxLevel) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } - analyticsData = { - uuid: user._id, - category: 'behavior' - }; - if (user.stats.lvl < api.maxLevel) { - user.balance -= 2; - analyticsData.acquireMethod = 'Gems'; - analyticsData.gemCost = 8; - } else { - analyticsData.gemCost = 0; - analyticsData.acquireMethod = '> 100'; - } - if (analytics != null) { - analytics.track('Rebirth', analyticsData); - } - lvl = api.capByLevel(user.stats.lvl); - _.each(user.tasks, function(task) { - if (task.type !== 'reward') { - task.value = 0; - } - if (task.type === 'daily') { - return task.streak = 0; - } - }); - stats = user.stats; - stats.buffs = {}; - stats.hp = 50; - stats.lvl = 1; - stats["class"] = 'warrior'; - _.each(['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'], function(value) { - return stats[value] = 0; - }); - // TODO during refactoring: move all gear code from rebirth() to its own function and then call it in reset() as well - gear = user.items.gear; - _.each(['equipped', 'costume'], function(type) { - gear[type] = {}; - gear[type].armor = 'armor_base_0'; - gear[type].weapon = 'weapon_warrior_0'; - gear[type].head = 'head_base_0'; - return gear[type].shield = 'shield_base_0'; - }); - if (user.items.currentPet) { - user.ops.equip({ - params: { - type: 'pet', - key: user.items.currentPet - } - }); - } - if (user.items.currentMount) { - user.ops.equip({ - params: { - type: 'mount', - key: user.items.currentMount - } - }); - } - _.each(gear.owned, function(v, k) { - if (gear.owned[k] && content.gear.flat[k].value) { - gear.owned[k] = false; - return true; - } - }); - gear.owned.weapon_warrior_0 = true; - if (typeof user.markModified === "function") { - user.markModified('items.gear.owned'); - } - user.preferences.costume = false; - flags = user.flags; - if (!user.achievements.beastMaster) { - flags.rebirthEnabled = false; - } - flags.itemsEnabled = false; - flags.dropsEnabled = false; - flags.classSelected = false; - flags.levelDrops = {}; - if (!user.achievements.rebirths) { - user.achievements.rebirths = 1; - user.achievements.rebirthLevel = lvl; - } else if (lvl > user.achievements.rebirthLevel || lvl === 100) { - user.achievements.rebirths++; - user.achievements.rebirthLevel = lvl; - } - user.stats.buffs = {}; - return typeof cb === "function" ? cb(null, user) : void 0; - }, - allocateNow: function(req, cb) { - _.times(user.stats.points, user.fns.autoAllocate); - user.stats.points = 0; - if (typeof user.markModified === "function") { - user.markModified('stats'); - } - return typeof cb === "function" ? cb(null, user.stats) : void 0; - }, - clearCompleted: function(req, cb) { - _.remove(user.todos, function(t) { - var ref; - return t.completed && !((ref = t.challenge) != null ? ref.id : void 0); - }); - if (typeof user.markModified === "function") { - user.markModified('todos'); - } - return typeof cb === "function" ? cb(null, user.todos) : void 0; - }, - sortTask: function(req, cb) { - var from, id, movedTask, preenedTasks, ref, task, tasks, to; - id = req.params.id; - ref = req.query, to = ref.to, from = ref.from; - task = user.tasks[id]; - if (!task) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTaskNotFound', req.language) - }) : void 0; - } - if (!((to != null) && (from != null))) { - return typeof cb === "function" ? cb('?to=__&from=__ are required') : void 0; - } - tasks = user[task.type + "s"]; - if (task.type === 'todo' && tasks[from] !== task) { - preenedTasks = api.preenTodos(tasks); - if (to !== -1) { - to = tasks.indexOf(preenedTasks[to]); - } - from = tasks.indexOf(preenedTasks[from]); - } - if (tasks[from] !== task) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTaskNotFound', req.language) - }) : void 0; - } - movedTask = tasks.splice(from, 1)[0]; - if (to === -1) { - tasks.push(movedTask); - } else { - tasks.splice(to, 0, movedTask); - } - return typeof cb === "function" ? cb(null, tasks) : void 0; - }, - updateTask: function(req, cb) { - var ref, task; - if (!(task = user.tasks[(ref = req.params) != null ? ref.id : void 0])) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTaskNotFound', req.language) - }) : void 0; - } - _.merge(task, _.omit(req.body, ['checklist', 'id', 'type'])); - if (req.body.checklist) { - task.checklist = req.body.checklist; - } - if (typeof task.markModified === "function") { - task.markModified('tags'); - } - return typeof cb === "function" ? cb(null, task) : void 0; - }, - deleteTask: function(req, cb) { - var i, ref, task; - task = user.tasks[(ref = req.params) != null ? ref.id : void 0]; - if (!task) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTaskNotFound', req.language) - }) : void 0; - } - i = user[task.type + "s"].indexOf(task); - if (~i) { - user[task.type + "s"].splice(i, 1); - } - return typeof cb === "function" ? cb(null, {}) : void 0; - }, - addTask: function(req, cb) { - var task; - task = api.taskDefaults(req.body); - if (user.tasks[task.id] != null) { - return typeof cb === "function" ? cb({ - code: 409, - message: i18n.t('messageDuplicateTaskID', req.language) - }) : void 0; - } - user[task.type + "s"].unshift(task); - if (user.preferences.newTaskEdit) { - task._editing = true; - } - if (user.preferences.tagsCollapsed) { - task._tags = true; - } - if (!user.preferences.advancedCollapsed) { - task._advanced = true; - } - if (typeof cb === "function") { - cb(null, task); - } - return task; - }, - addTag: function(req, cb) { - if (user.tags == null) { - user.tags = []; - } - user.tags.push({ - name: req.body.name, - id: req.body.id || api.uuid() - }); - return typeof cb === "function" ? cb(null, user.tags) : void 0; - }, - sortTag: function(req, cb) { - var from, ref, to; - ref = req.query, to = ref.to, from = ref.from; - if (!((to != null) && (from != null))) { - return typeof cb === "function" ? cb('?to=__&from=__ are required') : void 0; - } - user.tags.splice(to, 0, user.tags.splice(from, 1)[0]); - return typeof cb === "function" ? cb(null, user.tags) : void 0; - }, - getTags: function(req, cb) { - return typeof cb === "function" ? cb(null, user.tags) : void 0; - }, - getTag: function(req, cb) { - var i, tid; - tid = req.params.id; - i = _.findIndex(user.tags, { - id: tid - }); - if (!~i) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTagNotFound', req.language) - }) : void 0; - } - return typeof cb === "function" ? cb(null, user.tags[i]) : void 0; - }, - updateTag: function(req, cb) { - var i, tid; - tid = req.params.id; - i = _.findIndex(user.tags, { - id: tid - }); - if (!~i) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTagNotFound', req.language) - }) : void 0; - } - user.tags[i].name = req.body.name; - return typeof cb === "function" ? cb(null, user.tags[i]) : void 0; - }, - deleteTag: function(req, cb) { - var i, tag, tid; - tid = req.params.id; - i = _.findIndex(user.tags, { - id: tid - }); - if (!~i) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageTagNotFound', req.language) - }) : void 0; - } - tag = user.tags[i]; - delete user.filters[tag.id]; - user.tags.splice(i, 1); - _.each(user.tasks, function(task) { - return delete task.tags[tag.id]; - }); - _.each(['habits', 'dailys', 'todos', 'rewards'], function(type) { - return typeof user.markModified === "function" ? user.markModified(type) : void 0; - }); - return typeof cb === "function" ? cb(null, user.tags) : void 0; - }, - addWebhook: function(req, cb) { - var wh; - wh = user.preferences.webhooks; - api.refPush(wh, { - url: req.body.url, - enabled: req.body.enabled || true, - id: req.body.id - }); - if (typeof user.markModified === "function") { - user.markModified('preferences.webhooks'); - } - return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0; - }, - updateWebhook: function(req, cb) { - _.merge(user.preferences.webhooks[req.params.id], req.body); - if (typeof user.markModified === "function") { - user.markModified('preferences.webhooks'); - } - return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0; - }, - deleteWebhook: function(req, cb) { - delete user.preferences.webhooks[req.params.id]; - if (typeof user.markModified === "function") { - user.markModified('preferences.webhooks'); - } - return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0; - }, - addPushDevice: function(req, cb) { - var i, item, pd; - if (!user.pushDevices) { - user.pushDevices = []; - } - pd = user.pushDevices; - item = { - regId: req.body.regId, - type: req.body.type - }; - i = _.findIndex(pd, { - regId: item.regId - }); - if (i === -1) { - pd.push(item); - } - return typeof cb === "function" ? cb(null, user.pushDevices) : void 0; - }, - clearPMs: function(req, cb) { - user.inbox.messages = {}; - if (typeof user.markModified === "function") { - user.markModified('inbox.messages'); - } - return typeof cb === "function" ? cb(null, user.inbox.messages) : void 0; - }, - deletePM: function(req, cb) { - delete user.inbox.messages[req.params.id]; - if (typeof user.markModified === "function") { - user.markModified('inbox.messages.' + req.params.id); - } - return typeof cb === "function" ? cb(null, user.inbox.messages) : void 0; - }, - blockUser: function(req, cb) { - var i; - i = user.inbox.blocks.indexOf(req.params.uuid); - if (~i) { - user.inbox.blocks.splice(i, 1); - } else { - user.inbox.blocks.push(req.params.uuid); - } - if (typeof user.markModified === "function") { - user.markModified('inbox.blocks'); - } - return typeof cb === "function" ? cb(null, user.inbox.blocks) : void 0; - }, - feed: function(req, cb) { - var egg, eggText, evolve, food, message, pet, petDisplayName, potion, potionText, ref, ref1, ref2, userPets; - ref = req.params, pet = ref.pet, food = ref.food; - food = content.food[food]; - ref1 = pet.split('-'), egg = ref1[0], potion = ref1[1]; - userPets = user.items.pets; - potionText = content.hatchingPotions[potion] ? content.hatchingPotions[potion].text() : potion; - eggText = content.eggs[egg] ? content.eggs[egg].text() : egg; - petDisplayName = i18n.t('petName', { - potion: potionText, - egg: eggText - }); - if (!userPets[pet]) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messagePetNotFound', req.language) - }) : void 0; - } - if (!((ref2 = user.items.food) != null ? ref2[food.key] : void 0)) { - return typeof cb === "function" ? cb({ - code: 404, - message: i18n.t('messageFoodNotFound', req.language) - }) : void 0; - } - if (content.specialPets[pet]) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageCannotFeedPet', req.language) - }) : void 0; - } - if (user.items.mounts[pet]) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageAlreadyMount', req.language) - }) : void 0; - } - message = ''; - evolve = function() { - userPets[pet] = -1; - user.items.mounts[pet] = true; - if (pet === user.items.currentPet) { - user.items.currentPet = ""; - } - return message = i18n.t('messageEvolve', { - egg: petDisplayName - }, req.language); - }; - if (food.key === 'Saddle') { - evolve(); - } else { - if (food.target === potion || content.hatchingPotions[potion].premium) { - userPets[pet] += 5; - message = i18n.t('messageLikesFood', { - egg: petDisplayName, - foodText: food.text(req.language) - }, req.language); - } else { - userPets[pet] += 2; - message = i18n.t('messageDontEnjoyFood', { - egg: petDisplayName, - foodText: food.text(req.language) - }, req.language); - } - if (userPets[pet] >= 50 && !user.items.mounts[pet]) { - evolve(); - } - } - user.items.food[food.key]--; - return typeof cb === "function" ? cb({ - code: 200, - message: message - }, { - value: userPets[pet] - }) : void 0; - }, - buySpecialSpell: function(req, cb) { - var base, item, key, message; - key = req.params.key; - item = content.special[key]; - if (user.stats.gp < item.value) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageNotEnoughGold', req.language) - }) : void 0; - } - user.stats.gp -= item.value; - if ((base = user.items.special)[key] == null) { - base[key] = 0; - } - user.items.special[key]++; - if (typeof user.markModified === "function") { - user.markModified('items.special'); - } - message = i18n.t('messageBought', { - itemText: item.text(req.language) - }, req.language); - return typeof cb === "function" ? cb({ - code: 200, - message: message - }, _.pick(user, $w('items stats'))) : void 0; - }, - purchase: function(req, cb, analytics) { - var analyticsData, convCap, convRate, item, key, price, ref, ref1, ref2, ref3, type; - ref = req.params, type = ref.type, key = ref.key; - if (type === 'gems' && key === 'gem') { - ref1 = api.planGemLimits, convRate = ref1.convRate, convCap = ref1.convCap; - convCap += user.purchased.plan.consecutive.gemCapExtra; - if (!((ref2 = user.purchased) != null ? (ref3 = ref2.plan) != null ? ref3.customerId : void 0 : void 0)) { - return typeof cb === "function" ? cb({ - code: 401, - message: "Must subscribe to purchase gems with GP" - }, req) : void 0; - } - if (!(user.stats.gp >= convRate)) { - return typeof cb === "function" ? cb({ - code: 401, - message: "Not enough Gold" - }) : void 0; - } - if (user.purchased.plan.gemsBought >= convCap) { - return typeof cb === "function" ? cb({ - code: 401, - message: "You've reached the Gold=>Gem conversion cap (" + convCap + ") for this month. We have this to prevent abuse / farming. The cap will reset within the first three days of next month." - }) : void 0; - } - user.balance += .25; - user.purchased.plan.gemsBought++; - user.stats.gp -= convRate; - analyticsData = { - uuid: user._id, - itemKey: key, - acquireMethod: 'Gold', - goldCost: convRate, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('purchase gems', analyticsData); - } - return typeof cb === "function" ? cb({ - code: 200, - message: "+1 Gem" - }, _.pick(user, $w('stats balance'))) : void 0; - } - if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food' && type !== 'quests' && type !== 'gear') { - return typeof cb === "function" ? cb({ - code: 404, - message: ":type must be in [eggs,hatchingPotions,food,quests,gear]" - }, req) : void 0; - } - if (type === 'gear') { - item = content.gear.flat[key]; - if (user.items.gear.owned[key]) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('alreadyHave', req.language) - }) : void 0; - } - price = (item.twoHanded || item.gearSet === 'animal' ? 2 : 1) / 4; - } else { - item = content[type][key]; - price = item.value / 4; - } - if (!item) { - return typeof cb === "function" ? cb({ - code: 404, - message: ":key not found for Content." + type - }, req) : void 0; - } - if (!item.canBuy(user)) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('messageNotAvailable', req.language) - }) : void 0; - } - if ((user.balance < price) || !user.balance) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } - user.balance -= price; - if (type === 'gear') { - user.items.gear.owned[key] = true; - } else { - if (!(user.items[type][key] > 0)) { - user.items[type][key] = 0; - } - user.items[type][key]++; - } - analyticsData = { - uuid: user._id, - itemKey: key, - itemType: 'Market', - acquireMethod: 'Gems', - gemCost: item.value, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('acquire item', analyticsData); - } - return typeof cb === "function" ? cb(null, _.pick(user, $w('items balance'))) : void 0; - }, - releasePets: function(req, cb, analytics) { - var analyticsData, pet; - if (user.balance < 1) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - user.balance -= 1; - for (pet in content.pets) { - user.items.pets[pet] = 0; - } - if (!user.achievements.beastMasterCount) { - user.achievements.beastMasterCount = 0; - } - user.achievements.beastMasterCount++; - user.items.currentPet = ""; - } - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 4, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('release pets', analyticsData); - } - return typeof cb === "function" ? cb(null, user) : void 0; - }, - releaseMounts: function(req, cb, analytics) { - var analyticsData, mount; - if (user.balance < 1) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - user.balance -= 1; - user.items.currentMount = ""; - for (mount in content.pets) { - user.items.mounts[mount] = null; - } - if (!user.achievements.mountMasterCount) { - user.achievements.mountMasterCount = 0; - } - user.achievements.mountMasterCount++; - } - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 4, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('release mounts', analyticsData); - } - return typeof cb === "function" ? cb(null, user) : void 0; - }, - releaseBoth: function(req, cb) { - var analyticsData, animal, giveTriadBingo; - if (user.balance < 1.5 && !user.achievements.triadBingo) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - giveTriadBingo = true; - if (!user.achievements.triadBingo) { - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 6, - category: 'behavior' - }; - if (typeof analytics !== "undefined" && analytics !== null) { - analytics.track('release pets & mounts', analyticsData); - } - user.balance -= 1.5; - } - user.items.currentMount = ""; - user.items.currentPet = ""; - for (animal in content.pets) { - if (user.items.pets[animal] === -1) { - giveTriadBingo = false; - } - user.items.pets[animal] = 0; - user.items.mounts[animal] = null; - } - if (!user.achievements.beastMasterCount) { - user.achievements.beastMasterCount = 0; - } - user.achievements.beastMasterCount++; - if (!user.achievements.mountMasterCount) { - user.achievements.mountMasterCount = 0; - } - user.achievements.mountMasterCount++; - if (giveTriadBingo) { - if (!user.achievements.triadBingoCount) { - user.achievements.triadBingoCount = 0; - } - user.achievements.triadBingoCount++; - } - } - return typeof cb === "function" ? cb(null, user) : void 0; - }, - buy: function(req, cb, analytics) { - var analyticsData, armoireExp, armoireResp, armoireResult, base, buyResp, drop, eligibleEquipment, item, key, message, name; - key = req.params.key; - item = key === 'potion' ? content.potion : key === 'armoire' ? content.armoire : content.gear.flat[key]; - if (!item) { - return typeof cb === "function" ? cb({ - code: 404, - message: "Item '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" - }) : void 0; - } - if (user.stats.gp < item.value) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageNotEnoughGold', req.language) - }) : void 0; - } - if ((item.canOwn != null) && !item.canOwn(user)) { - return typeof cb === "function" ? cb({ - code: 401, - message: "You can't buy this item" - }) : void 0; - } - armoireResp = void 0; - if (item.key === 'potion') { - user.stats.hp += 15; - if (user.stats.hp > 50) { - user.stats.hp = 50; - } - } else if (item.key === 'armoire') { - armoireResult = user.fns.predictableRandom(user.stats.gp); - eligibleEquipment = _.filter(content.gear.flat, (function(i) { - return i.klass === 'armoire' && !user.items.gear.owned[i.key]; - })); - if (!_.isEmpty(eligibleEquipment) && (armoireResult < .6 || !user.flags.armoireOpened)) { - eligibleEquipment.sort(); - drop = user.fns.randomVal(eligibleEquipment); - user.items.gear.owned[drop.key] = true; - user.flags.armoireOpened = true; - message = i18n.t('armoireEquipment', { - image: '', - dropText: drop.text(req.language) - }, req.language); - if (api.count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) { - user.flags.armoireEmpty = true; - } - armoireResp = { - type: "gear", - dropKey: drop.key, - dropText: drop.text(req.language) - }; - } else if ((!_.isEmpty(eligibleEquipment) && armoireResult < .8) || armoireResult < .5) { - drop = user.fns.randomVal(_.where(content.food, { - canDrop: true - })); - if ((base = user.items.food)[name = drop.key] == null) { - base[name] = 0; - } - user.items.food[drop.key] += 1; - message = i18n.t('armoireFood', { - image: '', - dropArticle: drop.article, - dropText: drop.text(req.language) - }, req.language); - armoireResp = { - type: "food", - dropKey: drop.key, - dropArticle: drop.article, - dropText: drop.text(req.language) - }; - } else { - armoireExp = Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10); - user.stats.exp += armoireExp; - message = i18n.t('armoireExp', req.language); - armoireResp = { - "type": "experience", - "value": armoireExp - }; - } - } else { - if (user.preferences.autoEquip) { - user.items.gear.equipped[item.type] = item.key; - message = user.fns.handleTwoHanded(item, null, req); - } - user.items.gear.owned[item.key] = true; - if (message == null) { - message = i18n.t('messageBought', { - itemText: item.text(req.language) - }, req.language); - } - if (item.last) { - user.fns.ultimateGear(); - } - } - user.stats.gp -= item.value; - analyticsData = { - uuid: user._id, - itemKey: key, - acquireMethod: 'Gold', - goldCost: item.value, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('acquire item', analyticsData); - } - buyResp = _.pick(user, $w('items achievements stats flags')); - if (armoireResp) { - buyResp["armoire"] = armoireResp; - } - return typeof cb === "function" ? cb({ - code: 200, - message: message - }, buyResp) : void 0; - }, - buyQuest: function(req, cb, analytics) { - var analyticsData, base, item, key, message, name; - key = req.params.key; - item = content.quests[key]; - if (!item) { - return typeof cb === "function" ? cb({ - code: 404, - message: "Quest '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" - }) : void 0; - } - if (!(item.category === 'gold' && item.goldValue)) { - return typeof cb === "function" ? cb({ - code: 404, - message: "Quest '" + key + " is not a Gold-purchasable quest (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" - }) : void 0; - } - if (user.stats.gp < item.goldValue) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageNotEnoughGold', req.language) - }) : void 0; - } - message = i18n.t('messageBought', { - itemText: item.text(req.language) - }, req.language); - if ((base = user.items.quests)[name = item.key] == null) { - base[name] = 0; - } - user.items.quests[item.key] += 1; - user.stats.gp -= item.goldValue; - analyticsData = { - uuid: user._id, - itemKey: item.key, - itemType: 'Market', - goldCost: item.goldValue, - acquireMethod: 'Gold', - category: 'behavior' - }; - if (analytics != null) { - analytics.track('acquire item', analyticsData); - } - return typeof cb === "function" ? cb({ - code: 200, - message: message - }, user.items.quests) : void 0; - }, - buyMysterySet: function(req, cb, analytics) { - var mysterySet, ref; - if (!(user.purchased.plan.consecutive.trinkets > 0)) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughHourglasses', req.language) - }) : void 0; - } - mysterySet = (ref = content.timeTravelerStore(user.items.gear.owned)) != null ? ref[req.params.key] : void 0; - if ((typeof window !== "undefined" && window !== null ? window.confirm : void 0) != null) { - if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) { - return; - } - } - if (!mysterySet) { - return typeof cb === "function" ? cb({ - code: 404, - message: "Mystery set not found, or set already owned" - }) : void 0; - } - _.each(mysterySet.items, function(i) { - var analyticsData; - user.items.gear.owned[i.key] = true; - analyticsData = { - uuid: user._id, - itemKey: i.key, - itemType: 'Subscriber Gear', - acquireMethod: 'Hourglass', - category: 'behavior' - }; - return analytics != null ? analytics.track('acquire item', analyticsData) : void 0; - }); - user.purchased.plan.consecutive.trinkets--; - return typeof cb === "function" ? cb({ - code: 200, - message: i18n.t('hourglassPurchaseSet', req.language) - }, _.pick(user, $w('items purchased.plan.consecutive'))) : void 0; - }, - hourglassPurchase: function(req, cb, analytics) { - var analyticsData, key, ref, type; - ref = req.params, type = ref.type, key = ref.key; - if (!content.timeTravelStable[type]) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('typeNotAllowedHourglass', req.language) + JSON.stringify(_.keys(content.timeTravelStable)) - }) : void 0; - } - if (!_.contains(_.keys(content.timeTravelStable[type]), key)) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t(type + 'NotAllowedHourglass', req.language) - }) : void 0; - } - if (user.items[type][key]) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t(type + 'AlreadyOwned', req.language) - }) : void 0; - } - if (!(user.purchased.plan.consecutive.trinkets > 0)) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('notEnoughHourglasses', req.language) - }) : void 0; - } - user.purchased.plan.consecutive.trinkets--; - if (type === 'pets') { - user.items.pets[key] = 5; - } - if (type === 'mounts') { - user.items.mounts[key] = true; - } - analyticsData = { - uuid: user._id, - itemKey: key, - itemType: type, - acquireMethod: 'Hourglass', - category: 'behavior' - }; - if (analytics != null) { - analytics.track('acquire item', analyticsData); - } - return typeof cb === "function" ? cb({ - code: 200, - message: i18n.t('hourglassPurchase', req.language) - }, _.pick(user, $w('items purchased.plan.consecutive'))) : void 0; - }, - sell: function(req, cb) { - var key, ref, type; - ref = req.params, key = ref.key, type = ref.type; - if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food') { - return typeof cb === "function" ? cb({ - code: 404, - message: ":type not found. Must bes in [eggs, hatchingPotions, food]" - }) : void 0; - } - if (!user.items[type][key]) { - return typeof cb === "function" ? cb({ - code: 404, - message: ":key not found for user.items." + type - }) : void 0; - } - user.items[type][key]--; - user.stats.gp += content[type][key].value; - return typeof cb === "function" ? cb(null, _.pick(user, $w('stats items'))) : void 0; - }, - equip: function(req, cb) { - var item, key, message, ref, type; - ref = [req.params.type || 'equipped', req.params.key], type = ref[0], key = ref[1]; - switch (type) { - case 'mount': - if (!user.items.mounts[key]) { - return typeof cb === "function" ? cb({ - code: 404, - message: ":You do not own this mount." - }) : void 0; - } - user.items.currentMount = user.items.currentMount === key ? '' : key; - break; - case 'pet': - if (!user.items.pets[key]) { - return typeof cb === "function" ? cb({ - code: 404, - message: ":You do not own this pet." - }) : void 0; - } - user.items.currentPet = user.items.currentPet === key ? '' : key; - break; - case 'costume': - case 'equipped': - item = content.gear.flat[key]; - if (!user.items.gear.owned[key]) { - return typeof cb === "function" ? cb({ - code: 404, - message: ":You do not own this gear." - }) : void 0; - } - if (user.items.gear[type][item.type] === key) { - user.items.gear[type][item.type] = item.type + "_base_0"; - message = i18n.t('messageUnEquipped', { - itemText: item.text(req.language) - }, req.language); - } else { - user.items.gear[type][item.type] = item.key; - message = user.fns.handleTwoHanded(item, type, req); - } - if (typeof user.markModified === "function") { - user.markModified("items.gear." + type); - } - } - return typeof cb === "function" ? cb((message ? { - code: 200, - message: message - } : null), user.items) : void 0; - }, - hatch: function(req, cb) { - var egg, hatchingPotion, pet, ref; - ref = req.params, egg = ref.egg, hatchingPotion = ref.hatchingPotion; - if (!(egg && hatchingPotion)) { - return typeof cb === "function" ? cb({ - code: 400, - message: "Please specify query.egg & query.hatchingPotion" - }) : void 0; - } - if (!(user.items.eggs[egg] > 0 && user.items.hatchingPotions[hatchingPotion] > 0)) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('messageMissingEggPotion', req.language) - }) : void 0; - } - if (content.hatchingPotions[hatchingPotion].premium && !content.dropEggs[egg]) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('messageInvalidEggPotionCombo', req.language) - }) : void 0; - } - pet = egg + "-" + hatchingPotion; - if (user.items.pets[pet] && user.items.pets[pet] > 0) { - return typeof cb === "function" ? cb({ - code: 403, - message: i18n.t('messageAlreadyPet', req.language) - }) : void 0; - } - user.items.pets[pet] = 5; - user.items.eggs[egg]--; - user.items.hatchingPotions[hatchingPotion]--; - return typeof cb === "function" ? cb({ - code: 200, - message: i18n.t('messageHatched', req.language) - }, user.items) : void 0; - }, - unlock: function(req, cb, analytics) { - var alreadyOwns, analyticsData, cost, fullSet, k, path, split, v; - path = req.query.path; - fullSet = ~path.indexOf(","); - cost = ~path.indexOf('background.') ? fullSet ? 3.75 : 1.75 : fullSet ? 1.25 : 0.5; - alreadyOwns = !fullSet && user.fns.dotGet("purchased." + path) === true; - if ((user.balance < cost || !user.balance) && !alreadyOwns) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } - if (fullSet) { - _.each(path.split(","), function(p) { - if (~path.indexOf('gear.')) { - user.fns.dotSet("" + p, true); - true; - } else { - - } - user.fns.dotSet("purchased." + p, true); - return true; - }); - } else { - if (alreadyOwns) { - split = path.split('.'); - v = split.pop(); - k = split.join('.'); - if (k === 'background' && v === user.preferences.background) { - v = ''; - } - user.fns.dotSet("preferences." + k, v); - return typeof cb === "function" ? cb(null, req) : void 0; - } - user.fns.dotSet("purchased." + path, true); - } - user.balance -= cost; - if (~path.indexOf('gear.')) { - if (typeof user.markModified === "function") { - user.markModified('gear.owned'); - } - } else { - if (typeof user.markModified === "function") { - user.markModified('purchased'); - } - } - analyticsData = { - uuid: user._id, - itemKey: path, - itemType: 'customization', - acquireMethod: 'Gems', - gemCost: cost / .25, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('acquire item', analyticsData); - } - return typeof cb === "function" ? cb(null, _.pick(user, $w('purchased preferences items'))) : void 0; - }, - changeClass: function(req, cb, analytics) { - var analyticsData, klass, ref; - klass = (ref = req.query) != null ? ref["class"] : void 0; - if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') { - analyticsData = { - uuid: user._id, - "class": klass, - acquireMethod: 'Gems', - gemCost: 3, - category: 'behavior' - }; - if (analytics != null) { - analytics.track('change class', analyticsData); - } - user.stats["class"] = klass; - user.flags.classSelected = true; - _.each(["weapon", "armor", "shield", "head"], function(type) { - var foundKey; - foundKey = false; - _.findLast(user.items.gear.owned, function(v, k) { - if (~k.indexOf(type + "_" + klass) && v === true) { - return foundKey = k; - } - }); - 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")) { - user.items.gear.owned[type + "_" + klass + "_0"] = true; - } - return true; - }); - } else { - if (user.preferences.disableClasses) { - user.preferences.disableClasses = false; - user.preferences.autoAllocate = false; - } else { - if (!(user.balance >= .75)) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } - user.balance -= .75; - } - _.merge(user.stats, { - str: 0, - con: 0, - per: 0, - int: 0, - points: api.capByLevel(user.stats.lvl) - }); - user.flags.classSelected = false; - } - return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags items preferences'))) : void 0; - }, - disableClasses: function(req, cb) { - user.stats["class"] = 'warrior'; - user.flags.classSelected = true; - user.preferences.disableClasses = true; - user.preferences.autoAllocate = true; - user.stats.str = api.capByLevel(user.stats.lvl); - user.stats.points = 0; - return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags preferences'))) : void 0; - }, - allocate: function(req, cb) { - var stat; - stat = req.query.stat || 'str'; - if (user.stats.points > 0) { - user.stats[stat]++; - user.stats.points--; - if (stat === 'int') { - user.stats.mp++; - } - } - return typeof cb === "function" ? cb(null, _.pick(user, $w('stats'))) : void 0; - }, + update: _.partial(importedOps.update, user), + sleep: _.partial(importedOps.sleep, user), + revive: _.partial(importedOps.revive, user), + reset: _.partial(importedOps.reset, user), + reroll: _.partial(importedOps.reroll, user), + rebirth: _.partial(importedOps.reroll, user), + allocateNow: _.partial(importedOps.reroll, user), + clearCompleted: _.partial(importedOps.clearCompleted, user), + sortTask: _.partial(importedOps.sortTask, user), + updateTask: _.partial(importedOps.updateTask, user), + deleteTask: _.partial(importedOps.deleteTask, user), + addTask: _.partial(importedOps.addTask, user), + addTag: _.partial(importedOps.addTag, user), + sortTag: _.partial(importedOps.sortTag, user), + getTags: _.partial(importedOps.getTags, user), + getTag: _.partial(importedOps.getTag, user), + updateTag: _.partial(importedOps.updateTag, user), + deleteTag: _.partial(importedOps.deleteTag, user), + addWebhook: _.partial(importedOps.addWebhook, user), + updateWebhook: _.partial(importedOps.updateWebhook, user), + deleteWebhook: _.partial(importedOps.deleteWebhook, user), + addPushDevice: _.partial(importedOps.addPushDevice, user), + clearPMs: _.partial(importedOps.clearPMs, user), + deletePM: _.partial(importedOps.deletePM, user), + blockUser: _.partial(importedOps.blockUser, user), + feed: _.partial(importedOps.feed, user), + buySpecialSpell: _.partial(importedOps.buySpecialSpell, user), + purchase: _.partial(importedOps.purchase, user), + releasePets: _.partial(importedOps.releasePets, user), + releaseMounts: _.partial(importedOps.releaseMounts, user), + releaseBoth: _.partial(importedOps.releaseBoth, user), + buy: _.partial(importedOps.buy, user), + buyQuest: _.partial(importedOps.buyQuest, user), + buyMysterySet: _.partial(importedOps.buyMysterySet, user), + hourglassPurchase: _.partial(importedOps.hourglassPurchase, user), + sell: _.partial(importedOps.sell, user), + equip: _.partial(importedOps.equip, user), + hatch: _.partial(importedOps.hatch, user), + unlock: _.partial(importedOps.unlock, user), + changeClass: _.partial(importedOps.changeClass, user), + disableClasses: _.partial(importedOps.disableClasses, user), + allocate: _.partial(importedOps.allocate, user), readCard: _.partial(importedOps.readCard, user), - openMysteryItem: function(req, cb, analytics) { - var analyticsData, item, ref, ref1; - item = (ref = user.purchased.plan) != null ? (ref1 = ref.mysteryItems) != null ? ref1.shift() : void 0 : void 0; - if (!item) { - return typeof cb === "function" ? cb({ - code: 400, - message: "Empty" - }) : void 0; - } - item = content.gear.flat[item]; - user.items.gear.owned[item.key] = true; - if (typeof user.markModified === "function") { - user.markModified('purchased.plan.mysteryItems'); - } - item.notificationType = 'Mystery'; - analyticsData = { - uuid: user._id, - itemKey: item, - itemType: 'Subscriber Gear', - acquireMethod: 'Subscriber', - category: 'behavior' - }; - if (analytics != null) { - analytics.track('open mystery item', analyticsData); - } - if (typeof window !== 'undefined') { - (user._tmp != null ? user._tmp : user._tmp = {}).drop = item; - } - return typeof cb === "function" ? cb(null, user.items.gear.owned) : void 0; - }, - score: function(req, cb) { - var addPoints, calculateDelta, calculateReverseDelta, changeTaskValue, delta, direction, gainMP, id, multiplier, num, options, ref, stats, subtractPoints, task, th; - ref = req.params, id = ref.id, direction = ref.direction; - task = user.tasks[id]; - options = req.query || {}; - _.defaults(options, { - times: 1, - cron: false - }); - user._tmp = {}; - 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 typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('messageNotEnoughGold', req.language) - }) : void 0; - } - delta = 0; - calculateDelta = function() { - var currVal, nextDelta, ref1; - 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 (((ref1 = task.checklist) != null ? ref1.length : void 0) > 0) { - if (direction === 'down' && task.type === 'daily' && options.cron) { - nextDelta *= 1 - _.reduce(task.checklist, (function(m, i) { - return m + (i.completed ? 1 : 0); - }), 0) / task.checklist.length; - } - if (task.type === 'todo') { - nextDelta *= 1 + _.reduce(task.checklist, (function(m, i) { - return m + (i.completed ? 1 : 0); - }), 0); - } - } - return nextDelta; - }; - calculateReverseDelta = function() { - var calc, closeEnough, currVal, diff, nextDelta, ref1, testVal; - currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value; - testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); - closeEnough = 0.00001; - while (true) { - calc = testVal + Math.pow(0.9747, testVal); - diff = currVal - calc; - if (Math.abs(diff) < closeEnough) { - break; - } - if (diff > 0) { - testVal -= diff; - } else { - testVal += diff; - } - } - nextDelta = testVal - currVal; - if (((ref1 = task.checklist) != null ? ref1.length : void 0) > 0) { - if (task.type === 'todo') { - nextDelta *= 1 + _.reduce(task.checklist, (function(m, i) { - return m + (i.completed ? 1 : 0); - }), 0); - } - } - return nextDelta; - }; - changeTaskValue = function() { - return _.times(options.times, function() { - var nextDelta, ref1; - nextDelta = !options.cron && direction === 'down' ? calculateReverseDelta() : calculateDelta(); - if (task.type !== 'reward') { - if (user.preferences.automaticAllocation === true && user.preferences.allocationMode === 'taskbased' && !(task.type === 'todo' && direction === 'down')) { - user.stats.training[task.attribute] += nextDelta; - } - if (direction === 'up') { - user.party.quest.progress.up = user.party.quest.progress.up || 0; - if ((ref1 = task.type) === 'daily' || ref1 === 'todo') { - user.party.quest.progress.up += nextDelta * (1 + (user._statsComputed.str / 200)); - } - if (task.type === 'habit') { - user.party.quest.progress.up += nextDelta * (0.5 + (user._statsComputed.str / 400)); - } - } - task.value += nextDelta; - } - return delta += nextDelta; - }); - }; - addPoints = function() { - var _crit, afterStreak, currStreak, gpMod, intBonus, perBonus, streakBonus; - _crit = (delta > 0 ? user.fns.crit() : 1); - if (_crit > 1) { - user._tmp.crit = _crit; - } - intBonus = 1 + (user._statsComputed.int * .025); - stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6); - perBonus = 1 + user._statsComputed.per * .02; - gpMod = delta * task.priority * _crit * perBonus; - return stats.gp += task.streak ? (currStreak = direction === 'down' ? task.streak - 1 : task.streak, streakBonus = currStreak / 100 + 1, afterStreak = gpMod * streakBonus, currStreak > 0 ? gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0 : void 0, afterStreak) : gpMod; - }; - subtractPoints = function() { - var conBonus, hpMod; - conBonus = 1 - (user._statsComputed.con / 250); - if (conBonus < .1) { - conBonus = 0.1; - } - hpMod = delta * conBonus * task.priority * 2; - return stats.hp += Math.round(hpMod * 10) / 10; - }; - gainMP = function(delta) { - delta *= user._tmp.crit || 1; - user.stats.mp += delta; - if (user.stats.mp >= user._statsComputed.maxMP) { - user.stats.mp = user._statsComputed.maxMP; - } - if (user.stats.mp < 0) { - return user.stats.mp = 0; - } - }; - switch (task.type) { - case 'habit': - changeTaskValue(); - if (delta > 0) { - addPoints(); - } else { - subtractPoints(); - } - gainMP(_.max([0.25, .0025 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1)); - th = (task.history != null ? task.history : task.history = []); - if (th[th.length - 1] && moment(th[th.length - 1].date).isSame(new Date, 'day')) { - th[th.length - 1].value = task.value; - } else { - th.push({ - date: +(new Date), - value: task.value - }); - } - if (typeof user.markModified === "function") { - user.markModified("habits." + (_.findIndex(user.habits, { - id: task.id - })) + ".history"); - } - break; - case 'daily': - if (options.cron) { - changeTaskValue(); - subtractPoints(); - if (!user.stats.buffs.streaks) { - task.streak = 0; - } - } else { - changeTaskValue(); - if (direction === 'down') { - delta = calculateDelta(); - } - addPoints(); - gainMP(_.max([1, .01 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1)); - if (direction === 'up') { - 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 ((task.streak % 21) === 0) { - user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0; - } - task.streak = task.streak ? task.streak - 1 : 0; - } - } - break; - case 'todo': - if (options.cron) { - changeTaskValue(); - } else { - task.dateCompleted = direction === 'up' ? new Date : void 0; - changeTaskValue(); - if (direction === 'down') { - delta = calculateDelta(); - } - addPoints(); - multiplier = _.max([ - _.reduce(task.checklist, (function(m, i) { - return m + (i.completed ? 1 : 0); - }), 1), 1 - ]); - gainMP(_.max([multiplier, .01 * user._statsComputed.maxMP * multiplier]) * (direction === 'down' ? -1 : 1)); - } - break; - case 'reward': - changeTaskValue(); - stats.gp -= Math.abs(task.value); - num = parseFloat(task.value).toFixed(2); - if (stats.gp < 0) { - stats.hp += stats.gp; - stats.gp = 0; - } - } - user.fns.updateStats(stats, req); - if (typeof window === 'undefined') { - if (direction === 'up') { - user.fns.randomDrop({ - task: task, - delta: delta - }, req); - } - } - if (typeof cb === "function") { - cb(null, user); - } - return delta; - } + openMysteryItem: _.partial(importedOps.openMysteryItem, user), + score: _.partial(importedOps.score, user), }; } user.fns = { diff --git a/common/script/libs/index.js b/common/script/libs/index.js new file mode 100644 index 0000000000..2842fa9ec9 --- /dev/null +++ b/common/script/libs/index.js @@ -0,0 +1,15 @@ +import uuid from './uuid'; +import taskDefaults from './taskDefaults'; +import refPush from './refPush'; +import splitWhitespace from './splitWhitespace'; +import planGemLimits from './planGemLimits'; +import preenTodos from './preenTodos'; + +module.exports = { + uuid, + taskDefaults, + refPush, + splitWhitespace, + planGemLimits, + preenTodos, +}; diff --git a/common/script/libs/planGemLimits.js b/common/script/libs/planGemLimits.js new file mode 100644 index 0000000000..e719c7695d --- /dev/null +++ b/common/script/libs/planGemLimits.js @@ -0,0 +1,4 @@ +module.exports = { + convRate: 20, + convCap: 25, +}; diff --git a/common/script/libs/preenTodos.js b/common/script/libs/preenTodos.js new file mode 100644 index 0000000000..f07a49c9d0 --- /dev/null +++ b/common/script/libs/preenTodos.js @@ -0,0 +1,14 @@ +import moment from 'moment'; +import _ from 'lodash'; + +/* + Preen 3-day past-completed To-Dos from Angular & mobile app + */ + +module.exports = function(tasks) { + return _.filter(tasks, function(t) { + return !t.completed || (t.challenge && t.challenge.id) || moment(t.dateCompleted).isAfter(moment().subtract({ + days: 3 + })); + }); +}; diff --git a/common/script/libs/refPush.js b/common/script/libs/refPush.js new file mode 100644 index 0000000000..06b3617014 --- /dev/null +++ b/common/script/libs/refPush.js @@ -0,0 +1,19 @@ +import _ from 'lodash'; +import uuid from './uuid'; + +/* + Reflists are arrays, but stored as objects. Mongoose has a helluvatime working with arrays (the main problem for our + syncing issues) - so the goal is to move away from arrays to objects, since mongoose can reference elements by ID + no problem. To maintain sorting, we use these helper functions: + */ + +module.exports = function(reflist, item, prune) { + if (prune == null) { + prune = 0; + } + item.sort = _.isEmpty(reflist) ? 0 : _.max(reflist, 'sort').sort + 1; + if (!(item.id && !reflist[item.id])) { + item.id = uuid(); + } + return reflist[item.id] = item; +}; diff --git a/common/script/libs/splitWhitespace.js b/common/script/libs/splitWhitespace.js new file mode 100644 index 0000000000..1ef3d513aa --- /dev/null +++ b/common/script/libs/splitWhitespace.js @@ -0,0 +1,3 @@ +module.exports = function(s) { + return s.split(' '); +}; diff --git a/common/script/libs/taskDefaults.js b/common/script/libs/taskDefaults.js new file mode 100644 index 0000000000..69c815e4fd --- /dev/null +++ b/common/script/libs/taskDefaults.js @@ -0,0 +1,71 @@ +import uuid from './uuid'; +import _ from 'lodash'; + +/* +Even though Mongoose handles task defaults, we want to make sure defaults are set on the client-side before +sending up to the server for performance + */ + +// TODO revisit + +module.exports = function(task) { + var defaults, ref, ref1, ref2; + if (task == null) { + task = {}; + } + if (!(task.type && ((ref = task.type) === 'habit' || ref === 'daily' || ref === 'todo' || ref === 'reward'))) { + task.type = 'habit'; + } + defaults = { + id: uuid(), + text: task.id != null ? task.id : '', + notes: '', + priority: 1, + challenge: {}, + attribute: 'str', + dateCreated: new Date() + }; + _.defaults(task, defaults); + if (task.type === 'habit') { + _.defaults(task, { + up: true, + down: true + }); + } + if ((ref1 = task.type) === 'habit' || ref1 === 'daily') { + _.defaults(task, { + history: [] + }); + } + if ((ref2 = task.type) === 'daily' || ref2 === 'todo') { + _.defaults(task, { + completed: false + }); + } + if (task.type === 'daily') { + _.defaults(task, { + streak: 0, + repeat: { + su: true, + m: true, + t: true, + w: true, + th: true, + f: true, + s: true + } + }, { + startDate: new Date(), + everyX: 1, + frequency: 'weekly' + }); + } + task._id = task.id; + if (task.value == null) { + task.value = task.type === 'reward' ? 10 : 0; + } + if (!_.isNumber(task.priority)) { + task.priority = 1; + } + return task; +}; diff --git a/common/script/libs/uuid.js b/common/script/libs/uuid.js new file mode 100644 index 0000000000..4a26440d7d --- /dev/null +++ b/common/script/libs/uuid.js @@ -0,0 +1,9 @@ +// TODO use node-uuid module +module.exports = function() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { + var r, v; + r = Math.random() * 16 | 0; + v = (c === "x" ? r : r & 0x3 | 0x8); + return v.toString(16); + }); +}; diff --git a/common/script/ops/addPushDevice.js b/common/script/ops/addPushDevice.js new file mode 100644 index 0000000000..d96cf249cf --- /dev/null +++ b/common/script/ops/addPushDevice.js @@ -0,0 +1,20 @@ +import _ from 'lodash'; + +module.exports = function(user, req, cb) { + var i, item, pd; + if (!user.pushDevices) { + user.pushDevices = []; + } + pd = user.pushDevices; + item = { + regId: req.body.regId, + type: req.body.type + }; + i = _.findIndex(pd, { + regId: item.regId + }); + if (i === -1) { + pd.push(item); + } + return typeof cb === "function" ? cb(null, user.pushDevices) : void 0; +}; diff --git a/common/script/ops/addTag.js b/common/script/ops/addTag.js new file mode 100644 index 0000000000..88125ca600 --- /dev/null +++ b/common/script/ops/addTag.js @@ -0,0 +1,12 @@ +import uuid from '../libs/uuid'; + +module.exports = function(user, req, cb) { + if (user.tags == null) { + user.tags = []; + } + user.tags.push({ + name: req.body.name, + id: req.body.id || api.uuid() + }); + return typeof cb === "function" ? cb(null, user.tags) : void 0; +}; diff --git a/common/script/ops/addTask.js b/common/script/ops/addTask.js new file mode 100644 index 0000000000..51136a0fcc --- /dev/null +++ b/common/script/ops/addTask.js @@ -0,0 +1,27 @@ +import taskDefaults from '../libs/taskDefaults'; +import i18n from '../i18n'; + +module.exports = function(user, req, cb) { + var task; + task = api.taskDefaults(req.body); + if (user.tasks[task.id] != null) { + return typeof cb === "function" ? cb({ + code: 409, + message: i18n.t('messageDuplicateTaskID', req.language) + }) : void 0; + } + user[task.type + "s"].unshift(task); + if (user.preferences.newTaskEdit) { + task._editing = true; + } + if (user.preferences.tagsCollapsed) { + task._tags = true; + } + if (!user.preferences.advancedCollapsed) { + task._advanced = true; + } + if (typeof cb === "function") { + cb(null, task); + } + return task; +}; diff --git a/common/script/ops/addWebhook.js b/common/script/ops/addWebhook.js new file mode 100644 index 0000000000..61429449a4 --- /dev/null +++ b/common/script/ops/addWebhook.js @@ -0,0 +1,15 @@ +import refPush from '../libs/refPush'; + +module.exports = function(user, req, cb) { + var wh; + wh = user.preferences.webhooks; + api.refPush(wh, { + url: req.body.url, + enabled: req.body.enabled || true, + id: req.body.id + }); + if (typeof user.markModified === "function") { + user.markModified('preferences.webhooks'); + } + return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0; +}; diff --git a/common/script/ops/allocate.js b/common/script/ops/allocate.js new file mode 100644 index 0000000000..d471d45d62 --- /dev/null +++ b/common/script/ops/allocate.js @@ -0,0 +1,15 @@ +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function(user, req, cb) { + var stat; + stat = req.query.stat || 'str'; + if (user.stats.points > 0) { + user.stats[stat]++; + user.stats.points--; + if (stat === 'int') { + user.stats.mp++; + } + } + return typeof cb === "function" ? cb(null, _.pick(user, $w('stats'))) : void 0; +}; diff --git a/common/script/ops/allocateNow.js b/common/script/ops/allocateNow.js new file mode 100644 index 0000000000..815c0b8959 --- /dev/null +++ b/common/script/ops/allocateNow.js @@ -0,0 +1,10 @@ +import _ from 'lodash'; + +module.exports = function(user, req, cb) { + _.times(user.stats.points, user.fns.autoAllocate); + user.stats.points = 0; + if (typeof user.markModified === "function") { + user.markModified('stats'); + } + return typeof cb === "function" ? cb(null, user.stats) : void 0; +}; diff --git a/common/script/ops/blockUser.js b/common/script/ops/blockUser.js new file mode 100644 index 0000000000..dd08925640 --- /dev/null +++ b/common/script/ops/blockUser.js @@ -0,0 +1,13 @@ +module.exports = function(user, req, cb) { + var i; + i = user.inbox.blocks.indexOf(req.params.uuid); + if (~i) { + user.inbox.blocks.splice(i, 1); + } else { + user.inbox.blocks.push(req.params.uuid); + } + if (typeof user.markModified === "function") { + user.markModified('inbox.blocks'); + } + return typeof cb === "function" ? cb(null, user.inbox.blocks) : void 0; +}; diff --git a/common/script/ops/buy.js b/common/script/ops/buy.js new file mode 100644 index 0000000000..4735025cd4 --- /dev/null +++ b/common/script/ops/buy.js @@ -0,0 +1,119 @@ +import content from '../content/index'; +import i18n from '../i18n'; +import _ from 'lodash'; +import count from '../count'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function(user, req, cb, analytics) { + var analyticsData, armoireExp, armoireResp, armoireResult, base, buyResp, drop, eligibleEquipment, item, key, message, name; + key = req.params.key; + item = key === 'potion' ? content.potion : key === 'armoire' ? content.armoire : content.gear.flat[key]; + if (!item) { + return typeof cb === "function" ? cb({ + code: 404, + message: "Item '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" + }) : void 0; + } + if (user.stats.gp < item.value) { + return typeof cb === "function" ? cb({ + code: 401, + message: i18n.t('messageNotEnoughGold', req.language) + }) : void 0; + } + if ((item.canOwn != null) && !item.canOwn(user)) { + return typeof cb === "function" ? cb({ + code: 401, + message: "You can't buy this item" + }) : void 0; + } + armoireResp = void 0; + if (item.key === 'potion') { + user.stats.hp += 15; + if (user.stats.hp > 50) { + user.stats.hp = 50; + } + } else if (item.key === 'armoire') { + armoireResult = user.fns.predictableRandom(user.stats.gp); + eligibleEquipment = _.filter(content.gear.flat, (function(i) { + return i.klass === 'armoire' && !user.items.gear.owned[i.key]; + })); + if (!_.isEmpty(eligibleEquipment) && (armoireResult < .6 || !user.flags.armoireOpened)) { + eligibleEquipment.sort(); + drop = user.fns.randomVal(eligibleEquipment); + user.items.gear.owned[drop.key] = true; + user.flags.armoireOpened = true; + message = i18n.t('armoireEquipment', { + image: '', + dropText: drop.text(req.language) + }, req.language); + if (api.count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) { + user.flags.armoireEmpty = true; + } + armoireResp = { + type: "gear", + dropKey: drop.key, + dropText: drop.text(req.language) + }; + } else if ((!_.isEmpty(eligibleEquipment) && armoireResult < .8) || armoireResult < .5) { + drop = user.fns.randomVal(_.where(content.food, { + canDrop: true + })); + if ((base = user.items.food)[name = drop.key] == null) { + base[name] = 0; + } + user.items.food[drop.key] += 1; + message = i18n.t('armoireFood', { + image: '', + dropArticle: drop.article, + dropText: drop.text(req.language) + }, req.language); + armoireResp = { + type: "food", + dropKey: drop.key, + dropArticle: drop.article, + dropText: drop.text(req.language) + }; + } else { + armoireExp = Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10); + user.stats.exp += armoireExp; + message = i18n.t('armoireExp', req.language); + armoireResp = { + "type": "experience", + "value": armoireExp + }; + } + } else { + if (user.preferences.autoEquip) { + user.items.gear.equipped[item.type] = item.key; + message = user.fns.handleTwoHanded(item, null, req); + } + user.items.gear.owned[item.key] = true; + if (message == null) { + message = i18n.t('messageBought', { + itemText: item.text(req.language) + }, req.language); + } + if (item.last) { + user.fns.ultimateGear(); + } + } + user.stats.gp -= item.value; + analyticsData = { + uuid: user._id, + itemKey: key, + acquireMethod: 'Gold', + goldCost: item.value, + category: 'behavior' + }; + if (analytics != null) { + analytics.track('acquire item', analyticsData); + } + buyResp = _.pick(user, $w('items achievements stats flags')); + if (armoireResp) { + buyResp["armoire"] = armoireResp; + } + return typeof cb === "function" ? cb({ + code: 200, + message: message + }, buyResp) : void 0; +}; diff --git a/common/script/ops/buyMysterySet.js b/common/script/ops/buyMysterySet.js new file mode 100644 index 0000000000..557cfd0595 --- /dev/null +++ b/common/script/ops/buyMysterySet.js @@ -0,0 +1,43 @@ +import i18n from '../i18n'; +import content from '../content/index'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function(user, req, cb, analytics) { + var mysterySet, ref; + if (!(user.purchased.plan.consecutive.trinkets > 0)) { + return typeof cb === "function" ? cb({ + code: 401, + message: i18n.t('notEnoughHourglasses', req.language) + }) : void 0; + } + mysterySet = (ref = content.timeTravelerStore(user.items.gear.owned)) != null ? ref[req.params.key] : void 0; + if ((typeof window !== "undefined" && window !== null ? window.confirm : void 0) != null) { + if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) { + return; + } + } + if (!mysterySet) { + return typeof cb === "function" ? cb({ + code: 404, + message: "Mystery set not found, or set already owned" + }) : void 0; + } + _.each(mysterySet.items, function(i) { + var analyticsData; + user.items.gear.owned[i.key] = true; + analyticsData = { + uuid: user._id, + itemKey: i.key, + itemType: 'Subscriber Gear', + acquireMethod: 'Hourglass', + category: 'behavior' + }; + return analytics != null ? analytics.track('acquire item', analyticsData) : void 0; + }); + user.purchased.plan.consecutive.trinkets--; + return typeof cb === "function" ? cb({ + code: 200, + message: i18n.t('hourglassPurchaseSet', req.language) + }, _.pick(user, $w('items purchased.plan.consecutive'))) : void 0; +}; diff --git a/common/script/ops/buyQuest.js b/common/script/ops/buyQuest.js new file mode 100644 index 0000000000..b7653d43ce --- /dev/null +++ b/common/script/ops/buyQuest.js @@ -0,0 +1,49 @@ +import i18n from '../i18n'; +import content from '../content/index'; + +module.exports = function(user, req, cb, analytics) { + var analyticsData, base, item, key, message, name; + key = req.params.key; + item = content.quests[key]; + if (!item) { + return typeof cb === "function" ? cb({ + code: 404, + message: "Quest '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" + }) : void 0; + } + if (!(item.category === 'gold' && item.goldValue)) { + return typeof cb === "function" ? cb({ + code: 404, + message: "Quest '" + key + " is not a Gold-purchasable quest (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)" + }) : void 0; + } + if (user.stats.gp < item.goldValue) { + return typeof cb === "function" ? cb({ + code: 401, + message: i18n.t('messageNotEnoughGold', req.language) + }) : void 0; + } + message = i18n.t('messageBought', { + itemText: item.text(req.language) + }, req.language); + if ((base = user.items.quests)[name = item.key] == null) { + base[name] = 0; + } + user.items.quests[item.key] += 1; + user.stats.gp -= item.goldValue; + analyticsData = { + uuid: user._id, + itemKey: item.key, + itemType: 'Market', + goldCost: item.goldValue, + acquireMethod: 'Gold', + category: 'behavior' + }; + if (analytics != null) { + analytics.track('acquire item', analyticsData); + } + return typeof cb === "function" ? cb({ + code: 200, + message: message + }, user.items.quests) : void 0; +}; diff --git a/common/script/ops/buySpecialSpell.js b/common/script/ops/buySpecialSpell.js new file mode 100644 index 0000000000..408c8e94c3 --- /dev/null +++ b/common/script/ops/buySpecialSpell.js @@ -0,0 +1,31 @@ +import i18n from '../i18n'; +import content from '../content/index'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function(user, req, cb) { + var base, item, key, message; + key = req.params.key; + item = content.special[key]; + if (user.stats.gp < item.value) { + return typeof cb === "function" ? cb({ + code: 401, + message: i18n.t('messageNotEnoughGold', req.language) + }) : void 0; + } + user.stats.gp -= item.value; + if ((base = user.items.special)[key] == null) { + base[key] = 0; + } + user.items.special[key]++; + if (typeof user.markModified === "function") { + user.markModified('items.special'); + } + message = i18n.t('messageBought', { + itemText: item.text(req.language) + }, req.language); + return typeof cb === "function" ? cb({ + code: 200, + message: message + }, _.pick(user, $w('items stats'))) : void 0; +}; diff --git a/common/script/ops/changeClass.js b/common/script/ops/changeClass.js new file mode 100644 index 0000000000..4a9a7e0536 --- /dev/null +++ b/common/script/ops/changeClass.js @@ -0,0 +1,59 @@ +import i18n from '../i18n'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; +import { capByLevel } from '../statHelpers'; + +module.exports = function(user, req, cb, analytics) { + var analyticsData, klass, ref; + klass = (ref = req.query) != null ? ref["class"] : void 0; + if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') { + analyticsData = { + uuid: user._id, + "class": klass, + acquireMethod: 'Gems', + gemCost: 3, + category: 'behavior' + }; + if (analytics != null) { + analytics.track('change class', analyticsData); + } + user.stats["class"] = klass; + user.flags.classSelected = true; + _.each(["weapon", "armor", "shield", "head"], function(type) { + var foundKey; + foundKey = false; + _.findLast(user.items.gear.owned, function(v, k) { + if (~k.indexOf(type + "_" + klass) && v === true) { + return foundKey = k; + } + }); + 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")) { + user.items.gear.owned[type + "_" + klass + "_0"] = true; + } + return true; + }); + } else { + if (user.preferences.disableClasses) { + user.preferences.disableClasses = false; + user.preferences.autoAllocate = false; + } else { + if (!(user.balance >= .75)) { + return typeof cb === "function" ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } + user.balance -= .75; + } + _.merge(user.stats, { + str: 0, + con: 0, + per: 0, + int: 0, + points: api.capByLevel(user.stats.lvl) + }); + user.flags.classSelected = false; + } + return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags items preferences'))) : void 0; +}; diff --git a/common/script/ops/clearCompleted.js b/common/script/ops/clearCompleted.js new file mode 100644 index 0000000000..d60f12704f --- /dev/null +++ b/common/script/ops/clearCompleted.js @@ -0,0 +1,12 @@ +import _ from 'lodash'; + +module.exports = function(user, req, cb) { + _.remove(user.todos, function(t) { + var ref; + return t.completed && !((ref = t.challenge) != null ? ref.id : void 0); + }); + if (typeof user.markModified === "function") { + user.markModified('todos'); + } + return typeof cb === "function" ? cb(null, user.todos) : void 0; +}; diff --git a/common/script/ops/clearPMs.js b/common/script/ops/clearPMs.js new file mode 100644 index 0000000000..47e0a6ebc6 --- /dev/null +++ b/common/script/ops/clearPMs.js @@ -0,0 +1,7 @@ +module.exports = function(user, req, cb) { + user.inbox.messages = {}; + if (typeof user.markModified === "function") { + user.markModified('inbox.messages'); + } + return typeof cb === "function" ? cb(null, user.inbox.messages) : void 0; +}; diff --git a/common/script/ops/deletePM.js b/common/script/ops/deletePM.js new file mode 100644 index 0000000000..d4d5684fbd --- /dev/null +++ b/common/script/ops/deletePM.js @@ -0,0 +1,7 @@ +module.exports = function (user, req, cb) { + delete user.inbox.messages[req.params.id]; + if (typeof user.markModified === 'function') { + user.markModified('inbox.messages.' + req.params.id); + } + return typeof cb === 'function' ? cb(null, user.inbox.messages) : void 0; +}; diff --git a/common/script/ops/deleteTag.js b/common/script/ops/deleteTag.js new file mode 100644 index 0000000000..6339f62d3d --- /dev/null +++ b/common/script/ops/deleteTag.js @@ -0,0 +1,26 @@ +import i18n from '../i18n'; +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + var i, tag, tid; + tid = req.params.id; + i = _.findIndex(user.tags, { + id: tid + }); + if (!~i) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTagNotFound', req.language) + }) : void 0; + } + tag = user.tags[i]; + delete user.filters[tag.id]; + user.tags.splice(i, 1); + _.each(user.tasks, function (task) { + return delete task.tags[tag.id]; + }); + _.each(['habits', 'dailys', 'todos', 'rewards'], function (type) { + return typeof user.markModified === 'function' ? user.markModified(type) : void 0; + }); + return typeof cb === 'function' ? cb(null, user.tags) : void 0; +}; diff --git a/common/script/ops/deleteTask.js b/common/script/ops/deleteTask.js new file mode 100644 index 0000000000..c72f2ec07e --- /dev/null +++ b/common/script/ops/deleteTask.js @@ -0,0 +1,17 @@ +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var i, ref, task; + task = user.tasks[(ref = req.params) !== null ? ref.id : void 0]; + if (!task) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTaskNotFound', req.language) + }) : void 0; + } + i = user[task.type + 's'].indexOf(task); + if (~i) { + user[task.type + 's'].splice(i, 1); + } + return typeof cb === 'function' ? cb(null, {}) : void 0; +}; diff --git a/common/script/ops/deleteWebhook.js b/common/script/ops/deleteWebhook.js new file mode 100644 index 0000000000..da9b598ead --- /dev/null +++ b/common/script/ops/deleteWebhook.js @@ -0,0 +1,7 @@ +module.exports = function (user, req, cb) { + delete user.preferences.webhooks[req.params.id]; + if (typeof user.markModified === 'function') { + user.markModified('preferences.webhooks'); + } + return typeof cb === 'function' ? cb(null, user.preferences.webhooks) : void 0; +}; diff --git a/common/script/ops/disableClasses.js b/common/script/ops/disableClasses.js new file mode 100644 index 0000000000..cf8294dedd --- /dev/null +++ b/common/script/ops/disableClasses.js @@ -0,0 +1,13 @@ +import splitWhitespace from '../libs/splitWhitespace'; +import { capByLevel } from '../statHelpers'; +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + user.stats['class'] = 'warrior'; + user.flags.classSelected = true; + user.preferences.disableClasses = true; + user.preferences.autoAllocate = true; + user.stats.str = capByLevel(user.stats.lvl); + user.stats.points = 0; + return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('stats flags preferences'))) : void 0; +}; diff --git a/common/script/ops/equip.js b/common/script/ops/equip.js new file mode 100644 index 0000000000..621e3e1f67 --- /dev/null +++ b/common/script/ops/equip.js @@ -0,0 +1,52 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var item, key, message, ref, type; + ref = [req.params.type || 'equipped', req.params.key], type = ref[0], key = ref[1]; + switch (type) { + case 'mount': + if (!user.items.mounts[key]) { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':You do not own this mount.' + }) : void 0; + } + user.items.currentMount = user.items.currentMount === key ? '' : key; + break; + case 'pet': + if (!user.items.pets[key]) { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':You do not own this pet.' + }) : void 0; + } + user.items.currentPet = user.items.currentPet === key ? '' : key; + break; + case 'costume': + case 'equipped': + item = content.gear.flat[key]; + if (!user.items.gear.owned[key]) { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':You do not own this gear.' + }) : void 0; + } + if (user.items.gear[type][item.type] === key) { + user.items.gear[type][item.type] = item.type + '_base_0'; + message = i18n.t('messageUnEquipped', { + itemText: item.text(req.language) + }, req.language); + } else { + user.items.gear[type][item.type] = item.key; + message = user.fns.handleTwoHanded(item, type, req); + } + if (typeof user.markModified === 'function') { + user.markModified('items.gear.' + type); + } + } + return typeof cb === 'function' ? cb((message ? { + code: 200, + message: message + } : null), user.items) : void 0; +}; diff --git a/common/script/ops/feed.js b/common/script/ops/feed.js new file mode 100644 index 0000000000..c5b0b39271 --- /dev/null +++ b/common/script/ops/feed.js @@ -0,0 +1,78 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var egg, eggText, evolve, food, message, pet, petDisplayName, potion, potionText, ref, ref1, ref2, userPets; + ref = req.params, pet = ref.pet, food = ref.food; + food = content.food[food]; + ref1 = pet.split('-'), egg = ref1[0], potion = ref1[1]; + userPets = user.items.pets; + potionText = content.hatchingPotions[potion] ? content.hatchingPotions[potion].text() : potion; + eggText = content.eggs[egg] ? content.eggs[egg].text() : egg; + petDisplayName = i18n.t('petName', { + potion: potionText, + egg: eggText + }); + if (!userPets[pet]) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messagePetNotFound', req.language) + }) : void 0; + } + if (!((ref2 = user.items.food) !== null ? ref2[food.key] : void 0)) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageFoodNotFound', req.language) + }) : void 0; + } + if (content.specialPets[pet]) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('messageCannotFeedPet', req.language) + }) : void 0; + } + if (user.items.mounts[pet]) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('messageAlreadyMount', req.language) + }) : void 0; + } + message = ''; + evolve = function () { + userPets[pet] = -1; + user.items.mounts[pet] = true; + if (pet === user.items.currentPet) { + user.items.currentPet = ''; + } + return message = i18n.t('messageEvolve', { + egg: petDisplayName + }, req.language); + }; + if (food.key === 'Saddle') { + evolve(); + } else { + if (food.target === potion || content.hatchingPotions[potion].premium) { + userPets[pet] += 5; + message = i18n.t('messageLikesFood', { + egg: petDisplayName, + foodText: food.text(req.language) + }, req.language); + } else { + userPets[pet] += 2; + message = i18n.t('messageDontEnjoyFood', { + egg: petDisplayName, + foodText: food.text(req.language) + }, req.language); + } + if (userPets[pet] >= 50 && !user.items.mounts[pet]) { + evolve(); + } + } + user.items.food[food.key]--; + return typeof cb === 'function' ? cb({ + code: 200, + message: message + }, { + value: userPets[pet] + }) : void 0; +}; diff --git a/common/script/ops/getTag.js b/common/script/ops/getTag.js new file mode 100644 index 0000000000..8380ef5296 --- /dev/null +++ b/common/script/ops/getTag.js @@ -0,0 +1,17 @@ +import _ from 'lodash'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var i, tid; + tid = req.params.id; + i = _.findIndex(user.tags, { + id: tid + }); + if (!~i) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTagNotFound', req.language) + }) : void 0; + } + return typeof cb === 'function' ? cb(null, user.tags[i]) : void 0; +}; diff --git a/common/script/ops/getTags.js b/common/script/ops/getTags.js new file mode 100644 index 0000000000..bc05d1109e --- /dev/null +++ b/common/script/ops/getTags.js @@ -0,0 +1,3 @@ +module.exports = function (user, req, cb) { + return typeof cb === 'function' ? cb(null, user.tags) : void 0; +}; diff --git a/common/script/ops/hatch.js b/common/script/ops/hatch.js new file mode 100644 index 0000000000..40fa13ee4b --- /dev/null +++ b/common/script/ops/hatch.js @@ -0,0 +1,39 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var egg, hatchingPotion, pet, ref; + ref = req.params, egg = ref.egg, hatchingPotion = ref.hatchingPotion; + if (!(egg && hatchingPotion)) { + return typeof cb === 'function' ? cb({ + code: 400, + message: 'Please specify query.egg & query.hatchingPotion' + }) : void 0; + } + if (!(user.items.eggs[egg] > 0 && user.items.hatchingPotions[hatchingPotion] > 0)) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('messageMissingEggPotion', req.language) + }) : void 0; + } + if (content.hatchingPotions[hatchingPotion].premium && !content.dropEggs[egg]) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('messageInvalidEggPotionCombo', req.language) + }) : void 0; + } + pet = egg + '-' + hatchingPotion; + if (user.items.pets[pet] && user.items.pets[pet] > 0) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('messageAlreadyPet', req.language) + }) : void 0; + } + user.items.pets[pet] = 5; + user.items.eggs[egg]--; + user.items.hatchingPotions[hatchingPotion]--; + return typeof cb === 'function' ? cb({ + code: 200, + message: i18n.t('messageHatched', req.language) + }, user.items) : void 0; +}; diff --git a/common/script/ops/hourglassPurchase.js b/common/script/ops/hourglassPurchase.js new file mode 100644 index 0000000000..99f17b0e08 --- /dev/null +++ b/common/script/ops/hourglassPurchase.js @@ -0,0 +1,54 @@ +import content from '../content/index'; +import i18n from '../i18n'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, key, ref, type; + ref = req.params, type = ref.type, key = ref.key; + if (!content.timeTravelStable[type]) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('typeNotAllowedHourglass', req.language) + JSON.stringify(_.keys(content.timeTravelStable)) + }) : void 0; + } + if (!_.contains(_.keys(content.timeTravelStable[type]), key)) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t(type + 'NotAllowedHourglass', req.language) + }) : void 0; + } + if (user.items[type][key]) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t(type + 'AlreadyOwned', req.language) + }) : void 0; + } + if (!(user.purchased.plan.consecutive.trinkets > 0)) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('notEnoughHourglasses', req.language) + }) : void 0; + } + user.purchased.plan.consecutive.trinkets--; + if (type === 'pets') { + user.items.pets[key] = 5; + } + if (type === 'mounts') { + user.items.mounts[key] = true; + } + analyticsData = { + uuid: user._id, + itemKey: key, + itemType: type, + acquireMethod: 'Hourglass', + category: 'behavior' + }; + if (analytics) { + analytics.track('acquire item', analyticsData); + } + return typeof cb === 'function' ? cb({ + code: 200, + message: i18n.t('hourglassPurchase', req.language) + }, _.pick(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0; +}; diff --git a/common/script/ops/index.js b/common/script/ops/index.js index e3e58316b9..1bfd3d55f2 100644 --- a/common/script/ops/index.js +++ b/common/script/ops/index.js @@ -1,5 +1,93 @@ +import update from './update'; +import sleep from './sleep'; +import revive from './revive'; +import reset from './reset'; +import reroll from './reroll'; +import rebirth from './rebirth'; +import allocateNow from './allocateNow'; +import clearCompleted from './clearCompleted'; +import sortTask from './sortTask'; +import updateTask from './updateTask'; +import deleteTask from './deleteTask'; +import addTask from './addTask'; +import addTag from './addTag'; +import sortTag from './sortTag'; +import getTags from './getTags'; +import getTag from './getTag'; +import updateTag from './updateTag'; +import deleteTag from './deleteTag'; +import addWebhook from './addWebhook'; +import updateWebhook from './updateWebhook'; +import deleteWebhook from './deleteWebhook'; +import addPushDevice from './addPushDevice'; +import clearPMs from './clearPMs'; +import deletePM from './deletePM'; +import blockUser from './blockUser'; +import feed from './feed'; +import buySpecialSpell from './buySpecialSpell'; +import purchase from './purchase'; +import releasePets from './releasePets'; +import releaseMounts from './releaseMounts'; +import releaseBoth from './releaseBoth'; +import buy from './buy'; +import buyQuest from './buyQuest'; +import buyMysterySet from './buyMysterySet'; +import hourglassPurchase from './hourglassPurchase'; +import sell from './sell'; +import equip from './equip'; +import hatch from './hatch'; +import unlock from './unlock'; +import changeClass from './changeClass'; +import disableClasses from './disableClasses'; +import allocate from './allocate'; import readCard from './readCard'; +import openMysteryItem from './openMysteryItem'; +import score from './score'; module.exports = { + update, + sleep, + revive, + reset, + reroll, + rebirth, + allocateNow, + clearCompleted, + sortTask, + updateTask, + deleteTask, + addTask, + addTag, + sortTag, + getTags, + getTag, + updateTag, + deleteTag, + addWebhook, + updateWebhook, + deleteWebhook, + addPushDevice, + clearPMs, + deletePM, + blockUser, + feed, + buySpecialSpell, + purchase, + releasePets, + releaseMounts, + releaseBoth, + buy, + buyQuest, + buyMysterySet, + hourglassPurchase, + sell, + equip, + hatch, + unlock, + changeClass, + disableClasses, + allocate, readCard, + openMysteryItem, + score, }; diff --git a/common/script/ops/openMysteryItem.js b/common/script/ops/openMysteryItem.js new file mode 100644 index 0000000000..9536a206f5 --- /dev/null +++ b/common/script/ops/openMysteryItem.js @@ -0,0 +1,32 @@ +import content from '../content/index'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, item, ref, ref1; + item = (ref = user.purchased.plan) !== null ? (ref1 = ref.mysteryItems) !== null ? ref1.shift() : void 0 : void 0; + if (!item) { + return typeof cb === 'function' ? cb({ + code: 400, + message: 'Empty' + }) : void 0; + } + item = content.gear.flat[item]; + user.items.gear.owned[item.key] = true; + if (typeof user.markModified === 'function') { + user.markModified('purchased.plan.mysteryItems'); + } + item.notificationType = 'Mystery'; + analyticsData = { + uuid: user._id, + itemKey: item, + itemType: 'Subscriber Gear', + acquireMethod: 'Subscriber', + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('open mystery item', analyticsData); + } + if (typeof window !== 'undefined') { + (user._tmp !== null ? user._tmp : user._tmp = {}).drop = item; + } + return typeof cb === 'function' ? cb(null, user.items.gear.owned) : void 0; +}; diff --git a/common/script/ops/purchase.js b/common/script/ops/purchase.js new file mode 100644 index 0000000000..09e24b24fc --- /dev/null +++ b/common/script/ops/purchase.js @@ -0,0 +1,107 @@ +import content from '../content/index'; +import i18n from '../i18n'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; +import planGemLimits from '../libs/planGemLimits'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, convCap, convRate, item, key, price, ref, ref1, ref2, ref3, type; + ref = req.params, type = ref.type, key = ref.key; + if (type === 'gems' && key === 'gem') { + ref1 = planGemLimits, convRate = ref1.convRate, convCap = ref1.convCap; + convCap += user.purchased.plan.consecutive.gemCapExtra; + if (!((ref2 = user.purchased) !== null ? (ref3 = ref2.plan) !== null ? ref3.customerId : void 0 : void 0)) { + return typeof cb === 'function' ? cb({ + code: 401, + message: 'Must subscribe to purchase gems with GP' + }, req) : void 0; + } + if (!(user.stats.gp >= convRate)) { + return typeof cb === 'function' ? cb({ + code: 401, + message: 'Not enough Gold' + }) : void 0; + } + if (user.purchased.plan.gemsBought >= convCap) { + return typeof cb === 'function' ? cb({ + code: 401, + message: 'You\'ve reached the Gold=>Gem conversion cap (' + convCap + ') for this month. We have this to prevent abuse / farming. The cap will reset within the first three days of next month.' + }) : void 0; + } + user.balance += .25; + user.purchased.plan.gemsBought++; + user.stats.gp -= convRate; + analyticsData = { + uuid: user._id, + itemKey: key, + acquireMethod: 'Gold', + goldCost: convRate, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('purchase gems', analyticsData); + } + return typeof cb === 'function' ? cb({ + code: 200, + message: '+1 Gem' + }, _.pick(user, splitWhitespace('stats balance'))) : void 0; + } + if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food' && type !== 'quests' && type !== 'gear') { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':type must be in [eggs,hatchingPotions,food,quests,gear]' + }, req) : void 0; + } + if (type === 'gear') { + item = content.gear.flat[key]; + if (user.items.gear.owned[key]) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('alreadyHave', req.language) + }) : void 0; + } + price = (item.twoHanded || item.gearSet === 'animal' ? 2 : 1) / 4; + } else { + item = content[type][key]; + price = item.value / 4; + } + if (!item) { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':key not found for Content.' + type + }, req) : void 0; + } + if (!item.canBuy(user)) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('messageNotAvailable', req.language) + }) : void 0; + } + if ((user.balance < price) || !user.balance) { + return typeof cb === 'function' ? cb({ + code: 403, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } + user.balance -= price; + if (type === 'gear') { + user.items.gear.owned[key] = true; + } else { + if (!(user.items[type][key] > 0)) { + user.items[type][key] = 0; + } + user.items[type][key]++; + } + analyticsData = { + uuid: user._id, + itemKey: key, + itemType: 'Market', + acquireMethod: 'Gems', + gemCost: item.value, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('acquire item', analyticsData); + } + return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('items balance'))) : void 0; +}; diff --git a/common/script/ops/rebirth.js b/common/script/ops/rebirth.js new file mode 100644 index 0000000000..1370fce696 --- /dev/null +++ b/common/script/ops/rebirth.js @@ -0,0 +1,100 @@ +import content from '../content/index'; +import i18n from '../i18n'; +import _ from 'lodash'; +import { capByLevel } from '../statHelpers'; +import { MAX_LEVEL } from '../constants'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, flags, gear, lvl, stats; + if (user.balance < 2 && user.stats.lvl < MAX_LEVEL) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } + analyticsData = { + uuid: user._id, + category: 'behavior' + }; + if (user.stats.lvl < MAX_LEVEL) { + user.balance -= 2; + analyticsData.acquireMethod = 'Gems'; + analyticsData.gemCost = 8; + } else { + analyticsData.gemCost = 0; + analyticsData.acquireMethod = '> 100'; + } + if (analytics !== null) { + analytics.track('Rebirth', analyticsData); + } + lvl = capByLevel(user.stats.lvl); + _.each(user.tasks, function (task) { + if (task.type !== 'reward') { + task.value = 0; + } + if (task.type === 'daily') { + return task.streak = 0; + } + }); + stats = user.stats; + stats.buffs = {}; + stats.hp = 50; + stats.lvl = 1; + stats['class'] = 'warrior'; + _.each(['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'], function (value) { + return stats[value] = 0; + }); + // TODO during refactoring: move all gear code from rebirth() to its own function and then call it in reset() as well + gear = user.items.gear; + _.each(['equipped', 'costume'], function (type) { + gear[type] = {}; + gear[type].armor = 'armor_base_0'; + gear[type].weapon = 'weapon_warrior_0'; + gear[type].head = 'head_base_0'; + return gear[type].shield = 'shield_base_0'; + }); + if (user.items.currentPet) { + user.ops.equip({ + params: { + type: 'pet', + key: user.items.currentPet + } + }); + } + if (user.items.currentMount) { + user.ops.equip({ + params: { + type: 'mount', + key: user.items.currentMount + } + }); + } + _.each(gear.owned, function (v, k) { + if (gear.owned[k] && content.gear.flat[k].value) { + gear.owned[k] = false; + return true; + } + }); + gear.owned.weapon_warrior_0 = true; + if (typeof user.markModified === 'function') { + user.markModified('items.gear.owned'); + } + user.preferences.costume = false; + flags = user.flags; + if (!user.achievements.beastMaster) { + flags.rebirthEnabled = false; + } + flags.itemsEnabled = false; + flags.dropsEnabled = false; + flags.classSelected = false; + flags.levelDrops = {}; + if (!user.achievements.rebirths) { + user.achievements.rebirths = 1; + user.achievements.rebirthLevel = lvl; + } else if (lvl > user.achievements.rebirthLevel || lvl === 100) { + user.achievements.rebirths++; + user.achievements.rebirthLevel = lvl; + } + user.stats.buffs = {}; + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/releaseBoth.js b/common/script/ops/releaseBoth.js new file mode 100644 index 0000000000..08d6a7226e --- /dev/null +++ b/common/script/ops/releaseBoth.js @@ -0,0 +1,50 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, animal, giveTriadBingo; + if (user.balance < 1.5 && !user.achievements.triadBingo) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } else { + giveTriadBingo = true; + if (!user.achievements.triadBingo) { + analyticsData = { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 6, + category: 'behavior' + }; + if (typeof analytics !== 'undefined' && analytics !== null) { + analytics.track('release pets & mounts', analyticsData); + } + user.balance -= 1.5; + } + user.items.currentMount = ''; + user.items.currentPet = ''; + for (animal in content.pets) { + if (user.items.pets[animal] === -1) { + giveTriadBingo = false; + } + user.items.pets[animal] = 0; + user.items.mounts[animal] = null; + } + if (!user.achievements.beastMasterCount) { + user.achievements.beastMasterCount = 0; + } + user.achievements.beastMasterCount++; + if (!user.achievements.mountMasterCount) { + user.achievements.mountMasterCount = 0; + } + user.achievements.mountMasterCount++; + if (giveTriadBingo) { + if (!user.achievements.triadBingoCount) { + user.achievements.triadBingoCount = 0; + } + user.achievements.triadBingoCount++; + } + } + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/releaseMounts.js b/common/script/ops/releaseMounts.js new file mode 100644 index 0000000000..1cd4b1cb93 --- /dev/null +++ b/common/script/ops/releaseMounts.js @@ -0,0 +1,32 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, mount; + if (user.balance < 1) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } else { + user.balance -= 1; + user.items.currentMount = ''; + for (mount in content.pets) { + user.items.mounts[mount] = null; + } + if (!user.achievements.mountMasterCount) { + user.achievements.mountMasterCount = 0; + } + user.achievements.mountMasterCount++; + } + analyticsData = { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 4, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('release mounts', analyticsData); + } + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/releasePets.js b/common/script/ops/releasePets.js new file mode 100644 index 0000000000..f26bbd8492 --- /dev/null +++ b/common/script/ops/releasePets.js @@ -0,0 +1,32 @@ +import content from '../content/index'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, pet; + if (user.balance < 1) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } else { + user.balance -= 1; + for (pet in content.pets) { + user.items.pets[pet] = 0; + } + if (!user.achievements.beastMasterCount) { + user.achievements.beastMasterCount = 0; + } + user.achievements.beastMasterCount++; + user.items.currentPet = ''; + } + analyticsData = { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 4, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('release pets', analyticsData); + } + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/reroll.js b/common/script/ops/reroll.js new file mode 100644 index 0000000000..23f438abd8 --- /dev/null +++ b/common/script/ops/reroll.js @@ -0,0 +1,29 @@ +import i18n from '../i18n'; +import _ from 'lodash'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData; + if (user.balance < 1) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } + user.balance--; + _.each(user.tasks, function (task) { + if (task.type !== 'reward') { + return task.value = 0; + } + }); + user.stats.hp = 50; + analyticsData = { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 4, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('Fortify Potion', analyticsData); + } + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/reset.js b/common/script/ops/reset.js new file mode 100644 index 0000000000..dbc0cc5b44 --- /dev/null +++ b/common/script/ops/reset.js @@ -0,0 +1,35 @@ +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + var gear; + user.habits = []; + user.dailys = []; + user.todos = []; + user.rewards = []; + user.stats.hp = 50; + user.stats.lvl = 1; + user.stats.gp = 0; + user.stats.exp = 0; + gear = user.items.gear; + _.each(['equipped', 'costume'], function (type) { + gear[type].armor = 'armor_base_0'; + gear[type].weapon = 'weapon_base_0'; + gear[type].head = 'head_base_0'; + return gear[type].shield = 'shield_base_0'; + }); + if (typeof gear.owned === 'undefined') { + gear.owned = {}; + } + _.each(gear.owned, function (v, k) { + if (gear.owned[k]) { + gear.owned[k] = false; + } + return true; + }); + gear.owned.weapon_warrior_0 = true; + if (typeof user.markModified === 'function') { + user.markModified('items.gear.owned'); + } + user.preferences.costume = false; + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/revive.js b/common/script/ops/revive.js new file mode 100644 index 0000000000..f0a2b7bb08 --- /dev/null +++ b/common/script/ops/revive.js @@ -0,0 +1,72 @@ +import content from '../content/index'; +import i18n from '../i18n'; +import _ from 'lodash'; + +module.exports = function (user, req, cb, analytics) { + var analyticsData, base, cl, gearOwned, item, losableItems, lostItem, lostStat; + if (!(user.stats.hp <= 0)) { + return typeof cb === 'function' ? cb({ + code: 400, + message: 'Cannot revive if not dead' + }) : void 0; + } + _.merge(user.stats, { + hp: 50, + exp: 0, + gp: 0 + }); + if (user.stats.lvl > 1) { + user.stats.lvl--; + } + lostStat = user.fns.randomVal(_.reduce(['str', 'con', 'per', 'int'], (function (m, k) { + if (user.stats[k]) { + m[k] = k; + } + return m; + }), {})); + if (lostStat) { + user.stats[lostStat]--; + } + cl = user.stats['class']; + gearOwned = (typeof (base = user.items.gear.owned).toObject === 'function' ? base.toObject() : void 0) || user.items.gear.owned; + losableItems = {}; + _.each(gearOwned, function (v, k) { + var itm; + if (v) { + itm = content.gear.flat['' + k]; + if (itm) { + if ((itm.value > 0 || k === 'weapon_warrior_0') && (itm.klass === cl || (itm.klass === 'special' && (!itm.specialClass || itm.specialClass === cl)) || itm.klass === 'armoire')) { + return losableItems['' + k] = '' + k; + } + } + } + }); + lostItem = user.fns.randomVal(losableItems); + if (item = content.gear.flat[lostItem]) { + user.items.gear.owned[lostItem] = false; + if (user.items.gear.equipped[item.type] === lostItem) { + user.items.gear.equipped[item.type] = item.type + '_base_0'; + } + if (user.items.gear.costume[item.type] === lostItem) { + user.items.gear.costume[item.type] = item.type + '_base_0'; + } + } + if (typeof user.markModified === 'function') { + user.markModified('items.gear'); + } + analyticsData = { + uuid: user._id, + lostItem: lostItem, + gaLabel: lostItem, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('Death', analyticsData); + } + return typeof cb === 'function' ? cb((item ? { + code: 200, + message: i18n.t('messageLostItem', { + itemText: item.text(req.language) + }, req.language) + } : null), user) : void 0; +}; diff --git a/common/script/ops/score.js b/common/script/ops/score.js new file mode 100644 index 0000000000..75f473a24f --- /dev/null +++ b/common/script/ops/score.js @@ -0,0 +1,221 @@ +import moment from 'moment'; +import _ from 'lodash'; +import i18n from '../i18n'; + +module.exports = function (user, req, cb) { + var addPoints, calculateDelta, calculateReverseDelta, changeTaskValue, delta, direction, gainMP, id, multiplier, num, options, ref, stats, subtractPoints, task, th; + ref = req.params, id = ref.id, direction = ref.direction; + task = user.tasks[id]; + options = req.query || {}; + _.defaults(options, { + times: 1, + cron: false + }); + user._tmp = {}; + 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 typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('messageNotEnoughGold', req.language) + }) : void 0; + } + delta = 0; + calculateDelta = function () { + var currVal, nextDelta, ref1; + 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 (((ref1 = task.checklist) !== null ? ref1.length : void 0) > 0) { + if (direction === 'down' && task.type === 'daily' && options.cron) { + nextDelta *= 1 - _.reduce(task.checklist, (function (m, i) { + return m + (i.completed ? 1 : 0); + }), 0) / task.checklist.length; + } + if (task.type === 'todo') { + nextDelta *= 1 + _.reduce(task.checklist, (function (m, i) { + return m + (i.completed ? 1 : 0); + }), 0); + } + } + return nextDelta; + }; + calculateReverseDelta = function () { + var calc, closeEnough, currVal, diff, nextDelta, ref1, testVal; + currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value; + testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); + closeEnough = 0.00001; + while (true) { + calc = testVal + Math.pow(0.9747, testVal); + diff = currVal - calc; + if (Math.abs(diff) < closeEnough) { + break; + } + if (diff > 0) { + testVal -= diff; + } else { + testVal += diff; + } + } + nextDelta = testVal - currVal; + if (((ref1 = task.checklist) !== null ? ref1.length : void 0) > 0) { + if (task.type === 'todo') { + nextDelta *= 1 + _.reduce(task.checklist, (function (m, i) { + return m + (i.completed ? 1 : 0); + }), 0); + } + } + return nextDelta; + }; + changeTaskValue = function () { + return _.times(options.times, function () { + var nextDelta, ref1; + nextDelta = !options.cron && direction === 'down' ? calculateReverseDelta() : calculateDelta(); + if (task.type !== 'reward') { + if (user.preferences.automaticAllocation === true && user.preferences.allocationMode === 'taskbased' && !(task.type === 'todo' && direction === 'down')) { + user.stats.training[task.attribute] += nextDelta; + } + if (direction === 'up') { + user.party.quest.progress.up = user.party.quest.progress.up || 0; + if ((ref1 = task.type) === 'daily' || ref1 === 'todo') { + user.party.quest.progress.up += nextDelta * (1 + (user._statsComputed.str / 200)); + } + if (task.type === 'habit') { + user.party.quest.progress.up += nextDelta * (0.5 + (user._statsComputed.str / 400)); + } + } + task.value += nextDelta; + } + return delta += nextDelta; + }); + }; + addPoints = function () { + var _crit, afterStreak, currStreak, gpMod, intBonus, perBonus, streakBonus; + _crit = (delta > 0 ? user.fns.crit() : 1); + if (_crit > 1) { + user._tmp.crit = _crit; + } + intBonus = 1 + (user._statsComputed.int * .025); + stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6); + perBonus = 1 + user._statsComputed.per * .02; + gpMod = delta * task.priority * _crit * perBonus; + return stats.gp += task.streak ? (currStreak = direction === 'down' ? task.streak - 1 : task.streak, streakBonus = currStreak / 100 + 1, afterStreak = gpMod * streakBonus, currStreak > 0 ? gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0 : void 0, afterStreak) : gpMod; + }; + subtractPoints = function () { + var conBonus, hpMod; + conBonus = 1 - (user._statsComputed.con / 250); + if (conBonus < .1) { + conBonus = 0.1; + } + hpMod = delta * conBonus * task.priority * 2; + return stats.hp += Math.round(hpMod * 10) / 10; + }; + gainMP = function (delta) { + delta *= user._tmp.crit || 1; + user.stats.mp += delta; + if (user.stats.mp >= user._statsComputed.maxMP) { + user.stats.mp = user._statsComputed.maxMP; + } + if (user.stats.mp < 0) { + return user.stats.mp = 0; + } + }; + switch (task.type) { + case 'habit': + changeTaskValue(); + if (delta > 0) { + addPoints(); + } else { + subtractPoints(); + } + gainMP(_.max([0.25, .0025 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1)); + th = (task.history !== null ? task.history : task.history = []); + if (th[th.length - 1] && moment(th[th.length - 1].date).isSame(new Date, 'day')) { + th[th.length - 1].value = task.value; + } else { + th.push({ + date: +(new Date), + value: task.value + }); + } + if (typeof user.markModified === 'function') { + user.markModified('habits.' + (_.findIndex(user.habits, { + id: task.id + })) + '.history'); + } + break; + case 'daily': + if (options.cron) { + changeTaskValue(); + subtractPoints(); + if (!user.stats.buffs.streaks) { + task.streak = 0; + } + } else { + changeTaskValue(); + if (direction === 'down') { + delta = calculateDelta(); + } + addPoints(); + gainMP(_.max([1, .01 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1)); + if (direction === 'up') { + 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 ((task.streak % 21) === 0) { + user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0; + } + task.streak = task.streak ? task.streak - 1 : 0; + } + } + break; + case 'todo': + if (options.cron) { + changeTaskValue(); + } else { + task.dateCompleted = direction === 'up' ? new Date : void 0; + changeTaskValue(); + if (direction === 'down') { + delta = calculateDelta(); + } + addPoints(); + multiplier = _.max([ + _.reduce(task.checklist, (function (m, i) { + return m + (i.completed ? 1 : 0); + }), 1), 1 + ]); + gainMP(_.max([multiplier, .01 * user._statsComputed.maxMP * multiplier]) * (direction === 'down' ? -1 : 1)); + } + break; + case 'reward': + changeTaskValue(); + stats.gp -= Math.abs(task.value); + num = parseFloat(task.value).toFixed(2); + if (stats.gp < 0) { + stats.hp += stats.gp; + stats.gp = 0; + } + } + user.fns.updateStats(stats, req); + if (typeof window === 'undefined') { + if (direction === 'up') { + user.fns.randomDrop({ + task: task, + delta: delta + }, req); + } + } + if (typeof cb === 'function') { + cb(null, user); + } + return delta; +}; diff --git a/common/script/ops/sell.js b/common/script/ops/sell.js new file mode 100644 index 0000000000..b41846554a --- /dev/null +++ b/common/script/ops/sell.js @@ -0,0 +1,23 @@ +import content from '../content/index'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function (user, req, cb) { + var key, ref, type; + ref = req.params, key = ref.key, type = ref.type; + if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food') { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':type not found. Must bes in [eggs, hatchingPotions, food]' + }) : void 0; + } + if (!user.items[type][key]) { + return typeof cb === 'function' ? cb({ + code: 404, + message: ':key not found for user.items.' + type + }) : void 0; + } + user.items[type][key]--; + user.stats.gp += content[type][key].value; + return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('stats items'))) : void 0; +}; diff --git a/common/script/ops/sleep.js b/common/script/ops/sleep.js new file mode 100644 index 0000000000..c8ecc3d1ab --- /dev/null +++ b/common/script/ops/sleep.js @@ -0,0 +1,4 @@ +module.exports = function (user, req, cb) { + user.preferences.sleep = !user.preferences.sleep; + return typeof cb === 'function' ? cb(null, {}) : void 0; +}; diff --git a/common/script/ops/sortTag.js b/common/script/ops/sortTag.js new file mode 100644 index 0000000000..77cc0be7a4 --- /dev/null +++ b/common/script/ops/sortTag.js @@ -0,0 +1,9 @@ +module.exports = function (user, req, cb) { + var from, ref, to; + ref = req.query, to = ref.to, from = ref.from; + if (!((to !== null) && (from !== null))) { + return typeof cb === 'function' ? cb('?to=__&from=__ are required') : void 0; + } + user.tags.splice(to, 0, user.tags.splice(from, 1)[0]); + return typeof cb === 'function' ? cb(null, user.tags) : void 0; +}; diff --git a/common/script/ops/sortTask.js b/common/script/ops/sortTask.js new file mode 100644 index 0000000000..bed1758aa7 --- /dev/null +++ b/common/script/ops/sortTask.js @@ -0,0 +1,39 @@ +import i18n from '../i18n'; +import preenTodos from '../libs/preenTodos'; + +module.exports = function (user, req, cb) { + var from, id, movedTask, preenedTasks, ref, task, tasks, to; + id = req.params.id; + ref = req.query, to = ref.to, from = ref.from; + task = user.tasks[id]; + if (!task) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTaskNotFound', req.language) + }) : void 0; + } + if (!((to !== null) && (from !== null))) { + return typeof cb === 'function' ? cb('?to=__&from=__ are required') : void 0; + } + tasks = user[task.type + 's']; + if (task.type === 'todo' && tasks[from] !== task) { + preenedTasks = preenTodos(tasks); + if (to !== -1) { + to = tasks.indexOf(preenedTasks[to]); + } + from = tasks.indexOf(preenedTasks[from]); + } + if (tasks[from] !== task) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTaskNotFound', req.language) + }) : void 0; + } + movedTask = tasks.splice(from, 1)[0]; + if (to === -1) { + tasks.push(movedTask); + } else { + tasks.splice(to, 0, movedTask); + } + return typeof cb === 'function' ? cb(null, tasks) : void 0; +}; diff --git a/common/script/ops/unlock.js b/common/script/ops/unlock.js new file mode 100644 index 0000000000..4d3adda2ca --- /dev/null +++ b/common/script/ops/unlock.js @@ -0,0 +1,63 @@ +import i18n from '../i18n'; +import _ from 'lodash'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function (user, req, cb, analytics) { + var alreadyOwns, analyticsData, cost, fullSet, k, path, split, v; + path = req.query.path; + fullSet = ~path.indexOf(','); + cost = ~path.indexOf('background.') ? fullSet ? 3.75 : 1.75 : fullSet ? 1.25 : 0.5; + alreadyOwns = !fullSet && user.fns.dotGet('purchased.' + path) === true; + if ((user.balance < cost || !user.balance) && !alreadyOwns) { + return typeof cb === 'function' ? cb({ + code: 401, + message: i18n.t('notEnoughGems', req.language) + }) : void 0; + } + if (fullSet) { + _.each(path.split(','), function (p) { + if (~path.indexOf('gear.')) { + user.fns.dotSet('' + p, true); + true; + } else { + + } + user.fns.dotSet('purchased.' + p, true); + return true; + }); + } else { + if (alreadyOwns) { + split = path.split('.'); + v = split.pop(); + k = split.join('.'); + if (k === 'background' && v === user.preferences.background) { + v = ''; + } + user.fns.dotSet('preferences.' + k, v); + return typeof cb === 'function' ? cb(null, req) : void 0; + } + user.fns.dotSet('purchased.' + path, true); + } + user.balance -= cost; + if (~path.indexOf('gear.')) { + if (typeof user.markModified === 'function') { + user.markModified('gear.owned'); + } + } else { + if (typeof user.markModified === 'function') { + user.markModified('purchased'); + } + } + analyticsData = { + uuid: user._id, + itemKey: path, + itemType: 'customization', + acquireMethod: 'Gems', + gemCost: cost / .25, + category: 'behavior' + }; + if (analytics !== null) { + analytics.track('acquire item', analyticsData); + } + return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('purchased preferences items'))) : void 0; +}; diff --git a/common/script/ops/update.js b/common/script/ops/update.js new file mode 100644 index 0000000000..963f8f6cba --- /dev/null +++ b/common/script/ops/update.js @@ -0,0 +1,9 @@ +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + _.each(req.body, function (v, k) { + user.fns.dotSet(k, v); + return true; + }); + return typeof cb === 'function' ? cb(null, user) : void 0; +}; diff --git a/common/script/ops/updateTag.js b/common/script/ops/updateTag.js new file mode 100644 index 0000000000..d1b54e51b6 --- /dev/null +++ b/common/script/ops/updateTag.js @@ -0,0 +1,18 @@ +import i18n from '../i18n'; +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + var i, tid; + tid = req.params.id; + i = _.findIndex(user.tags, { + id: tid + }); + if (!~i) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTagNotFound', req.language) + }) : void 0; + } + user.tags[i].name = req.body.name; + return typeof cb === 'function' ? cb(null, user.tags[i]) : void 0; +}; diff --git a/common/script/ops/updateTask.js b/common/script/ops/updateTask.js new file mode 100644 index 0000000000..ede672c71e --- /dev/null +++ b/common/script/ops/updateTask.js @@ -0,0 +1,20 @@ +import i18n from '../i18n'; +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + var ref, task; + if (!(task = user.tasks[(ref = req.params) !== null ? ref.id : void 0])) { + return typeof cb === 'function' ? cb({ + code: 404, + message: i18n.t('messageTaskNotFound', req.language) + }) : void 0; + } + _.merge(task, _.omit(req.body, ['checklist', 'id', 'type'])); + if (req.body.checklist) { + task.checklist = req.body.checklist; + } + if (typeof task.markModified === 'function') { + task.markModified('tags'); + } + return typeof cb === 'function' ? cb(null, task) : void 0; +}; diff --git a/common/script/ops/updateWebhook.js b/common/script/ops/updateWebhook.js new file mode 100644 index 0000000000..19d712a027 --- /dev/null +++ b/common/script/ops/updateWebhook.js @@ -0,0 +1,9 @@ +import _ from 'lodash'; + +module.exports = function (user, req, cb) { + _.merge(user.preferences.webhooks[req.params.id], req.body); + if (typeof user.markModified === 'function') { + user.markModified('preferences.webhooks'); + } + return typeof cb === 'function' ? cb(null, user.preferences.webhooks) : void 0; +}; diff --git a/test/common/user.ops.hourglassPurchase.test.js b/test/common/user.ops.hourglassPurchase.test.js index b9c7369d21..0f4cff118f 100644 --- a/test/common/user.ops.hourglassPurchase.test.js +++ b/test/common/user.ops.hourglassPurchase.test.js @@ -107,7 +107,7 @@ describe('user.ops.hourglassPurchase', () => { }); }); - it('buys a mount', (done) => { + it.only('buys a mount', (done) => { user.purchased.plan.consecutive.trinkets = 2; user.ops.hourglassPurchase({params: {type: 'mounts', key: 'MantisShrimp-Base'}}, (response) => {