API v3 [WIP] (#6144)
* Fixed more tests * Added tags into user service * Added api-v3 auth urls * v3: fix package.json * v3: fix package.json * Fixed auth tests. Updated Authctrl response * v3: remove newrelic config file in favour of env variables * v3: upgrade some deps * switch from Q to Bluebird * v3 fix tests with deferred * Removed extra consoles.log. Changed data.data to res.data * v3 fix tests and use coroutines instead of regenerator * v3: fix tests * v3: do not await a non promise * v3: q -> bluebird * Changed id param for registration response * Updated party query and create * Ensured login callback happens after user sync * Add challenges to groups. Fixed isMemberOfGuild check * Updated party and group tests * Fixed cron test * return user.id and send analytics event before changing page * fix trailing spaces * disable redirects * Api v3 party tavern fixes (#7191) * Added check if user is in party before query * Cached party query. Prevented party request when user is not in party. Updated Party create with no invites * Update tavern ctrl to use new promise * v3: misc fixes * Api v3 task fixes (#7193) * Update task view to use _id * Added try catch to user service ops calls * v3 client: saving after syncing is complete * Fixed test broken by part sync change (#7195) * v3: fix todo scoring and try to fix production testing problem * revert changes to mongoose config * mongoose: increase keepAlive * test mongoose fix * fix: Only apply captureStackTrace if it exists on the error object * v3: fix reminders with no startDate * mongoose: use options * chore(): rename website/src -> website/server and website/public -> website/client (#7199) * v3 fix GET /groups: return an error only if an invalid type is supplied not when there are 0 results (#7203) * [API v3] Fix calls to user.ops and deleting tags (#7204) * v3: fixes calls to user.ops from views and deleting tags * v3: fix tests that use user._statsComputed * Api v3 fixes continued (#7205) * Added timzeone offset back * Added APIToken back to settings page * Fixed fetch recent messages for party * Fixed returning group description * Fixed check if user is member of challenge * Fixed party members appearing in header * Updated get myGroups param to include public groups. Fixed isMemberOf group * Fixed hourglass purchase * Fixed challenge addding tasks on first creating * Updated tests to accomidate new changes * fix: Correct checklist on client Closes #7207 * fix: Pin eslint to 2.9 * minor improvements to cron code for clarity; fix inaccurate comments; add TODOs for rest-in-inn actions * fix: Add missing type param to equip call closes #7212 * rename and reword pubChalsMinPrize to reflect that it's only for Tavern challenges * allows players to send gems to each other; other minor related changes - fixes https://github.com/HabitRPG/habitrpg/issues/7227 * fix tests for /members/transfer-gems * fix: Set gems sent notification as translatable string * chore: Remove unusued variable * fix: Remove requirement on message paramter in transfer-gems * add a missing variable declaration * chore: clarify comments on cron code * fix: Correct client request from habitrpg -> tavern * update apidoc URL in package.json Closes #7222 * Fixed start party by invites * Updated spell casting to v3 * Fixed adding and removing tags on tasks * Fixed page reload on settings change * Fixed battle monsters with friends button * Loaded completed todos when done is clicked * chore: Reinstate floating version number for eslint babel-eslint regression fixed * Fixed reload tests * change "an user" to "a user" in comments and text (no code changes) (#7257) * fix: Alert user that drops were recieved * remove userServices.js from karma.conf - it's been moved to website/client/js/services * feat: Create debug update user route * fix: Correct set cron debug function * feat: Add make admin button to debug menu * lint: Add missing semicolons in test * fix: Temporarilly comment out udpate user debug route * v3: fix _tmp for crit and streakBonus * v3: execute all actions when leaving a solo party * v3 client: fix group not found when leaving party * v3 migration: fix challenge prize * v3 cron: only save modified tasks * v3: add CHALLENGE_TASK_NOT_FOUND to valid broken reasons * v3: fix tasks chart * v3 client: fix ability to leave challenge * v3 client: fix filtering by tag and correctly show tag tooltip * v3 common: fix tags tests * v3 client: support unlinking not found challenges tasks * v3: disable Bluebird warning for missing return, fixes #7269 * feat: Separate out update-user into set-cron and make-admin debug routes * chore: Disable make admin debug route for v3 prod testing * v3: misc fixes * v3: misc fixes * v3: fix adding multiple tasks * Fixed join/leave button updates * Queried only user groups to be available when creating challenges * Fixed bulk add tasks to challenge * Synced challenge tasks after leave and join. * Fixed default selected group * Fixed challenge member info. Fixed challenge winner selection * Fixed deleting challenge tasks * Fixed particiapting filter * v3 client: fix casting spells * v3: do not log sensitive data * v3: always save user when casting spell * v3: always save user when casting spell * v3: more fixes for spells * fix typos and missing information in apidocs - fixes https://github.com/HabitRPG/habitrpg/issues/7277 (#7282) * v3: add TODO for client side spells * feat: Add modify inventory debug menu * Fixed viewing user progress on challenge * Updated tests * fix: Fix quest progress button * fix incorrect Armoire test; remove unneeded param details from apidocs; disambiguate health potion * v3: fix stealth casting * v3: fix tasks saving and selection for rebirth reroll and reset (server-only) * v3: fix auto allocation * v3 client: misc fixes * rename buyPotion and buy-potion to buyHealthPotion and buy-health-potion; fix apidoc param error * Added delete for saved challenge task * Fixed member modal on front page * adjust text in apidocs for errors / clarity / consistency / standard terminology (no code changes) (#7298) * fix bug in Rebirth test, add new tests, adjust apidocs (#7293) * Updated task model to allow setting streak (#7306) * fix: Correct missing * in apidoc comments * Api v3 challenge fixes (#7287) * Fixed join/leave button updates * Queried only user groups to be available when creating challenges * Fixed bulk add tasks to challenge * Synced challenge tasks after leave and join. * Fixed default selected group * Fixed challenge member info. Fixed challenge winner selection * Fixed deleting challenge tasks * Fixed particiapting filter * Fixed viewing user progress on challenge * Updated tests * Added delete for saved challenge task * v3: fix sorting * [API v3] add CRON_SAFE_MODE (#7286) * add CRON_SAFE_MODE to example config file, fix some bugs, add an unrelated low-priority TODO * create CRON_SAFE_MODE to disable parts of cron for use after extended outage - fixes https://github.com/HabitRPG/habitrpg/issues/7161 * fix a bug with CRON_SAFE_MODE, remove duplicated code, remove completed TODO comment * fix check for CRON_SAFE_MODE * v3 client: fix typo * adjust debug menu Modify Inventory: hungrier pets, fewer Special items, "Hide" buttons * completed To-Dos: return the 30 most recent instead of 30 oldest (#7318) * v3 migration: fix createdAt date * adjust locales text, key names, and files for Rebirth, Reset, and Fortify / ReRoll for consistency with existing strings (#7321) * v3: fix unlinking multiple tasks * v3 fix releasing pets * v3: fix authenticating with apiUrl * v3: fix typo * v3 fix client tests for unlinking * v3 client: do not show start quest button when quest is active * v3 client: fix ability to send cards * v3 client: fix misc challenge issues * v3: fix notifications * v3 client: more user friendly errors * v3 client: only load completed todos once * v3 client: fix tests * v3: move TAVERN_ID to common code * fix: Provide default type and text for new task creation in score route * fix: Provide default history [] for habit in score route * fix: Add _legacyId prop to tasks to support non-uuid identifiers * chore: Change v3 migration to use _legacyId instead of legacyId * fix: check for _legacyId in tasks if id does not exist * refactor: Extract out finding task by id or _legacyId into a function * Api v3 party quest fixes (#7341) * Fix display of add challenge message when group challenges are empty * Fixed forced quest start to update quest without reload * Fixed needing to reload when accepting party invite * Fix group leave and join reload * Fixed leave current party and join another * Updated party tests * v3 client: remove console.log statement * v3: misc fixes * v3 client: fix predicatbale random * v3: info about API v3 * v3: update footer with links to developer resources * v3: support party invitation from email * v3 client: fix chat flagging * fix: Correct get tasks route to properly get todos (#7349) * move locales strings from api-v3.json to other locales files (#7347) * move locales strings from api-v3.json: authentication strings -> front.json * move locales strings from api-v3.json: authentication strings -> tasks.json * move locales strings from api-v3.json: authentication strings -> groups.json * move locales strings from api-v3.json: authentication strings -> challenge.json * move locales strings from api-v3.json: authentication strings -> groups.json (again) * move locales strings from api-v3.json: authentication strings -> quests.json * move locales strings from api-v3.json: authentication strings -> subscriber.json * move locales strings from api-v3.json: authentication strings -> spells.json * move locales strings from api-v3.json: authentication strings -> character.json * move locales strings from api-v3.json: authentication strings -> groups.json (PMs) * move locales strings from api-v3.json: authentication strings -> npc.json * move locales strings from api-v3.json: authentication strings -> pets.json * move locales strings from api-v3.json: authentication strings -> miscellaneous * move locales strings from api-v3.json: authentication strings -> contrib.json and settings.json * move locales strings from api-v3.json: delete unused string (invalidTasksOwner), delete api-v3.json, whitespace cleanup * v3 client: fix sticky header * v3: remove unused code * v3 client: correctly redirect after inviting * Removed v2 calls from views (#7351) * v3: fix tests for challenge export * v3: fallbackto authWithHeaders if wuthWithSession or authWithUrl fails * Added force cache update when fetching new messages (#7360) * v3: fetch whole user when booting from group tto avoid issues with pre save hook expecting all data * v3: misc fixes for payments * v3: limit fields of challenge tasks that can be updated * fix(tests): never connect to NODE_DB_URI for tests * Added new route for setting last cron and updated front end * v3: fix iap url * v3: fix build and ios IAP * Changed route to user set custom day start * v3: iap accessible under /api/v3, fixes to spells and groups invitations * v3: correctly use v3 routes in client * remove XP, GP when unticking a Daily with a completed checklist - fixes https://github.com/HabitRPG/habitrpg/issues/7246 * use natural language for error message about skills on challenge tasks (#7336), fix other gramatical error * Updated ui when user rejects a guild invite (#7368) * feat: complete custom day start route Closes #7363 * fix: Correct spelling of healAll skill fix: Correct sprite name of healAll skill * fix: Change all instances of spookDust -> spookySparkles * add dateCreated to all tasks; add empty challenge object to tasks that don't have one (#7386) * add plumilla to artists for Tangle Tree in Bailey message * Fixed quest drop modal (#7377) * Fixed quest drop modal * Fixed broken party test * [API v3] Maintenance Mode (#7367) * WIP(maintenance): maintenance * WIP(maintenance): working locale features * fix(maintenance): don't translate info page target * WIP(maintenance): start adding info page * fix(maintenance): linting * feat: Add container to maintenance info page * fix(maintenance): add config.json edits Also DRY variables for main vs info pages * fix(maintenance): linting * refactor(maintenance): further slim down variables * refactor: Remove unnecessary variables * fix: Correct string interpolation in maintenace view * feat: Dynamically add time to maintenance pages * maintenance mode: do not connect to mongodb * fix(maintenance): clean up timezones etc. * fix(maintenance): remove unneeded sprite * Tavern party challenges invites fix (#7394) * Added challenges and invitations to party * Loaded tavern challenges * Updated group and quest services tests * v3: implement automatic syncing if user is not up to date * Removed unnecessary fields when updating groups and challenges (#7395) * v3: do not saved populated user * v3: correctly return user subset * Chained party promises together (#7396) * v3: $w -> splitWhitespace * use bluebird * use babel polyfill * migration: fix items * update links for v3 * Updated shortname validation to support multiple browsers * Docs changes (#7401) * chore: Clarify transfer-gems documentation * chore: Clarify api status route documentation * chore: Mark webhooks as BETA * Added tags update route. Added sort to user service (#7381) * Added tags update route. Added sort to user service * Change update tasks route to reorder tasks * Fixed linting issue * Changed params for reorder tags route * Fixed not found tag and added test * Added password confirmation when deleteing account (#7402) * fix production logging * feat(commit): push * empty commit * feat(maintenance): post-downtime news & awards (#7406) * fix exporting avatar * second attempt at fixing exporting avatar * fix production logging * s3: convert moment to date instance * fix avatar sharing and caching (30 minutes) * fix: Correct missing parameter Closes #7433 * fix: Validate challenge shortname on server * adjust text strings - fixes https://github.com/HabitRPG/habitrpg/issues/5631 and also Short Name -> Tag Name
This commit is contained in:
@@ -1,20 +1,41 @@
|
||||
import _ from 'lodash';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
// TODO move to server code
|
||||
module.exports = function addPushDevice (user, req = {}) {
|
||||
let regId = _.get(req, 'body.regId');
|
||||
if (!regId) throw new BadRequest(i18n.t('regIdRequired', req.language));
|
||||
|
||||
let type = _.get(req, 'body.type');
|
||||
if (!type) throw new BadRequest(i18n.t('typeRequired', req.language));
|
||||
|
||||
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
|
||||
|
||||
let pushDevices = user.pushDevices;
|
||||
|
||||
let item = {
|
||||
regId,
|
||||
type,
|
||||
};
|
||||
i = _.findIndex(pd, {
|
||||
regId: item.regId
|
||||
|
||||
let indexOfPushDevice = _.findIndex(pushDevices, {
|
||||
regId: item.regId,
|
||||
});
|
||||
if (i === -1) {
|
||||
pd.push(item);
|
||||
|
||||
if (indexOfPushDevice !== -1) {
|
||||
throw new NotAuthorized(i18n.t('pushDeviceAlreadyAdded', req.language));
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.pushDevices) : void 0;
|
||||
|
||||
pushDevices.push(item);
|
||||
|
||||
return [
|
||||
user.pushDevices,
|
||||
i18n.t('pushDeviceAdded', req.language),
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import uuid from '../libs/uuid';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
if (user.tags == null) {
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function addTag (user, req = {}) {
|
||||
if (!user.tags) {
|
||||
user.tags = [];
|
||||
}
|
||||
|
||||
user.tags.push({
|
||||
name: req.body.name,
|
||||
id: req.body.id || uuid()
|
||||
id: _.get(req, 'body.id') || uuid(),
|
||||
});
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
|
||||
return user.tags;
|
||||
};
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
import taskDefaults from '../libs/taskDefaults';
|
||||
import i18n from '../i18n';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var task;
|
||||
task = 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);
|
||||
// TODO move to client since it's only used there?
|
||||
|
||||
module.exports = function addTask (user, req = {body: {}}) {
|
||||
let task = taskDefaults(req.body);
|
||||
user.tasksOrder[`${task.type}s`].unshift(task._id);
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
import refPush from '../libs/refPush';
|
||||
import validator from 'validator';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var wh;
|
||||
wh = user.preferences.webhooks;
|
||||
refPush(wh, {
|
||||
url: req.body.url,
|
||||
enabled: req.body.enabled || true,
|
||||
id: req.body.id
|
||||
});
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('preferences.webhooks');
|
||||
module.exports = function addWebhook (user, req = {}) {
|
||||
let wh = user.preferences.webhooks;
|
||||
|
||||
if (!validator.isURL(_.get(req, 'body.url'))) throw new BadRequest(i18n.t('invalidUrl', req.language));
|
||||
if (!validator.isBoolean(_.get(req, 'body.enabled'))) throw new BadRequest(i18n.t('invalidEnabled', req.language));
|
||||
|
||||
user.markModified('preferences.webhooks');
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.preferences.webhooks;
|
||||
} else {
|
||||
return [
|
||||
refPush(wh, {
|
||||
url: req.body.url,
|
||||
enabled: req.body.enabled,
|
||||
}),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
ATTRIBUTES,
|
||||
} from '../constants';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import i18n from '../i18n';
|
||||
|
||||
module.exports = function allocate (user, req = {}) {
|
||||
let stat = _.get(req, 'query.stat', 'str');
|
||||
|
||||
if (ATTRIBUTES.indexOf(stat) === -1) {
|
||||
throw new BadRequest(i18n.t('invalidAttribute', {attr: stat}, req.language));
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
} else {
|
||||
throw new NotAuthorized(i18n.t('notEnoughAttrPoints', req.language));
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('stats'))) : void 0;
|
||||
|
||||
return [
|
||||
user.stats,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import _ from 'lodash';
|
||||
import autoAllocate from '../fns/autoAllocate';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
_.times(user.stats.points, user.fns.autoAllocate);
|
||||
module.exports = function allocateNow (user, req = {}) {
|
||||
_.times(user.stats.points, () => autoAllocate(user));
|
||||
user.stats.points = 0;
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('stats');
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, 'stats');
|
||||
} else {
|
||||
return [
|
||||
user.stats,
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.stats) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
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 {
|
||||
import validator from 'validator';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function blockUser (user, req = {}) {
|
||||
if (!validator.isUUID(req.params.uuid)) throw new BadRequest(i18n.t('invalidUUID', req.language));
|
||||
|
||||
let i = user.inbox.blocks.indexOf(req.params.uuid);
|
||||
if (i === -1) {
|
||||
user.inbox.blocks.push(req.params.uuid);
|
||||
} else {
|
||||
user.inbox.blocks.splice(i, 1);
|
||||
}
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('inbox.blocks');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.inbox.blocks) : void 0;
|
||||
|
||||
user.markModified('inbox.blocks');
|
||||
return [
|
||||
user.inbox.blocks,
|
||||
];
|
||||
};
|
||||
|
||||
+18
-113
@@ -1,119 +1,24 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import count from '../count';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
import buyHealthPotion from './buyHealthPotion';
|
||||
import buyArmoire from './buyArmoire';
|
||||
import buyGear from './buyGear';
|
||||
|
||||
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: '<span class="shop_' + drop.key + ' pull-left"></span>',
|
||||
dropText: drop.text(req.language)
|
||||
}, req.language);
|
||||
if (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: '<span class="Pet_Food_' + drop.key + ' pull-left"></span>',
|
||||
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
|
||||
};
|
||||
}
|
||||
module.exports = function buy (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let buyRes;
|
||||
if (key === 'potion') {
|
||||
buyRes = buyHealthPotion(user, req, analytics);
|
||||
} else if (key === 'armoire') {
|
||||
buyRes = buyArmoire(user, req, analytics);
|
||||
} 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();
|
||||
}
|
||||
buyRes = buyGear(user, req, analytics);
|
||||
}
|
||||
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, splitWhitespace('items achievements stats flags'));
|
||||
if (armoireResp) {
|
||||
buyResp["armoire"] = armoireResp;
|
||||
}
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, buyResp) : void 0;
|
||||
|
||||
return buyRes;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import count from '../count';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import predictableRandom from '../fns/predictableRandom';
|
||||
import randomVal from '../fns/randomVal';
|
||||
|
||||
module.exports = function buyArmoire (user, req = {}, analytics) {
|
||||
let item = content.armoire;
|
||||
|
||||
if (user.stats.gp < item.value) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
if (item.canOwn && !item.canOwn(user)) {
|
||||
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||
}
|
||||
|
||||
let armoireResp;
|
||||
let armoireResult;
|
||||
let eligibleEquipment;
|
||||
let drop;
|
||||
let message;
|
||||
|
||||
armoireResult = predictableRandom(user, user.stats.gp);
|
||||
eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
||||
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
|
||||
});
|
||||
|
||||
if (!_.isEmpty(eligibleEquipment) && (armoireResult < 0.6 || !user.flags.armoireOpened)) {
|
||||
eligibleEquipment.sort();
|
||||
drop = randomVal(user, eligibleEquipment);
|
||||
|
||||
if (user.items.gear.owned[drop.key]) {
|
||||
throw new NotAuthorized(i18n.t('equipmentAlradyOwned', req.language));
|
||||
}
|
||||
|
||||
user.items.gear.owned[drop.key] = true;
|
||||
user.flags.armoireOpened = true;
|
||||
message = i18n.t('armoireEquipment', {
|
||||
image: `<span class="shop_${drop.key} pull-left"></span>`,
|
||||
dropText: drop.text(req.language),
|
||||
}, req.language);
|
||||
|
||||
if (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 < 0.8) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
||||
drop = randomVal(user, _.where(content.food, {
|
||||
canDrop: true,
|
||||
}));
|
||||
user.items.food[drop.key] = user.items.food[drop.key] || 0;
|
||||
user.items.food[drop.key] += 1;
|
||||
|
||||
message = i18n.t('armoireFood', {
|
||||
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
||||
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 {
|
||||
let armoireExp = Math.floor(predictableRandom(user, user.stats.exp) * 40 + 10);
|
||||
user.stats.exp += armoireExp;
|
||||
message = i18n.t('armoireExp', req.language);
|
||||
armoireResp = {
|
||||
type: 'experience',
|
||||
value: armoireExp,
|
||||
};
|
||||
}
|
||||
|
||||
user.stats.gp -= item.value;
|
||||
|
||||
if (!message) {
|
||||
message = i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language);
|
||||
}
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: 'Armoire',
|
||||
acquireMethod: 'Gold',
|
||||
goldCost: item.value,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
let resData = _.pick(user, splitWhitespace('items flags'));
|
||||
if (armoireResp) resData.armoire = armoireResp;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return resData;
|
||||
} else {
|
||||
return [
|
||||
resData,
|
||||
message,
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
import handleTwoHanded from '../fns/handleTwoHanded';
|
||||
import ultimateGear from '../fns/ultimateGear';
|
||||
|
||||
module.exports = function buyGear (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.gear.flat[key];
|
||||
|
||||
if (!item) throw new NotFound(i18n.t('itemNotFound', {key}, req.language));
|
||||
|
||||
if (user.stats.gp < item.value) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
if (item.canOwn && !item.canOwn(user)) {
|
||||
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||
}
|
||||
|
||||
let message;
|
||||
|
||||
if (user.items.gear.owned[item.key]) {
|
||||
throw new NotAuthorized(i18n.t('equipmentAlreadyOwned', req.language));
|
||||
}
|
||||
|
||||
if (user.preferences.autoEquip) {
|
||||
user.items.gear.equipped[item.type] = item.key;
|
||||
message = handleTwoHanded(user, item, undefined, req);
|
||||
}
|
||||
|
||||
user.items.gear.owned[item.key] = true;
|
||||
|
||||
if (item.last) ultimateGear(user);
|
||||
|
||||
user.stats.gp -= item.value;
|
||||
|
||||
if (!message) {
|
||||
message = i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language);
|
||||
}
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: key,
|
||||
acquireMethod: 'Gold',
|
||||
goldCost: item.value,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('items achievements stats flags'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('items achievements stats flags')),
|
||||
message,
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function buyHealthPotion (user, req = {}, analytics) {
|
||||
let item = content.potion;
|
||||
|
||||
if (user.stats.gp < item.value) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
if (item.canOwn && !item.canOwn(user)) {
|
||||
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||
}
|
||||
|
||||
user.stats.hp += 15;
|
||||
if (user.stats.hp > 50) {
|
||||
user.stats.hp = 50;
|
||||
}
|
||||
|
||||
user.stats.gp -= item.value;
|
||||
|
||||
let message = i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language);
|
||||
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: 'Potion',
|
||||
acquireMethod: 'Gold',
|
||||
goldCost: item.value,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.stats;
|
||||
} else {
|
||||
return [
|
||||
user.stats,
|
||||
message,
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -2,42 +2,54 @@ import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import pickDeep from '../libs/pickDeep';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function buyMysterySet (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
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;
|
||||
}
|
||||
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||
}
|
||||
|
||||
let ref = content.timeTravelerStore(user.items.gear.owned);
|
||||
let mysterySet = ref ? ref[key] : undefined;
|
||||
|
||||
if (!mysterySet) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: "Mystery set not found, or set already owned"
|
||||
}) : void 0;
|
||||
throw new NotFound(i18n.t('mysterySetNotFound', req.language));
|
||||
}
|
||||
_.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;
|
||||
|
||||
if (typeof window !== 'undefined' && window.confirm) { // TODO move to client
|
||||
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) return;
|
||||
}
|
||||
|
||||
_.each(mysterySet.items, item => {
|
||||
user.items.gear.owned[item.key] = true;
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: item.key,
|
||||
itemType: 'Subscriber Gear',
|
||||
acquireMethod: 'Hourglass',
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
user.purchased.plan.consecutive.trinkets--;
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: i18n.t('hourglassPurchaseSet', req.language)
|
||||
}, _.pick(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0;
|
||||
|
||||
|
||||
if (req.v2 === true) {
|
||||
return pickDeep(user, splitWhitespace('items purchased.plan.consecutive'));
|
||||
} else {
|
||||
return [
|
||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||
i18n.t('hourglassPurchaseSet', req.language),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
// buy a quest with gold
|
||||
module.exports = function buyQuest (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.quests[key];
|
||||
if (!item) throw new NotFound(i18n.t('questNotFound', {key}, req.language));
|
||||
|
||||
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;
|
||||
throw new NotAuthorized(i18n.t('questNotGoldPurchasable', {key}, req.language));
|
||||
}
|
||||
if (user.stats.gp < item.goldValue) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('messageNotEnoughGold', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
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.items.quests[item.key] = user.items.quests[item.key] || 0;
|
||||
user.items.quests[item.key]++;
|
||||
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);
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: item.key,
|
||||
itemType: 'Market',
|
||||
goldCost: item.goldValue,
|
||||
acquireMethod: 'Gold',
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.items.quests;
|
||||
} else {
|
||||
return [
|
||||
user.items.quests,
|
||||
i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, user.items.quests) : void 0;
|
||||
};
|
||||
|
||||
@@ -2,30 +2,34 @@ import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function buySpecialSpell (user, req = {}) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.special[key];
|
||||
if (!item) throw new NotFound(i18n.t('spellNotFound', {spellId: key}, req.language));
|
||||
|
||||
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;
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
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');
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('items stats'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('items stats')),
|
||||
i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language),
|
||||
];
|
||||
}
|
||||
message = i18n.t('messageBought', {
|
||||
itemText: item.text(req.language)
|
||||
}, req.language);
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, _.pick(user, splitWhitespace('items stats'))) : void 0;
|
||||
};
|
||||
|
||||
@@ -2,58 +2,77 @@ import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import { capByLevel } from '../statHelpers';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
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;
|
||||
module.exports = function changeClass (user, req = {}, analytics) {
|
||||
let klass = _.get(req, 'query.class');
|
||||
|
||||
// user.flags.classSelected is set to false after the user paid the 3 gems
|
||||
if (user.stats.lvl < 10) {
|
||||
throw new NotAuthorized(i18n.t('lvl10ChangeClass', req.language));
|
||||
} else if (!user.flags.classSelected && (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer')) {
|
||||
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;
|
||||
|
||||
_.each(['weapon', 'armor', 'shield', 'head'], (type) => {
|
||||
let foundKey = false;
|
||||
_.findLast(user.items.gear.owned, (val, key) => {
|
||||
if (key.indexOf(`${type}_${klass}`) !== -1 && val === true) {
|
||||
foundKey = key;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
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;
|
||||
|
||||
if (!foundKey) {
|
||||
if (type === 'weapon') {
|
||||
foundKey = `weapon_${klass}_0`;
|
||||
} else if (type === 'shield' && klass === 'rogue') {
|
||||
foundKey = 'shield_rogue_0';
|
||||
} else {
|
||||
foundKey = `${type}_base_0`;
|
||||
}
|
||||
}
|
||||
|
||||
user.items.gear.equipped[type] = foundKey;
|
||||
|
||||
if (type === 'weapon' || (type === 'shield' && klass === 'rogue')) { // eslint-disable-line no-extra-parens
|
||||
user.items.gear.owned[`${type}_${klass}_0`] = true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('change class', {
|
||||
uuid: user._id,
|
||||
class: klass,
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: 3,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
} 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;
|
||||
if (user.balance < 0.75) throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
user.balance -= 0.75;
|
||||
}
|
||||
_.merge(user.stats, {
|
||||
str: 0,
|
||||
con: 0,
|
||||
per: 0,
|
||||
int: 0,
|
||||
points: capByLevel(user.stats.lvl)
|
||||
});
|
||||
|
||||
user.stats.str = 0;
|
||||
user.stats.con = 0;
|
||||
user.stats.per = 0;
|
||||
user.stats.int = 0;
|
||||
user.stats.points = capByLevel(user.stats.lvl);
|
||||
user.flags.classSelected = false;
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('stats flags items preferences'))) : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('stats flags items preferences'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('stats flags items preferences')),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
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);
|
||||
// TODO move to client since it's only used there?
|
||||
// TODO rename file to clearCompletedTodos
|
||||
|
||||
module.exports = function clearCompletedTodos (todos) {
|
||||
_.remove(todos, todo => {
|
||||
return todo.completed && (!todo.challenge || !todo.challenge.id || todo.challenge.broken);
|
||||
});
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('todos');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.todos) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = function(user, req, cb) {
|
||||
module.exports = function clearPMs (user) {
|
||||
user.inbox.messages = {};
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('inbox.messages');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.inbox.messages) : void 0;
|
||||
user.markModified('inbox.messages');
|
||||
return [
|
||||
user.inbox.messages,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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;
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function deletePM (user, req = {}) {
|
||||
delete user.inbox.messages[_.get(req, 'params.id')];
|
||||
user.markModified(`inbox.messages.${req.params.id}`);
|
||||
return [
|
||||
user.inbox.messages,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import { NotFound } from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var i, tag, tid;
|
||||
tid = req.params.id;
|
||||
i = _.findIndex(user.tags, {
|
||||
id: tid
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function deleteTag (user, req = {}) {
|
||||
let tid = _.get(req, 'params.id');
|
||||
|
||||
let index = _.findIndex(user.tags, {
|
||||
id: tid,
|
||||
});
|
||||
if (!~i) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: i18n.t('messageTagNotFound', req.language)
|
||||
}) : void 0;
|
||||
|
||||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTagNotFound', req.language));
|
||||
}
|
||||
tag = user.tags[i];
|
||||
|
||||
let tag = user.tags[index];
|
||||
delete user.filters[tag.id];
|
||||
user.tags.splice(i, 1);
|
||||
_.each(user.tasks, function(task) {
|
||||
|
||||
user.tags.splice(index, 1);
|
||||
|
||||
_.each(user.tasks, (task) => {
|
||||
return delete task.tags[tag.id];
|
||||
});
|
||||
_.each(['habits', 'dailys', 'todos', 'rewards'], function(type) {
|
||||
return typeof user.markModified === "function" ? user.markModified(type) : void 0;
|
||||
|
||||
_.each(['habits', 'dailys', 'todos', 'rewards'], (type) => {
|
||||
user.markModified(type);
|
||||
});
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
|
||||
return user.tags;
|
||||
};
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import i18n from '../i18n';
|
||||
import { NotFound } from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
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;
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function deleteTask (user, req = {}) {
|
||||
let tid = _.get(req, 'params.id');
|
||||
let taskType = _.get(req, 'params.taskType');
|
||||
|
||||
let index = _.findIndex(user[`${taskType}s`], function findById (task) {
|
||||
return task._id === tid;
|
||||
});
|
||||
|
||||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTaskNotFound', req.language));
|
||||
}
|
||||
i = user[task.type + "s"].indexOf(task);
|
||||
if (~i) {
|
||||
user[task.type + "s"].splice(i, 1);
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, {}) : void 0;
|
||||
|
||||
user[`${taskType}s`].splice(index, 1);
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
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;
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function deleteWebhook (user, req) {
|
||||
delete user.preferences.webhooks[_.get(req, 'params.id')];
|
||||
user.markModified('preferences.webhooks');
|
||||
|
||||
return [
|
||||
user.preferences.webhooks,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -2,12 +2,19 @@ import splitWhitespace from '../libs/splitWhitespace';
|
||||
import { capByLevel } from '../statHelpers';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
user.stats["class"] = 'warrior';
|
||||
module.exports = function disableClasses (user, req = {}) {
|
||||
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;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('stats flags preferences'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('stats flags preferences')),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
+45
-29
@@ -1,52 +1,68 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import handleTwoHanded from '../fns/handleTwoHanded';
|
||||
import {
|
||||
NotFound,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function equip (user, req = {}) {
|
||||
// Being type a parameter followed by another parameter
|
||||
// when using the API it must be passes specifically in the URL, it's won't default to equipped
|
||||
let type = _.get(req, 'params.type', 'equipped');
|
||||
let key = _.get(req, 'params.key');
|
||||
|
||||
if (!key || !type) throw new BadRequest(i18n.t('missingTypeKeyEquip', req.language));
|
||||
if (['mount', 'pet', 'costume', 'equipped'].indexOf(type) === -1) {
|
||||
throw new BadRequest(i18n.t('invalidTypeEquip', req.language));
|
||||
}
|
||||
|
||||
let message;
|
||||
|
||||
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':
|
||||
case 'mount': {
|
||||
if (!user.items.mounts[key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":You do not own this mount."
|
||||
}) : void 0;
|
||||
throw new NotFound(i18n.t('mountNotOwned', req.language));
|
||||
}
|
||||
|
||||
user.items.currentMount = user.items.currentMount === key ? '' : key;
|
||||
break;
|
||||
case 'pet':
|
||||
}
|
||||
case 'pet': {
|
||||
if (!user.items.pets[key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":You do not own this pet."
|
||||
}) : void 0;
|
||||
throw new NotFound(i18n.t('petNotOwned', req.language));
|
||||
}
|
||||
|
||||
user.items.currentPet = user.items.currentPet === key ? '' : key;
|
||||
break;
|
||||
}
|
||||
case 'costume':
|
||||
case 'equipped':
|
||||
item = content.gear.flat[key];
|
||||
case 'equipped': {
|
||||
if (!user.items.gear.owned[key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":You do not own this gear."
|
||||
}) : void 0;
|
||||
throw new NotFound(i18n.t('gearNotOwned', req.language));
|
||||
}
|
||||
|
||||
let item = content.gear.flat[key];
|
||||
|
||||
if (user.items.gear[type][item.type] === key) {
|
||||
user.items.gear[type][item.type] = item.type + "_base_0";
|
||||
user.items.gear[type][item.type] = `${item.type}_base_0`;
|
||||
message = i18n.t('messageUnEquipped', {
|
||||
itemText: item.text(req.language)
|
||||
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);
|
||||
message = handleTwoHanded(user, item, type, req);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.items;
|
||||
} else {
|
||||
let res = [user.items];
|
||||
if (message) res.push(message);
|
||||
return res;
|
||||
}
|
||||
return typeof cb === "function" ? cb((message ? {
|
||||
code: 200,
|
||||
message: message
|
||||
} : null), user.items) : void 0;
|
||||
};
|
||||
|
||||
+74
-50
@@ -1,78 +1,102 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
|
||||
function evolve (user, pet, petDisplayName, req) {
|
||||
user.items.pets[pet] = -1;
|
||||
user.items.mounts[pet] = true;
|
||||
|
||||
if (pet === user.items.currentPet) {
|
||||
user.items.currentPet = '';
|
||||
}
|
||||
|
||||
return i18n.t('messageEvolve', {
|
||||
egg: petDisplayName,
|
||||
}, req.language);
|
||||
}
|
||||
|
||||
module.exports = function feed (user, req = {}) {
|
||||
let pet = _.get(req, 'params.pet');
|
||||
let foodK = _.get(req, 'params.food');
|
||||
|
||||
if (!pet || !foodK) throw new BadRequest(i18n.t('missingPetFoodFeed', req.language));
|
||||
|
||||
if (pet.indexOf('-') === -1) {
|
||||
throw new BadRequest(i18n.t('invalidPetName', req.language));
|
||||
}
|
||||
|
||||
let food = content.food[foodK];
|
||||
if (!food) {
|
||||
throw new NotFound(i18n.t('messageFoodNotFound', req.language));
|
||||
}
|
||||
|
||||
let userPets = user.items.pets;
|
||||
|
||||
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;
|
||||
throw new NotFound(i18n.t('messagePetNotFound', req.language));
|
||||
}
|
||||
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;
|
||||
|
||||
let [egg, potion] = pet.split('-');
|
||||
|
||||
let potionText = content.hatchingPotions[potion] ? content.hatchingPotions[potion].text(req.language) : potion;
|
||||
let eggText = content.eggs[egg] ? content.eggs[egg].text(req.language) : egg;
|
||||
|
||||
let petDisplayName = i18n.t('petName', {
|
||||
potion: potionText,
|
||||
egg: eggText,
|
||||
}, req.language);
|
||||
|
||||
if (!user.items.food[food.key]) {
|
||||
throw new NotFound(i18n.t('messageFoodNotFound', req.language));
|
||||
}
|
||||
|
||||
if (content.specialPets[pet]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('messageCannotFeedPet', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('messageCannotFeedPet', req.language));
|
||||
}
|
||||
|
||||
if (user.items.mounts[pet]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('messageAlreadyMount', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('messageAlreadyMount', req.language));
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
let message;
|
||||
|
||||
if (food.key === 'Saddle') {
|
||||
evolve();
|
||||
message = evolve(user, pet, petDisplayName, req);
|
||||
} else {
|
||||
if (food.target === potion || content.hatchingPotions[potion].premium) {
|
||||
userPets[pet] += 5;
|
||||
message = i18n.t('messageLikesFood', {
|
||||
egg: petDisplayName,
|
||||
foodText: food.text(req.language)
|
||||
foodText: food.text(req.language),
|
||||
}, req.language);
|
||||
} else {
|
||||
userPets[pet] += 2;
|
||||
message = i18n.t('messageDontEnjoyFood', {
|
||||
egg: petDisplayName,
|
||||
foodText: food.text(req.language)
|
||||
foodText: food.text(req.language),
|
||||
}, req.language);
|
||||
}
|
||||
|
||||
if (userPets[pet] >= 50 && !user.items.mounts[pet]) {
|
||||
evolve();
|
||||
message = evolve(user, pet, petDisplayName, req);
|
||||
}
|
||||
}
|
||||
|
||||
user.items.food[food.key]--;
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, {
|
||||
value: userPets[pet]
|
||||
}) : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return {
|
||||
value: userPets[pet],
|
||||
};
|
||||
} else {
|
||||
return [
|
||||
userPets[pet],
|
||||
message,
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
+12
-11
@@ -1,17 +1,18 @@
|
||||
import _ from 'lodash';
|
||||
import i18n from '../i18n';
|
||||
import { NotFound } from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var i, tid;
|
||||
tid = req.params.id;
|
||||
i = _.findIndex(user.tags, {
|
||||
id: tid
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function getTag (user, req = {}) {
|
||||
let tid = _.get(req, 'params.id');
|
||||
|
||||
let index = _.findIndex(user.tags, {
|
||||
id: tid,
|
||||
});
|
||||
if (!~i) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: i18n.t('messageTagNotFound', req.language)
|
||||
}) : void 0;
|
||||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTagNotFound', req.language));
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.tags[i]) : void 0;
|
||||
|
||||
return user.tags[index];
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
module.exports = function(user, req, cb) {
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function getTags (user) {
|
||||
return user.tags;
|
||||
};
|
||||
|
||||
+29
-24
@@ -1,39 +1,44 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function hatch (user, req = {}) {
|
||||
let egg = _.get(req, 'params.egg');
|
||||
let hatchingPotion = _.get(req, 'params.hatchingPotion');
|
||||
|
||||
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;
|
||||
throw new BadRequest(i18n.t('missingEggHatchingPotionHatch', req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
throw new NotFound(i18n.t('messageMissingEggPotion', req.language));
|
||||
}
|
||||
|
||||
if (content.hatchingPotions[hatchingPotion].premium && !content.dropEggs[egg]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('messageInvalidEggPotionCombo', req.language)
|
||||
}) : void 0;
|
||||
throw new BadRequest(i18n.t('messageInvalidEggPotionCombo', req.language));
|
||||
}
|
||||
pet = egg + "-" + hatchingPotion;
|
||||
|
||||
let 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;
|
||||
throw new NotAuthorized(i18n.t('messageAlreadyPet', req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.items;
|
||||
} else {
|
||||
return [
|
||||
user.items,
|
||||
i18n.t('messageHatched', req.language),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,54 +1,61 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
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;
|
||||
module.exports = function purchaseHourglass (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let type = _.get(req, 'params.type');
|
||||
if (!type) throw new BadRequest(i18n.t('missingTypeParam', req.language));
|
||||
|
||||
if (!content.timeTravelStable[type]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('typeNotAllowedHourglass', {allowedTypes: _.keys(content.timeTravelStable).toString()}, req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('typeNotAllowedHourglass', {allowedTypes: _.keys(content.timeTravelStable).toString()}, req.language));
|
||||
}
|
||||
|
||||
if (!_.contains(_.keys(content.timeTravelStable[type]), key)) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t(type + 'NotAllowedHourglass', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('notAllowedHourglass', req.language));
|
||||
}
|
||||
|
||||
if (user.items[type][key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t(type + 'AlreadyOwned', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t(`${type}AlreadyOwned`, req.language));
|
||||
}
|
||||
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('notEnoughHourglasses', req.language)
|
||||
}) : void 0;
|
||||
|
||||
if (user.purchased.plan.consecutive.trinkets <= 0) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: key,
|
||||
itemType: type,
|
||||
acquireMethod: 'Hourglass',
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('items purchased.plan.consecutive'));
|
||||
} else {
|
||||
return [
|
||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||
i18n.t('hourglassPurchase', req.language),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: i18n.t('hourglassPurchase', req.language)
|
||||
}, _.pick(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,9 @@ import releasePets from './releasePets';
|
||||
import releaseMounts from './releaseMounts';
|
||||
import releaseBoth from './releaseBoth';
|
||||
import buy from './buy';
|
||||
import buyGear from './buyGear';
|
||||
import buyHealthPotion from './buyHealthPotion';
|
||||
import buyArmoire from './buyArmoire';
|
||||
import buyQuest from './buyQuest';
|
||||
import buyMysterySet from './buyMysterySet';
|
||||
import hourglassPurchase from './hourglassPurchase';
|
||||
@@ -42,7 +45,9 @@ import disableClasses from './disableClasses';
|
||||
import allocate from './allocate';
|
||||
import readCard from './readCard';
|
||||
import openMysteryItem from './openMysteryItem';
|
||||
import score from './score';
|
||||
import scoreTask from './scoreTask';
|
||||
import markPmsRead from './markPMSRead';
|
||||
|
||||
|
||||
module.exports = {
|
||||
update,
|
||||
@@ -77,6 +82,9 @@ module.exports = {
|
||||
releaseMounts,
|
||||
releaseBoth,
|
||||
buy,
|
||||
buyGear,
|
||||
buyHealthPotion,
|
||||
buyArmoire,
|
||||
buyQuest,
|
||||
buyMysterySet,
|
||||
hourglassPurchase,
|
||||
@@ -89,5 +97,6 @@ module.exports = {
|
||||
allocate,
|
||||
readCard,
|
||||
openMysteryItem,
|
||||
score,
|
||||
scoreTask,
|
||||
markPmsRead,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import i18n from '../i18n';
|
||||
|
||||
module.exports = function markPmsRead (user, req = {}) {
|
||||
user.inbox.newMessages = 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
user.inbox.newMessages,
|
||||
i18n.t('pmsMarkedRead'),
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -1,32 +1,44 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function openMysteryItem (user, req = {}, analytics) {
|
||||
let item = user.purchased.plan.mysteryItems.shift();
|
||||
|
||||
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');
|
||||
throw new BadRequest(i18n.t('mysteryItemIsEmpty', req.language));
|
||||
}
|
||||
|
||||
item = _.cloneDeep(content.gear.flat[item]);
|
||||
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);
|
||||
user.items.gear.owned[item.key] = true;
|
||||
|
||||
user.markModified('purchased.plan.mysteryItems');
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('open mystery item', {
|
||||
uuid: user._id,
|
||||
itemKey: item,
|
||||
itemType: 'Subscriber Gear',
|
||||
acquireMethod: 'Subscriber',
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
(user._tmp != null ? user._tmp : user._tmp = {}).drop = item;
|
||||
if (!user._tmp) user._tmp = {};
|
||||
user._tmp.drop = item;
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.items.gear.owned;
|
||||
} else {
|
||||
return [
|
||||
user.items.gear.owned,
|
||||
i18n.t('mysteryItemOpened', req.language),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.items.gear.owned) : void 0;
|
||||
};
|
||||
|
||||
@@ -3,105 +3,125 @@ import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import planGemLimits from '../libs/planGemLimits';
|
||||
import {
|
||||
NotFound,
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function purchase (user, req = {}, analytics) {
|
||||
let type = _.get(req.params, 'type');
|
||||
let key = _.get(req.params, 'key');
|
||||
let item;
|
||||
let price;
|
||||
|
||||
if (!type) {
|
||||
throw new BadRequest(i18n.t('typeRequired', req.language));
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new BadRequest(i18n.t('keyRequired', req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
let convRate = planGemLimits.convRate;
|
||||
let convCap = planGemLimits.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.purchased || !user.purchased.plan || !user.purchased.plan.customerId) {
|
||||
throw new NotAuthorized(i18n.t('mustSubscribeToPurchaseGems', req.language));
|
||||
}
|
||||
if (!(user.stats.gp >= convRate)) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "Not enough Gold"
|
||||
}) : void 0;
|
||||
|
||||
if (user.stats.gp < convRate) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
throw new NotAuthorized(i18n.t('reachedGoldToGemCap', {convCap}, req.language));
|
||||
}
|
||||
user.balance += .25;
|
||||
|
||||
user.balance += 0.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);
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('purchase gems', {
|
||||
uuid: user._id,
|
||||
itemKey: key,
|
||||
acquireMethod: 'Gold',
|
||||
goldCost: convRate,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: "+1 Gem"
|
||||
}, _.pick(user, splitWhitespace('stats balance'))) : void 0;
|
||||
|
||||
return [
|
||||
_.pick(user, splitWhitespace('stats balance')),
|
||||
i18n.t('plusOneGem'),
|
||||
];
|
||||
}
|
||||
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;
|
||||
|
||||
let acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'quests', 'gear'];
|
||||
if (acceptedTypes.indexOf(type) === -1) {
|
||||
throw new NotFound(i18n.t('notAccteptedType', req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!item) {
|
||||
throw new NotFound(i18n.t('contentKeyNotFound', {type}, req.language));
|
||||
}
|
||||
|
||||
if (user.items.gear.owned[key]) {
|
||||
throw new NotAuthorized(i18n.t('alreadyHave', req.language));
|
||||
}
|
||||
|
||||
price = (item.twoHanded || item.gearSet === 'animal' ? 2 : 1) / 4;
|
||||
} else {
|
||||
item = content[type][key];
|
||||
|
||||
if (!item) {
|
||||
throw new NotFound(i18n.t('contentKeyNotFound', {type}, req.language));
|
||||
}
|
||||
|
||||
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;
|
||||
throw new NotAuthorized(i18n.t('messageNotAvailable', req.language));
|
||||
}
|
||||
if ((user.balance < price) || !user.balance) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('notEnoughGems', req.language)
|
||||
}) : void 0;
|
||||
|
||||
if (!user.balance || user.balance < price) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
user.balance -= price;
|
||||
|
||||
if (type === 'gear') {
|
||||
user.items.gear.owned[key] = true;
|
||||
} else {
|
||||
if (!(user.items[type][key] > 0)) {
|
||||
if (!user.items[type][key] || 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);
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: key,
|
||||
itemType: 'Market',
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: item.value,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('items balance'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('items balance')),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('items balance'))) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
module.exports = function(user, req, cb) {
|
||||
var cardType;
|
||||
cardType = req.params.cardType;
|
||||
user.items.special[cardType + "Received"].shift();
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified("items.special." + cardType + "Received");
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import _ from 'lodash';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import content from '../content/index';
|
||||
|
||||
module.exports = function readCard (user, req = {}) {
|
||||
let cardType = _.get(req.params, 'cardType');
|
||||
|
||||
if (!cardType) {
|
||||
throw new BadRequest(i18n.t('cardTypeRequired', req.language));
|
||||
}
|
||||
|
||||
if (_.keys(content.cardTypes).indexOf(cardType) === -1) {
|
||||
throw new NotAuthorized(i18n.t('cardTypeNotAllowed', req.language));
|
||||
}
|
||||
|
||||
user.items.special[`${cardType}Received`].shift();
|
||||
user.flags.cardReceived = false;
|
||||
return typeof cb === "function" ? cb(null, 'items.special flags.cardReceived') : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('items.special flags.cardReceived'));
|
||||
} else {
|
||||
return [
|
||||
{ specialItems: user.items.special, cardReceived: user.flags.cardReceived },
|
||||
i18n.t('readCard', {cardType}, req.language),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import { capByLevel } from '../statHelpers';
|
||||
import { MAX_LEVEL } from '../constants';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import resetGear from '../fns/resetGear';
|
||||
import equip from './equip';
|
||||
|
||||
module.exports = function(user, req, cb, analytics) {
|
||||
var analyticsData, flags, gear, lvl, stats;
|
||||
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
|
||||
|
||||
module.exports = function rebirth (user, tasks = [], req = {}, analytics) {
|
||||
if (user.balance < 2 && user.stats.lvl < MAX_LEVEL) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('notEnoughGems', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
analyticsData = {
|
||||
|
||||
let analyticsData = {
|
||||
uuid: user._id,
|
||||
category: 'behavior'
|
||||
category: 'behavior',
|
||||
};
|
||||
|
||||
if (user.stats.lvl < MAX_LEVEL) {
|
||||
user.balance -= 2;
|
||||
analyticsData.acquireMethod = 'Gems';
|
||||
@@ -24,63 +28,55 @@ module.exports = function(user, req, cb, analytics) {
|
||||
analyticsData.gemCost = 0;
|
||||
analyticsData.acquireMethod = '> 100';
|
||||
}
|
||||
if (analytics != null) {
|
||||
|
||||
if (analytics) {
|
||||
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;
|
||||
|
||||
let lvl = capByLevel(user.stats.lvl);
|
||||
|
||||
_.each(tasks, function resetTasks (task) {
|
||||
if (!task.challenge || !task.challenge.id || task.challenge.broken) {
|
||||
if (task.type !== 'reward') {
|
||||
task.value = 0;
|
||||
}
|
||||
if (task.type === 'daily') {
|
||||
task.streak = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
stats = user.stats;
|
||||
|
||||
let 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';
|
||||
stats.class = 'warrior';
|
||||
|
||||
_.each(USERSTATSLIST, function resetStats (value) {
|
||||
stats[value] = 0;
|
||||
});
|
||||
|
||||
resetGear(user);
|
||||
|
||||
if (user.items.currentPet) {
|
||||
user.ops.equip({
|
||||
equip(user, {
|
||||
params: {
|
||||
type: 'pet',
|
||||
key: user.items.currentPet
|
||||
}
|
||||
key: user.items.currentPet,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (user.items.currentMount) {
|
||||
user.ops.equip({
|
||||
equip(user, {
|
||||
params: {
|
||||
type: 'mount',
|
||||
key: user.items.currentMount
|
||||
}
|
||||
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;
|
||||
|
||||
let flags = user.flags;
|
||||
if (!user.achievements.beastMaster) {
|
||||
flags.rebirthEnabled = false;
|
||||
}
|
||||
@@ -88,13 +84,23 @@ module.exports = function(user, req, cb, analytics) {
|
||||
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) {
|
||||
} else if (lvl > user.achievements.rebirthLevel || lvl === MAX_LEVEL) {
|
||||
user.achievements.rebirths++;
|
||||
user.achievements.rebirthLevel = lvl;
|
||||
}
|
||||
|
||||
user.stats.buffs = {};
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
{user, tasks},
|
||||
i18n.t('rebirthComplete'),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,50 +1,68 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function releaseBoth (user, req = {}, analytics) {
|
||||
let animal;
|
||||
|
||||
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 = {
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
let giveTriadBingo = true;
|
||||
|
||||
if (!user.achievements.triadBingo) {
|
||||
if (analytics) {
|
||||
analytics.track('release pets & mounts', {
|
||||
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++;
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('achievements items balance')),
|
||||
i18n.t('mountsAndPetsReleased'),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function releaseMounts (user, req = {}, analytics) {
|
||||
let mount;
|
||||
|
||||
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;
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
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++;
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('release mounts', {
|
||||
uuid: user._id,
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: 4,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} 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++;
|
||||
return [
|
||||
user.items.mounts,
|
||||
i18n.t('mountsReleased'),
|
||||
];
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,32 +1,41 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb, analytics) {
|
||||
var analyticsData, pet;
|
||||
module.exports = function releasePets (user, req = {}, analytics) {
|
||||
if (user.balance < 1) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('notEnoughGems', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
user.balance -= 1;
|
||||
user.items.currentPet = '';
|
||||
|
||||
for (let pet in content.pets) {
|
||||
user.items.pets[pet] = 0;
|
||||
}
|
||||
|
||||
if (!user.achievements.beastMasterCount) {
|
||||
user.achievements.beastMasterCount = 0;
|
||||
}
|
||||
user.achievements.beastMasterCount++;
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('release pets', {
|
||||
uuid: user._id,
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: 4,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} 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 = "";
|
||||
return [
|
||||
user.items.pets,
|
||||
i18n.t('petsReleased'),
|
||||
];
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
+30
-19
@@ -1,29 +1,40 @@
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb, analytics) {
|
||||
var analyticsData;
|
||||
module.exports = function reroll (user, tasks = [], req = {}, analytics) {
|
||||
if (user.balance < 1) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: i18n.t('notEnoughGems', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
user.balance--;
|
||||
_.each(user.tasks, function(task) {
|
||||
if (task.type !== 'reward') {
|
||||
return task.value = 0;
|
||||
user.stats.hp = 50;
|
||||
|
||||
_.each(tasks, function resetTaskValues (task) {
|
||||
if (!task.challenge || !task.challenge.id || task.challenge.broken) {
|
||||
if (task.type !== 'reward') {
|
||||
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);
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('Fortify Potion', {
|
||||
uuid: user._id,
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: 4,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
{user, tasks},
|
||||
i18n.t('fortifyComplete'),
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
};
|
||||
|
||||
+20
-26
@@ -1,35 +1,29 @@
|
||||
import _ from 'lodash';
|
||||
import resetGear from '../fns/resetGear';
|
||||
import i18n from '../i18n';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var gear;
|
||||
user.habits = [];
|
||||
user.dailys = [];
|
||||
user.todos = [];
|
||||
user.rewards = [];
|
||||
module.exports = function reset (user, tasks = [], req = {}) {
|
||||
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;
|
||||
|
||||
let tasksToRemove = [];
|
||||
tasks.forEach(task => {
|
||||
if (!task.challenge || !task.challenge.id || task.challenge.broken) {
|
||||
tasksToRemove.push(task._id);
|
||||
let i = user.tasksOrder[`${task.type}s`].indexOf(task._id);
|
||||
if (i !== -1) user.tasksOrder[`${task.type}s`].splice(i, 1);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
gear.owned.weapon_warrior_0 = true;
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('items.gear.owned');
|
||||
|
||||
resetGear(user);
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
{user, tasksToRemove},
|
||||
i18n.t('resetComplete'),
|
||||
];
|
||||
}
|
||||
user.preferences.costume = false;
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
};
|
||||
|
||||
+74
-39
@@ -1,72 +1,107 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import randomVal from '../fns/randomVal';
|
||||
|
||||
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;
|
||||
module.exports = function revive (user, req = {}, analytics) {
|
||||
if (user.stats.hp > 0) {
|
||||
throw new NotAuthorized(i18n.t('cannotRevive', req.language));
|
||||
}
|
||||
|
||||
_.merge(user.stats, {
|
||||
hp: 50,
|
||||
exp: 0,
|
||||
gp: 0
|
||||
gp: 0,
|
||||
});
|
||||
|
||||
if (user.stats.lvl > 1) {
|
||||
user.stats.lvl--;
|
||||
}
|
||||
lostStat = user.fns.randomVal(_.reduce(['str', 'con', 'per', 'int'], (function(m, k) {
|
||||
|
||||
let lostStat = randomVal(user, _.reduce(['str', 'con', 'per', 'int'], function findRandomStat (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];
|
||||
|
||||
let base = user.items.gear.owned;
|
||||
let gearOwned;
|
||||
|
||||
if (typeof base.toObject === 'function') {
|
||||
gearOwned = base.toObject();
|
||||
} else {
|
||||
gearOwned = user.items.gear.owned;
|
||||
}
|
||||
|
||||
let losableItems = {};
|
||||
let userClass = user.stats.class;
|
||||
|
||||
_.each(gearOwned, function findLosableItems (value, key) {
|
||||
let itm;
|
||||
if (value) {
|
||||
itm = content.gear.flat[key];
|
||||
|
||||
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;
|
||||
let itemHasValueOrWarrior0 = itm.value > 0 || key === 'weapon_warrior_0';
|
||||
|
||||
let itemClassEqualsUserClass = itm.klass === userClass;
|
||||
|
||||
let itemClassSpecial = itm.klass === 'special';
|
||||
let itemNotSpecialOrUserClassIsSpecial = !itm.specialClass || itm.specialClass === userClass;
|
||||
let itemIsSpecial = itemNotSpecialOrUserClassIsSpecial && itemClassSpecial;
|
||||
|
||||
let itemIsArmoire = itm.klass === 'armoire';
|
||||
|
||||
if (itemHasValueOrWarrior0 && (itemClassEqualsUserClass || itemIsSpecial || itemIsArmoire)) {
|
||||
losableItems[key] = key;
|
||||
return losableItems[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
lostItem = user.fns.randomVal(losableItems);
|
||||
if (item = content.gear.flat[lostItem]) {
|
||||
|
||||
let lostItem = randomVal(user, losableItems);
|
||||
|
||||
let message = '';
|
||||
let item = content.gear.flat[lostItem];
|
||||
|
||||
if (item) {
|
||||
user.items.gear.owned[lostItem] = false;
|
||||
|
||||
if (user.items.gear.equipped[item.type] === lostItem) {
|
||||
user.items.gear.equipped[item.type] = item.type + "_base_0";
|
||||
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";
|
||||
user.items.gear.costume[item.type] = `${item.type}_base_0`;
|
||||
}
|
||||
|
||||
message = i18n.t('messageLostItem', { itemText: item.text(req.language)}, req.language);
|
||||
}
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('items.gear');
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('Death', {
|
||||
uuid: user._id,
|
||||
lostItem,
|
||||
gaLabel: lostItem,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
analyticsData = {
|
||||
uuid: user._id,
|
||||
lostItem: lostItem,
|
||||
gaLabel: lostItem,
|
||||
category: 'behavior'
|
||||
};
|
||||
if (analytics != null) {
|
||||
analytics.track('Death', analyticsData);
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user;
|
||||
} else {
|
||||
return [
|
||||
user.items,
|
||||
message,
|
||||
];
|
||||
}
|
||||
return typeof cb === "function" ? cb((item ? {
|
||||
code: 200,
|
||||
message: i18n.t('messageLostItem', {
|
||||
itemText: item.text(req.language)
|
||||
}, req.language)
|
||||
} : null), user) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
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;
|
||||
};
|
||||
@@ -0,0 +1,260 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import i18n from '../i18n';
|
||||
import updateStats from '../fns/updateStats';
|
||||
import crit from '../fns/crit';
|
||||
|
||||
const MAX_TASK_VALUE = 21.27;
|
||||
const MIN_TASK_VALUE = -47.27;
|
||||
const CLOSE_ENOUGH = 0.00001;
|
||||
|
||||
function _getTaskValue (taskValue) {
|
||||
if (taskValue < MIN_TASK_VALUE) {
|
||||
return MIN_TASK_VALUE;
|
||||
} else if (taskValue > MAX_TASK_VALUE) {
|
||||
return MAX_TASK_VALUE;
|
||||
} else {
|
||||
return taskValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the next task.value based on direction
|
||||
// Uses a capped inverse log y=.95^x, y>= -5
|
||||
function _calculateDelta (task, direction, cron) {
|
||||
// Min/max on task redness
|
||||
let currVal = _getTaskValue(task.value);
|
||||
let nextDelta = Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
|
||||
|
||||
// Checklists
|
||||
if (task.checklist && task.checklist.length > 0) {
|
||||
// If the Daily, only dock them a portion based on their checklist completion
|
||||
if (direction === 'down' && task.type === 'daily' && cron) {
|
||||
nextDelta *= 1 - _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0) / task.checklist.length;
|
||||
}
|
||||
|
||||
// If To-Do, point-match the TD per checklist item completed
|
||||
if (task.type === 'todo') {
|
||||
nextDelta *= 1 + _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
return nextDelta;
|
||||
}
|
||||
|
||||
// Approximates the reverse delta for the task value
|
||||
// This is meant to return the task value to its original value when unchecking a task.
|
||||
// First, calculate the value using the normal way for our first guess although
|
||||
// it will be a bit off
|
||||
function _calculateReverseDelta (task, direction) {
|
||||
let currVal = _getTaskValue(task.value);
|
||||
let testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
|
||||
|
||||
// Now keep moving closer to the original value until we get "close enough"
|
||||
// Check how close we are to the original value by computing the delta off our guess
|
||||
// and looking at the difference between that and our current value.
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
let calc = testVal + Math.pow(0.9747, testVal);
|
||||
let diff = currVal - calc;
|
||||
|
||||
if (Math.abs(diff) < CLOSE_ENOUGH) break;
|
||||
|
||||
if (diff > 0) {
|
||||
testVal -= diff;
|
||||
} else {
|
||||
testVal += diff;
|
||||
}
|
||||
}
|
||||
|
||||
// When we get close enough, return the difference between our approximated value
|
||||
// and the current value. This will be the delta calculated from the original value
|
||||
// before the task was checked.
|
||||
let nextDelta = testVal - currVal;
|
||||
|
||||
// Checklists - If To-Do, point-match the TD per checklist item completed
|
||||
if (task.checklist && task.checklist.length > 0 && task.type === 'todo') {
|
||||
nextDelta *= 1 + _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0);
|
||||
}
|
||||
|
||||
return nextDelta;
|
||||
}
|
||||
|
||||
function _gainMP (user, val) {
|
||||
val *= user._tmp.crit || 1;
|
||||
user.stats.mp += val;
|
||||
|
||||
if (user.stats.mp >= user._statsComputed.maxMP) user.stats.mp = user._statsComputed.maxMP;
|
||||
if (user.stats.mp < 0) {
|
||||
user.stats.mp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// HP modifier
|
||||
// ===== CONSTITUTION =====
|
||||
// TODO Decreases HP loss from bad habits / missed dailies by 0.5% per point.
|
||||
function _subtractPoints (user, task, stats, delta) {
|
||||
let conBonus = 1 - user._statsComputed.con / 250;
|
||||
if (conBonus < 0.1) conBonus = 0.1;
|
||||
|
||||
let hpMod = delta * conBonus * task.priority * 2; // constant 2 multiplier for better results
|
||||
stats.hp += Math.round(hpMod * 10) / 10; // round to 1dp
|
||||
return stats.hp;
|
||||
}
|
||||
|
||||
function _addPoints (user, task, stats, direction, delta) {
|
||||
// ===== CRITICAL HITS =====
|
||||
// allow critical hit only when checking off a task, not when unchecking it:
|
||||
let _crit = delta > 0 ? crit(user) : 1;
|
||||
// if there was a crit, alert the user via notification
|
||||
if (_crit > 1) user._tmp.crit = _crit;
|
||||
|
||||
// Exp Modifier
|
||||
// ===== Intelligence =====
|
||||
// TODO Increases Experience gain by .2% per point.
|
||||
let intBonus = 1 + user._statsComputed.int * 0.025;
|
||||
stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6);
|
||||
|
||||
// GP modifier
|
||||
// ===== PERCEPTION =====
|
||||
// TODO Increases Gold gained from tasks by .3% per point.
|
||||
let perBonus = 1 + user._statsComputed.per * 0.02;
|
||||
let gpMod = delta * task.priority * _crit * perBonus;
|
||||
|
||||
if (task.streak) {
|
||||
let currStreak = direction === 'down' ? task.streak - 1 : task.streak;
|
||||
let streakBonus = currStreak / 100 + 1; // eg, 1-day streak is 1.01, 2-day is 1.02, etc
|
||||
let afterStreak = gpMod * streakBonus;
|
||||
if (currStreak > 0 && gpMod > 0) {
|
||||
user._tmp.streakBonus = afterStreak - gpMod; // keep this on-hand for later, so we can notify streak-bonus
|
||||
}
|
||||
|
||||
stats.gp += afterStreak;
|
||||
} else {
|
||||
stats.gp += gpMod;
|
||||
}
|
||||
}
|
||||
|
||||
function _changeTaskValue (user, task, direction, times, cron) {
|
||||
let addToDelta = 0;
|
||||
|
||||
// If multiple days have passed, multiply times days missed
|
||||
_.times(times, () => {
|
||||
// Each iteration calculate the nextDelta, which is then accumulated in the total delta.
|
||||
let nextDelta = !cron && direction === 'down' ? _calculateReverseDelta(task, direction) : _calculateDelta(task, direction, cron);
|
||||
|
||||
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') { // Make progress on quest based on STR
|
||||
user.party.quest.progress.up = user.party.quest.progress.up || 0;
|
||||
|
||||
if (task.type === 'todo' || task.type === 'daily') {
|
||||
user.party.quest.progress.up += nextDelta * (1 + user._statsComputed.str / 200);
|
||||
} else if (task.type === 'habit') {
|
||||
user.party.quest.progress.up += nextDelta * (0.5 + user._statsComputed.str / 400);
|
||||
}
|
||||
}
|
||||
|
||||
task.value += nextDelta;
|
||||
}
|
||||
|
||||
addToDelta += nextDelta;
|
||||
});
|
||||
|
||||
return addToDelta;
|
||||
}
|
||||
|
||||
module.exports = function scoreTask (options = {}, req = {}) {
|
||||
let {user, task, direction, times = 1, cron = false} = options;
|
||||
let delta = 0;
|
||||
let stats = {
|
||||
gp: user.stats.gp,
|
||||
hp: user.stats.hp,
|
||||
exp: user.stats.exp,
|
||||
};
|
||||
|
||||
// This is for setting one-time temporary flags, such as streakBonus or itemDropped. Useful for notifying
|
||||
// the API consumer, then cleared afterwards
|
||||
user._tmp = {};
|
||||
|
||||
// If they're trying to purchase a too-expensive reward, don't allow them to do that.
|
||||
if (task.value > user.stats.gp && task.type === 'reward') throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
|
||||
if (task.type === 'habit') {
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
|
||||
// Add habit value to habit-history (if different)
|
||||
if (delta > 0) {
|
||||
_addPoints(user, task, stats, direction, delta);
|
||||
} else {
|
||||
_subtractPoints(user, task, stats, delta);
|
||||
}
|
||||
_gainMP(user, _.max([0.25, 0.0025 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1));
|
||||
|
||||
task.history = task.history || [];
|
||||
// Add history entry, even more than 1 per day
|
||||
task.history.push({
|
||||
date: Number(new Date()),
|
||||
value: task.value,
|
||||
});
|
||||
} else if (task.type === 'daily') {
|
||||
if (cron) {
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
_subtractPoints(user, task, stats, delta);
|
||||
if (!user.stats.buffs.streaks) task.streak = 0;
|
||||
} else {
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
if (direction === 'down') delta = _calculateDelta(task, direction, cron); // recalculate delta for unchecking so the gp and exp come out correctly
|
||||
_addPoints(user, task, stats, direction, delta); // obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
_gainMP(user, _.max([1, 0.01 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1));
|
||||
|
||||
if (direction === 'up') {
|
||||
task.streak += 1;
|
||||
// Give a streak achievement when the streak is a multiple of 21
|
||||
if (task.streak % 21 === 0) user.achievements.streak = user.achievements.streak ? user.achievements.streak + 1 : 1;
|
||||
task.completed = true;
|
||||
} else if (direction === 'down') {
|
||||
// Remove a streak achievement if streak was a multiple of 21 and the daily was undone
|
||||
if (task.streak % 21 === 0) user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0;
|
||||
task.streak -= 1;
|
||||
task.completed = false;
|
||||
}
|
||||
}
|
||||
} else if (task.type === 'todo') {
|
||||
if (cron) { // don't touch stats on cron
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
} else {
|
||||
if (direction === 'up') {
|
||||
task.dateCompleted = new Date();
|
||||
task.completed = true;
|
||||
} else if (direction === 'down') {
|
||||
task.completed = false;
|
||||
task.dateCompleted = undefined;
|
||||
}
|
||||
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
if (direction === 'down') delta = _calculateDelta(task, direction, cron); // recalculate delta for unchecking so the gp and exp come out correctly
|
||||
_addPoints(user, task, stats, direction, delta);
|
||||
|
||||
// MP++ per checklist item in ToDo, bonus per CLI
|
||||
let multiplier = _.max([_.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 1), 1]);
|
||||
_gainMP(user, _.max([multiplier, 0.01 * user._statsComputed.maxMP * multiplier]) * (direction === 'down' ? -1 : 1));
|
||||
}
|
||||
} else if (task.type === 'reward') {
|
||||
// Don't adjust values for rewards
|
||||
delta += _changeTaskValue(user, task, direction, times, cron);
|
||||
// purchase item
|
||||
stats.gp -= Math.abs(task.value);
|
||||
// hp - gp difference
|
||||
if (stats.gp < 0) {
|
||||
stats.hp += stats.gp;
|
||||
stats.gp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateStats(user, stats, req);
|
||||
return [delta];
|
||||
};
|
||||
+33
-13
@@ -1,23 +1,43 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
NotFound,
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
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;
|
||||
const ACCEPTEDTYPES = ['eggs', 'hatchingPotions', 'food'];
|
||||
|
||||
module.exports = function sell (user, req = {}) {
|
||||
let key = _.get(req.params, 'key');
|
||||
let type = _.get(req.params, 'type');
|
||||
|
||||
if (!type) {
|
||||
throw new BadRequest(i18n.t('typeRequired', req.language));
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new BadRequest(i18n.t('keyRequired', req.language));
|
||||
}
|
||||
|
||||
if (ACCEPTEDTYPES.indexOf(type) === -1) {
|
||||
throw new NotAuthorized(i18n.t('typeNotSellable', {acceptedTypes: ACCEPTEDTYPES.join(', ')}, req.language));
|
||||
}
|
||||
|
||||
if (!user.items[type][key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":key not found for user.items." + type
|
||||
}) : void 0;
|
||||
throw new NotFound(i18n.t('userItemsKeyNotFound', {type}, req.language));
|
||||
}
|
||||
|
||||
user.items[type][key]--;
|
||||
user.stats.gp += content[type][key].value;
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('stats items'))) : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return _.pick(user, splitWhitespace('stats items'));
|
||||
} else {
|
||||
return [
|
||||
_.pick(user, splitWhitespace('stats items')),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
module.exports = function(user, req, cb) {
|
||||
module.exports = function sleep (user, req = {}) {
|
||||
user.preferences.sleep = !user.preferences.sleep;
|
||||
return typeof cb === "function" ? cb(null, {}) : void 0;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return {};
|
||||
} else {
|
||||
return [user.preferences.sleep];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
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;
|
||||
import { BadRequest } from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function sortTag (user, req = {}) {
|
||||
let to = _.get(req, 'query.to');
|
||||
let fromParam = _.get(req, 'query.from');
|
||||
|
||||
let invalidTo = !to && to !== 0;
|
||||
let invalidFrom = !fromParam && fromParam !== 0;
|
||||
|
||||
if (invalidTo || invalidFrom) {
|
||||
throw new BadRequest('?to=__&from=__ are required');
|
||||
}
|
||||
user.tags.splice(to, 0, user.tags.splice(from, 1)[0]);
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
|
||||
user.tags.splice(to, 0, user.tags.splice(fromParam, 1)[0]);
|
||||
return user.tags;
|
||||
};
|
||||
|
||||
@@ -1,39 +1,49 @@
|
||||
import i18n from '../i18n';
|
||||
import preenTodos from '../libs/preenTodos';
|
||||
import {
|
||||
NotFound,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
import _ from 'lodash';
|
||||
|
||||
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;
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function sortTask (user, req = {}) {
|
||||
let id = _.get(req, 'params.id');
|
||||
let to = _.get(req, 'query.to');
|
||||
let fromParam = _.get(req, 'query.from');
|
||||
let taskType = _.get(req, 'params.taskType');
|
||||
|
||||
let index = _.findIndex(user[`${taskType}s`], function findById (task) {
|
||||
return task._id === id;
|
||||
});
|
||||
|
||||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTaskNotFound', req.language));
|
||||
}
|
||||
if (!((to != null) && (from != null))) {
|
||||
return typeof cb === "function" ? cb('?to=__&from=__ are required') : void 0;
|
||||
if (!to && !fromParam) {
|
||||
throw new BadRequest('?to=__&from=__ are required');
|
||||
}
|
||||
tasks = user[task.type + "s"];
|
||||
if (task.type === 'todo' && tasks[from] !== task) {
|
||||
preenedTasks = preenTodos(tasks);
|
||||
|
||||
let tasks = user[`${taskType}s`];
|
||||
|
||||
if (taskType === 'todo') {
|
||||
let preenedTasks = preenTodos(tasks);
|
||||
|
||||
if (to !== -1) {
|
||||
to = tasks.indexOf(preenedTasks[to]);
|
||||
}
|
||||
from = tasks.indexOf(preenedTasks[from]);
|
||||
|
||||
fromParam = tasks.indexOf(preenedTasks[fromParam]);
|
||||
}
|
||||
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];
|
||||
|
||||
let movedTask = tasks.splice(fromParam, 1)[0];
|
||||
|
||||
if (to === -1) {
|
||||
tasks.push(movedTask);
|
||||
} else {
|
||||
tasks.splice(to, 0, movedTask);
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, tasks) : void 0;
|
||||
|
||||
return tasks;
|
||||
};
|
||||
|
||||
+96
-46
@@ -1,63 +1,113 @@
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
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 item is already purchased -> equip it
|
||||
// Otherwise unlock it
|
||||
module.exports = function unlock (user, req = {}, analytics) {
|
||||
let path = _.get(req.query, 'path');
|
||||
|
||||
if (!path) {
|
||||
throw new BadRequest(i18n.t('pathRequired', req.language));
|
||||
}
|
||||
if (fullSet) {
|
||||
_.each(path.split(","), function(p) {
|
||||
if (~path.indexOf('gear.')) {
|
||||
user.fns.dotSet("" + p, true);
|
||||
true;
|
||||
} else {
|
||||
|
||||
let isFullSet = path.indexOf(',') !== -1;
|
||||
let isBackground = path.indexOf('background.') !== -1;
|
||||
|
||||
let cost;
|
||||
if (isBackground && isFullSet) {
|
||||
cost = 3.75;
|
||||
} else if (isBackground) {
|
||||
cost = 1.75;
|
||||
} else if (isFullSet) {
|
||||
cost = 1.25;
|
||||
} else {
|
||||
cost = 0.5;
|
||||
}
|
||||
|
||||
let setPaths;
|
||||
let alreadyOwns;
|
||||
|
||||
if (isFullSet) {
|
||||
setPaths = path.split(',');
|
||||
let alreadyOwnedItems = 0;
|
||||
|
||||
_.each(setPaths, singlePath => {
|
||||
if (_.get(user, `purchased.${singlePath}`) === true) {
|
||||
alreadyOwnedItems++;
|
||||
}
|
||||
user.fns.dotSet("purchased." + p, true);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (alreadyOwnedItems === setPaths.length) {
|
||||
throw new NotAuthorized(i18n.t('alreadyUnlocked', req.language));
|
||||
// TODO write math formula to check if buying the full set is cheaper than the items individually
|
||||
// (item cost * number of remaining items) < setCost`
|
||||
} /* else if (alreadyOwnedItems > 0) {
|
||||
throw new NotAuthorized(i18n.t('alreadyUnlockedPart', req.language));
|
||||
} */
|
||||
} else {
|
||||
alreadyOwns = _.get(user, `purchased.${path}`) === true;
|
||||
}
|
||||
|
||||
if ((!user.balance || user.balance < cost) && !alreadyOwns) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
if (isFullSet) {
|
||||
_.each(setPaths, function markItemsAsPurchased (pathPart) {
|
||||
if (path.indexOf('gear.') !== -1) {
|
||||
_.set(user, pathPart, true);
|
||||
}
|
||||
|
||||
_.set(user, `purchased.${pathPart}`, true);
|
||||
});
|
||||
} else {
|
||||
if (alreadyOwns) {
|
||||
split = path.split('.');
|
||||
v = split.pop();
|
||||
k = split.join('.');
|
||||
if (k === 'background' && v === user.preferences.background) {
|
||||
v = '';
|
||||
if (alreadyOwns) { // eslint-disable-line no-lonely-if
|
||||
let split = path.split('.');
|
||||
let value = split.pop();
|
||||
let key = split.join('.');
|
||||
if (key === 'background' && value === user.preferences.background) {
|
||||
value = '';
|
||||
}
|
||||
user.fns.dotSet("preferences." + k, v);
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
|
||||
_.set(user, `preferences.${key}`, value);
|
||||
} else {
|
||||
_.set(user, `purchased.${path}`, true);
|
||||
}
|
||||
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") {
|
||||
|
||||
if (!alreadyOwns) {
|
||||
if (path.indexOf('gear.') === -1) {
|
||||
user.markModified('purchased');
|
||||
}
|
||||
|
||||
user.balance -= cost;
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: path,
|
||||
itemType: 'customization',
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: cost / 0.25,
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
}
|
||||
analyticsData = {
|
||||
uuid: user._id,
|
||||
itemKey: path,
|
||||
itemType: 'customization',
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: cost / .25,
|
||||
category: 'behavior'
|
||||
};
|
||||
if (analytics != null) {
|
||||
analytics.track('acquire item', analyticsData);
|
||||
|
||||
let response = [
|
||||
_.pick(user, splitWhitespace('purchased preferences items')),
|
||||
];
|
||||
|
||||
if (!alreadyOwns) response.push(i18n.t('unlocked', req.language));
|
||||
|
||||
if (req.v2 === true) {
|
||||
return response[0];
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('purchased preferences items'))) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
_.each(req.body, function(v, k) {
|
||||
user.fns.dotSet(k, v);
|
||||
return true;
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function updateUser (user, req = {}) {
|
||||
_.each(req.body, (val, key) => {
|
||||
_.set(user, key, val);
|
||||
});
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import { NotFound } from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
var i, tid;
|
||||
tid = req.params.id;
|
||||
i = _.findIndex(user.tags, {
|
||||
id: tid
|
||||
// TODO used only in client, move there?
|
||||
|
||||
module.exports = function updateTag (user, req = {}) {
|
||||
let tid = _.get(req, 'params.id');
|
||||
|
||||
let index = _.findIndex(user.tags, {
|
||||
id: tid,
|
||||
});
|
||||
if (!~i) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: i18n.t('messageTagNotFound', req.language)
|
||||
}) : void 0;
|
||||
|
||||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTagNotFound', req.language));
|
||||
}
|
||||
user.tags[i].name = req.body.name;
|
||||
return typeof cb === "function" ? cb(null, user.tags[i]) : void 0;
|
||||
|
||||
user.tags[index].name = _.get(req, 'body.name');
|
||||
return user.tags[index];
|
||||
};
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
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;
|
||||
// From server pass task.toObject() not the task document directly
|
||||
module.exports = function updateTask (task, req = {}) {
|
||||
let body = req.body || {};
|
||||
|
||||
// If reminders are updated -> replace the original ones
|
||||
if (body.reminders) {
|
||||
task.reminders = body.reminders;
|
||||
}
|
||||
_.merge(task, _.omit(req.body, ['checklist', 'reminders', 'id', 'type']));
|
||||
if (req.body.checklist) {
|
||||
task.checklist = req.body.checklist;
|
||||
|
||||
// If checklist is updated -> replace the original one
|
||||
if (body.checklist) {
|
||||
task.checklist = body.checklist;
|
||||
}
|
||||
if (req.body.reminders) {
|
||||
task.reminders = req.body.reminders;
|
||||
|
||||
// If tags are updated -> replace the original ones
|
||||
if (body.tags) {
|
||||
task.tags = body.tags;
|
||||
}
|
||||
if (typeof task.markModified === "function") {
|
||||
task.markModified('tags');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, task) : void 0;
|
||||
|
||||
_.merge(task, _.omit(body, ['_id', 'id', 'type', 'reminders', 'checklist', 'tags']));
|
||||
|
||||
return [task];
|
||||
};
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import _ from 'lodash';
|
||||
import validator from 'validator';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
_.merge(user.preferences.webhooks[req.params.id], req.body);
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('preferences.webhooks');
|
||||
module.exports = function updateWebhook (user, req) {
|
||||
if (!validator.isURL(req.body.url)) throw new BadRequest(i18n.t('invalidUrl', req.language));
|
||||
if (!validator.isBoolean(req.body.enabled)) throw new BadRequest(i18n.t('invalidEnabled', req.language));
|
||||
|
||||
user.markModified('preferences.webhooks');
|
||||
user.preferences.webhooks[req.params.id].url = req.body.url;
|
||||
user.preferences.webhooks[req.params.id].enabled = req.body.enabled;
|
||||
|
||||
if (req.v2 === true) {
|
||||
return user.preferences.webhooks;
|
||||
} else {
|
||||
return [user.preferences.webhooks[req.params.id]];
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user