wip: split shared ops

This commit is contained in:
Matteo Pagliazzi
2016-03-07 23:02:42 +01:00
parent 8437c21a51
commit eeebe4c342
55 changed files with 1925 additions and 1634 deletions

View File

@@ -32,7 +32,6 @@
"no-octal": 2, "no-octal": 2,
"no-process-env": 2, "no-process-env": 2,
"no-proto": 2, "no-proto": 2,
"no-implied-eval": 2,
"yoda": 2, "yoda": 2,
"wrap-iife": 2, "wrap-iife": 2,
"radix": 2, "radix": 2,
@@ -59,7 +58,6 @@
"no-path-concat": 2, "no-path-concat": 2,
"arrow-spacing": 2, "arrow-spacing": 2,
"constructor-super": 2, "constructor-super": 2,
"generator-star-spacing": 2,
"no-arrow-condition": 2, "no-arrow-condition": 2,
"no-class-assign": 2, "no-class-assign": 2,
"no-const-assign": 2, "no-const-assign": 2,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
import uuid from './uuid';
import taskDefaults from './taskDefaults';
import refPush from './refPush';
import splitWhitespace from './splitWhitespace';
import planGemLimits from './planGemLimits';
import preenTodos from './preenTodos';
module.exports = {
uuid,
taskDefaults,
refPush,
splitWhitespace,
planGemLimits,
preenTodos,
};

View File

@@ -0,0 +1,4 @@
module.exports = {
convRate: 20,
convCap: 25,
};

View File

@@ -0,0 +1,14 @@
import moment from 'moment';
import _ from 'lodash';
/*
Preen 3-day past-completed To-Dos from Angular & mobile app
*/
module.exports = function(tasks) {
return _.filter(tasks, function(t) {
return !t.completed || (t.challenge && t.challenge.id) || moment(t.dateCompleted).isAfter(moment().subtract({
days: 3
}));
});
};

View File

@@ -0,0 +1,19 @@
import _ from 'lodash';
import uuid from './uuid';
/*
Reflists are arrays, but stored as objects. Mongoose has a helluvatime working with arrays (the main problem for our
syncing issues) - so the goal is to move away from arrays to objects, since mongoose can reference elements by ID
no problem. To maintain sorting, we use these helper functions:
*/
module.exports = function(reflist, item, prune) {
if (prune == null) {
prune = 0;
}
item.sort = _.isEmpty(reflist) ? 0 : _.max(reflist, 'sort').sort + 1;
if (!(item.id && !reflist[item.id])) {
item.id = uuid();
}
return reflist[item.id] = item;
};

View File

@@ -0,0 +1,3 @@
module.exports = function(s) {
return s.split(' ');
};

View File

@@ -0,0 +1,71 @@
import uuid from './uuid';
import _ from 'lodash';
/*
Even though Mongoose handles task defaults, we want to make sure defaults are set on the client-side before
sending up to the server for performance
*/
// TODO revisit
module.exports = function(task) {
var defaults, ref, ref1, ref2;
if (task == null) {
task = {};
}
if (!(task.type && ((ref = task.type) === 'habit' || ref === 'daily' || ref === 'todo' || ref === 'reward'))) {
task.type = 'habit';
}
defaults = {
id: uuid(),
text: task.id != null ? task.id : '',
notes: '',
priority: 1,
challenge: {},
attribute: 'str',
dateCreated: new Date()
};
_.defaults(task, defaults);
if (task.type === 'habit') {
_.defaults(task, {
up: true,
down: true
});
}
if ((ref1 = task.type) === 'habit' || ref1 === 'daily') {
_.defaults(task, {
history: []
});
}
if ((ref2 = task.type) === 'daily' || ref2 === 'todo') {
_.defaults(task, {
completed: false
});
}
if (task.type === 'daily') {
_.defaults(task, {
streak: 0,
repeat: {
su: true,
m: true,
t: true,
w: true,
th: true,
f: true,
s: true
}
}, {
startDate: new Date(),
everyX: 1,
frequency: 'weekly'
});
}
task._id = task.id;
if (task.value == null) {
task.value = task.type === 'reward' ? 10 : 0;
}
if (!_.isNumber(task.priority)) {
task.priority = 1;
}
return task;
};

View File

@@ -0,0 +1,9 @@
// TODO use node-uuid module
module.exports = function() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r, v;
r = Math.random() * 16 | 0;
v = (c === "x" ? r : r & 0x3 | 0x8);
return v.toString(16);
});
};

View File

@@ -0,0 +1,20 @@
import _ from 'lodash';
module.exports = function(user, req, cb) {
var i, item, pd;
if (!user.pushDevices) {
user.pushDevices = [];
}
pd = user.pushDevices;
item = {
regId: req.body.regId,
type: req.body.type
};
i = _.findIndex(pd, {
regId: item.regId
});
if (i === -1) {
pd.push(item);
}
return typeof cb === "function" ? cb(null, user.pushDevices) : void 0;
};

View File

@@ -0,0 +1,12 @@
import uuid from '../libs/uuid';
module.exports = function(user, req, cb) {
if (user.tags == null) {
user.tags = [];
}
user.tags.push({
name: req.body.name,
id: req.body.id || api.uuid()
});
return typeof cb === "function" ? cb(null, user.tags) : void 0;
};

View File

@@ -0,0 +1,27 @@
import taskDefaults from '../libs/taskDefaults';
import i18n from '../i18n';
module.exports = function(user, req, cb) {
var task;
task = api.taskDefaults(req.body);
if (user.tasks[task.id] != null) {
return typeof cb === "function" ? cb({
code: 409,
message: i18n.t('messageDuplicateTaskID', req.language)
}) : void 0;
}
user[task.type + "s"].unshift(task);
if (user.preferences.newTaskEdit) {
task._editing = true;
}
if (user.preferences.tagsCollapsed) {
task._tags = true;
}
if (!user.preferences.advancedCollapsed) {
task._advanced = true;
}
if (typeof cb === "function") {
cb(null, task);
}
return task;
};

View File

@@ -0,0 +1,15 @@
import refPush from '../libs/refPush';
module.exports = function(user, req, cb) {
var wh;
wh = user.preferences.webhooks;
api.refPush(wh, {
url: req.body.url,
enabled: req.body.enabled || true,
id: req.body.id
});
if (typeof user.markModified === "function") {
user.markModified('preferences.webhooks');
}
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
};

View File

@@ -0,0 +1,15 @@
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function(user, req, cb) {
var stat;
stat = req.query.stat || 'str';
if (user.stats.points > 0) {
user.stats[stat]++;
user.stats.points--;
if (stat === 'int') {
user.stats.mp++;
}
}
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats'))) : void 0;
};

View File

@@ -0,0 +1,10 @@
import _ from 'lodash';
module.exports = function(user, req, cb) {
_.times(user.stats.points, user.fns.autoAllocate);
user.stats.points = 0;
if (typeof user.markModified === "function") {
user.markModified('stats');
}
return typeof cb === "function" ? cb(null, user.stats) : void 0;
};

View File

@@ -0,0 +1,13 @@
module.exports = function(user, req, cb) {
var i;
i = user.inbox.blocks.indexOf(req.params.uuid);
if (~i) {
user.inbox.blocks.splice(i, 1);
} else {
user.inbox.blocks.push(req.params.uuid);
}
if (typeof user.markModified === "function") {
user.markModified('inbox.blocks');
}
return typeof cb === "function" ? cb(null, user.inbox.blocks) : void 0;
};

119
common/script/ops/buy.js Normal file
View File

@@ -0,0 +1,119 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
import count from '../count';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function(user, req, cb, analytics) {
var analyticsData, armoireExp, armoireResp, armoireResult, base, buyResp, drop, eligibleEquipment, item, key, message, name;
key = req.params.key;
item = key === 'potion' ? content.potion : key === 'armoire' ? content.armoire : content.gear.flat[key];
if (!item) {
return typeof cb === "function" ? cb({
code: 404,
message: "Item '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
}) : void 0;
}
if (user.stats.gp < item.value) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('messageNotEnoughGold', req.language)
}) : void 0;
}
if ((item.canOwn != null) && !item.canOwn(user)) {
return typeof cb === "function" ? cb({
code: 401,
message: "You can't buy this item"
}) : void 0;
}
armoireResp = void 0;
if (item.key === 'potion') {
user.stats.hp += 15;
if (user.stats.hp > 50) {
user.stats.hp = 50;
}
} else if (item.key === 'armoire') {
armoireResult = user.fns.predictableRandom(user.stats.gp);
eligibleEquipment = _.filter(content.gear.flat, (function(i) {
return i.klass === 'armoire' && !user.items.gear.owned[i.key];
}));
if (!_.isEmpty(eligibleEquipment) && (armoireResult < .6 || !user.flags.armoireOpened)) {
eligibleEquipment.sort();
drop = user.fns.randomVal(eligibleEquipment);
user.items.gear.owned[drop.key] = true;
user.flags.armoireOpened = true;
message = i18n.t('armoireEquipment', {
image: '<span class="shop_' + drop.key + ' pull-left"></span>',
dropText: drop.text(req.language)
}, req.language);
if (api.count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
user.flags.armoireEmpty = true;
}
armoireResp = {
type: "gear",
dropKey: drop.key,
dropText: drop.text(req.language)
};
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < .8) || armoireResult < .5) {
drop = user.fns.randomVal(_.where(content.food, {
canDrop: true
}));
if ((base = user.items.food)[name = drop.key] == null) {
base[name] = 0;
}
user.items.food[drop.key] += 1;
message = i18n.t('armoireFood', {
image: '<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
};
}
} else {
if (user.preferences.autoEquip) {
user.items.gear.equipped[item.type] = item.key;
message = user.fns.handleTwoHanded(item, null, req);
}
user.items.gear.owned[item.key] = true;
if (message == null) {
message = i18n.t('messageBought', {
itemText: item.text(req.language)
}, req.language);
}
if (item.last) {
user.fns.ultimateGear();
}
}
user.stats.gp -= item.value;
analyticsData = {
uuid: user._id,
itemKey: key,
acquireMethod: 'Gold',
goldCost: item.value,
category: 'behavior'
};
if (analytics != null) {
analytics.track('acquire item', analyticsData);
}
buyResp = _.pick(user, $w('items achievements stats flags'));
if (armoireResp) {
buyResp["armoire"] = armoireResp;
}
return typeof cb === "function" ? cb({
code: 200,
message: message
}, buyResp) : void 0;
};

View File

@@ -0,0 +1,43 @@
import i18n from '../i18n';
import content from '../content/index';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function(user, req, cb, analytics) {
var mysterySet, ref;
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('notEnoughHourglasses', req.language)
}) : void 0;
}
mysterySet = (ref = content.timeTravelerStore(user.items.gear.owned)) != null ? ref[req.params.key] : void 0;
if ((typeof window !== "undefined" && window !== null ? window.confirm : void 0) != null) {
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) {
return;
}
}
if (!mysterySet) {
return typeof cb === "function" ? cb({
code: 404,
message: "Mystery set not found, or set already owned"
}) : void 0;
}
_.each(mysterySet.items, function(i) {
var analyticsData;
user.items.gear.owned[i.key] = true;
analyticsData = {
uuid: user._id,
itemKey: i.key,
itemType: 'Subscriber Gear',
acquireMethod: 'Hourglass',
category: 'behavior'
};
return analytics != null ? analytics.track('acquire item', analyticsData) : void 0;
});
user.purchased.plan.consecutive.trinkets--;
return typeof cb === "function" ? cb({
code: 200,
message: i18n.t('hourglassPurchaseSet', req.language)
}, _.pick(user, $w('items purchased.plan.consecutive'))) : void 0;
};

View File

@@ -0,0 +1,49 @@
import i18n from '../i18n';
import content from '../content/index';
module.exports = function(user, req, cb, analytics) {
var analyticsData, base, item, key, message, name;
key = req.params.key;
item = content.quests[key];
if (!item) {
return typeof cb === "function" ? cb({
code: 404,
message: "Quest '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
}) : void 0;
}
if (!(item.category === 'gold' && item.goldValue)) {
return typeof cb === "function" ? cb({
code: 404,
message: "Quest '" + key + " is not a Gold-purchasable quest (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
}) : void 0;
}
if (user.stats.gp < item.goldValue) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('messageNotEnoughGold', req.language)
}) : void 0;
}
message = i18n.t('messageBought', {
itemText: item.text(req.language)
}, req.language);
if ((base = user.items.quests)[name = item.key] == null) {
base[name] = 0;
}
user.items.quests[item.key] += 1;
user.stats.gp -= item.goldValue;
analyticsData = {
uuid: user._id,
itemKey: item.key,
itemType: 'Market',
goldCost: item.goldValue,
acquireMethod: 'Gold',
category: 'behavior'
};
if (analytics != null) {
analytics.track('acquire item', analyticsData);
}
return typeof cb === "function" ? cb({
code: 200,
message: message
}, user.items.quests) : void 0;
};

View File

@@ -0,0 +1,31 @@
import i18n from '../i18n';
import content from '../content/index';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function(user, req, cb) {
var base, item, key, message;
key = req.params.key;
item = content.special[key];
if (user.stats.gp < item.value) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('messageNotEnoughGold', req.language)
}) : void 0;
}
user.stats.gp -= item.value;
if ((base = user.items.special)[key] == null) {
base[key] = 0;
}
user.items.special[key]++;
if (typeof user.markModified === "function") {
user.markModified('items.special');
}
message = i18n.t('messageBought', {
itemText: item.text(req.language)
}, req.language);
return typeof cb === "function" ? cb({
code: 200,
message: message
}, _.pick(user, $w('items stats'))) : void 0;
};

View File

@@ -0,0 +1,59 @@
import i18n from '../i18n';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
import { capByLevel } from '../statHelpers';
module.exports = function(user, req, cb, analytics) {
var analyticsData, klass, ref;
klass = (ref = req.query) != null ? ref["class"] : void 0;
if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
analyticsData = {
uuid: user._id,
"class": klass,
acquireMethod: 'Gems',
gemCost: 3,
category: 'behavior'
};
if (analytics != null) {
analytics.track('change class', analyticsData);
}
user.stats["class"] = klass;
user.flags.classSelected = true;
_.each(["weapon", "armor", "shield", "head"], function(type) {
var foundKey;
foundKey = false;
_.findLast(user.items.gear.owned, function(v, k) {
if (~k.indexOf(type + "_" + klass) && v === true) {
return foundKey = k;
}
});
user.items.gear.equipped[type] = foundKey ? foundKey : type === "weapon" ? "weapon_" + klass + "_0" : type === "shield" && klass === "rogue" ? "shield_rogue_0" : type + "_base_0";
if (type === "weapon" || (type === "shield" && klass === "rogue")) {
user.items.gear.owned[type + "_" + klass + "_0"] = true;
}
return true;
});
} else {
if (user.preferences.disableClasses) {
user.preferences.disableClasses = false;
user.preferences.autoAllocate = false;
} else {
if (!(user.balance >= .75)) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
}
user.balance -= .75;
}
_.merge(user.stats, {
str: 0,
con: 0,
per: 0,
int: 0,
points: api.capByLevel(user.stats.lvl)
});
user.flags.classSelected = false;
}
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags items preferences'))) : void 0;
};

View File

@@ -0,0 +1,12 @@
import _ from 'lodash';
module.exports = function(user, req, cb) {
_.remove(user.todos, function(t) {
var ref;
return t.completed && !((ref = t.challenge) != null ? ref.id : void 0);
});
if (typeof user.markModified === "function") {
user.markModified('todos');
}
return typeof cb === "function" ? cb(null, user.todos) : void 0;
};

View File

@@ -0,0 +1,7 @@
module.exports = function(user, req, cb) {
user.inbox.messages = {};
if (typeof user.markModified === "function") {
user.markModified('inbox.messages');
}
return typeof cb === "function" ? cb(null, user.inbox.messages) : void 0;
};

View File

@@ -0,0 +1,7 @@
module.exports = function (user, req, cb) {
delete user.inbox.messages[req.params.id];
if (typeof user.markModified === 'function') {
user.markModified('inbox.messages.' + req.params.id);
}
return typeof cb === 'function' ? cb(null, user.inbox.messages) : void 0;
};

View File

@@ -0,0 +1,26 @@
import i18n from '../i18n';
import _ from 'lodash';
module.exports = function (user, req, cb) {
var i, tag, tid;
tid = req.params.id;
i = _.findIndex(user.tags, {
id: tid
});
if (!~i) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTagNotFound', req.language)
}) : void 0;
}
tag = user.tags[i];
delete user.filters[tag.id];
user.tags.splice(i, 1);
_.each(user.tasks, function (task) {
return delete task.tags[tag.id];
});
_.each(['habits', 'dailys', 'todos', 'rewards'], function (type) {
return typeof user.markModified === 'function' ? user.markModified(type) : void 0;
});
return typeof cb === 'function' ? cb(null, user.tags) : void 0;
};

View File

@@ -0,0 +1,17 @@
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var i, ref, task;
task = user.tasks[(ref = req.params) !== null ? ref.id : void 0];
if (!task) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTaskNotFound', req.language)
}) : void 0;
}
i = user[task.type + 's'].indexOf(task);
if (~i) {
user[task.type + 's'].splice(i, 1);
}
return typeof cb === 'function' ? cb(null, {}) : void 0;
};

View File

@@ -0,0 +1,7 @@
module.exports = function (user, req, cb) {
delete user.preferences.webhooks[req.params.id];
if (typeof user.markModified === 'function') {
user.markModified('preferences.webhooks');
}
return typeof cb === 'function' ? cb(null, user.preferences.webhooks) : void 0;
};

View File

@@ -0,0 +1,13 @@
import splitWhitespace from '../libs/splitWhitespace';
import { capByLevel } from '../statHelpers';
import _ from 'lodash';
module.exports = function (user, req, cb) {
user.stats['class'] = 'warrior';
user.flags.classSelected = true;
user.preferences.disableClasses = true;
user.preferences.autoAllocate = true;
user.stats.str = capByLevel(user.stats.lvl);
user.stats.points = 0;
return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('stats flags preferences'))) : void 0;
};

View File

@@ -0,0 +1,52 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var item, key, message, ref, type;
ref = [req.params.type || 'equipped', req.params.key], type = ref[0], key = ref[1];
switch (type) {
case 'mount':
if (!user.items.mounts[key]) {
return typeof cb === 'function' ? cb({
code: 404,
message: ':You do not own this mount.'
}) : void 0;
}
user.items.currentMount = user.items.currentMount === key ? '' : key;
break;
case 'pet':
if (!user.items.pets[key]) {
return typeof cb === 'function' ? cb({
code: 404,
message: ':You do not own this pet.'
}) : void 0;
}
user.items.currentPet = user.items.currentPet === key ? '' : key;
break;
case 'costume':
case 'equipped':
item = content.gear.flat[key];
if (!user.items.gear.owned[key]) {
return typeof cb === 'function' ? cb({
code: 404,
message: ':You do not own this gear.'
}) : void 0;
}
if (user.items.gear[type][item.type] === key) {
user.items.gear[type][item.type] = item.type + '_base_0';
message = i18n.t('messageUnEquipped', {
itemText: item.text(req.language)
}, req.language);
} else {
user.items.gear[type][item.type] = item.key;
message = user.fns.handleTwoHanded(item, type, req);
}
if (typeof user.markModified === 'function') {
user.markModified('items.gear.' + type);
}
}
return typeof cb === 'function' ? cb((message ? {
code: 200,
message: message
} : null), user.items) : void 0;
};

78
common/script/ops/feed.js Normal file
View File

@@ -0,0 +1,78 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var egg, eggText, evolve, food, message, pet, petDisplayName, potion, potionText, ref, ref1, ref2, userPets;
ref = req.params, pet = ref.pet, food = ref.food;
food = content.food[food];
ref1 = pet.split('-'), egg = ref1[0], potion = ref1[1];
userPets = user.items.pets;
potionText = content.hatchingPotions[potion] ? content.hatchingPotions[potion].text() : potion;
eggText = content.eggs[egg] ? content.eggs[egg].text() : egg;
petDisplayName = i18n.t('petName', {
potion: potionText,
egg: eggText
});
if (!userPets[pet]) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messagePetNotFound', req.language)
}) : void 0;
}
if (!((ref2 = user.items.food) !== null ? ref2[food.key] : void 0)) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageFoodNotFound', req.language)
}) : void 0;
}
if (content.specialPets[pet]) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('messageCannotFeedPet', req.language)
}) : void 0;
}
if (user.items.mounts[pet]) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('messageAlreadyMount', req.language)
}) : void 0;
}
message = '';
evolve = function () {
userPets[pet] = -1;
user.items.mounts[pet] = true;
if (pet === user.items.currentPet) {
user.items.currentPet = '';
}
return message = i18n.t('messageEvolve', {
egg: petDisplayName
}, req.language);
};
if (food.key === 'Saddle') {
evolve();
} else {
if (food.target === potion || content.hatchingPotions[potion].premium) {
userPets[pet] += 5;
message = i18n.t('messageLikesFood', {
egg: petDisplayName,
foodText: food.text(req.language)
}, req.language);
} else {
userPets[pet] += 2;
message = i18n.t('messageDontEnjoyFood', {
egg: petDisplayName,
foodText: food.text(req.language)
}, req.language);
}
if (userPets[pet] >= 50 && !user.items.mounts[pet]) {
evolve();
}
}
user.items.food[food.key]--;
return typeof cb === 'function' ? cb({
code: 200,
message: message
}, {
value: userPets[pet]
}) : void 0;
};

View File

@@ -0,0 +1,17 @@
import _ from 'lodash';
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var i, tid;
tid = req.params.id;
i = _.findIndex(user.tags, {
id: tid
});
if (!~i) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTagNotFound', req.language)
}) : void 0;
}
return typeof cb === 'function' ? cb(null, user.tags[i]) : void 0;
};

View File

@@ -0,0 +1,3 @@
module.exports = function (user, req, cb) {
return typeof cb === 'function' ? cb(null, user.tags) : void 0;
};

View File

@@ -0,0 +1,39 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var egg, hatchingPotion, pet, ref;
ref = req.params, egg = ref.egg, hatchingPotion = ref.hatchingPotion;
if (!(egg && hatchingPotion)) {
return typeof cb === 'function' ? cb({
code: 400,
message: 'Please specify query.egg & query.hatchingPotion'
}) : void 0;
}
if (!(user.items.eggs[egg] > 0 && user.items.hatchingPotions[hatchingPotion] > 0)) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('messageMissingEggPotion', req.language)
}) : void 0;
}
if (content.hatchingPotions[hatchingPotion].premium && !content.dropEggs[egg]) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('messageInvalidEggPotionCombo', req.language)
}) : void 0;
}
pet = egg + '-' + hatchingPotion;
if (user.items.pets[pet] && user.items.pets[pet] > 0) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('messageAlreadyPet', req.language)
}) : void 0;
}
user.items.pets[pet] = 5;
user.items.eggs[egg]--;
user.items.hatchingPotions[hatchingPotion]--;
return typeof cb === 'function' ? cb({
code: 200,
message: i18n.t('messageHatched', req.language)
}, user.items) : void 0;
};

View File

@@ -0,0 +1,54 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function (user, req, cb, analytics) {
var analyticsData, key, ref, type;
ref = req.params, type = ref.type, key = ref.key;
if (!content.timeTravelStable[type]) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('typeNotAllowedHourglass', req.language) + JSON.stringify(_.keys(content.timeTravelStable))
}) : void 0;
}
if (!_.contains(_.keys(content.timeTravelStable[type]), key)) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t(type + 'NotAllowedHourglass', req.language)
}) : void 0;
}
if (user.items[type][key]) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t(type + 'AlreadyOwned', req.language)
}) : void 0;
}
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('notEnoughHourglasses', req.language)
}) : void 0;
}
user.purchased.plan.consecutive.trinkets--;
if (type === 'pets') {
user.items.pets[key] = 5;
}
if (type === 'mounts') {
user.items.mounts[key] = true;
}
analyticsData = {
uuid: user._id,
itemKey: key,
itemType: type,
acquireMethod: 'Hourglass',
category: 'behavior'
};
if (analytics) {
analytics.track('acquire item', analyticsData);
}
return typeof cb === 'function' ? cb({
code: 200,
message: i18n.t('hourglassPurchase', req.language)
}, _.pick(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0;
};

View File

@@ -1,5 +1,93 @@
import update from './update';
import sleep from './sleep';
import revive from './revive';
import reset from './reset';
import reroll from './reroll';
import rebirth from './rebirth';
import allocateNow from './allocateNow';
import clearCompleted from './clearCompleted';
import sortTask from './sortTask';
import updateTask from './updateTask';
import deleteTask from './deleteTask';
import addTask from './addTask';
import addTag from './addTag';
import sortTag from './sortTag';
import getTags from './getTags';
import getTag from './getTag';
import updateTag from './updateTag';
import deleteTag from './deleteTag';
import addWebhook from './addWebhook';
import updateWebhook from './updateWebhook';
import deleteWebhook from './deleteWebhook';
import addPushDevice from './addPushDevice';
import clearPMs from './clearPMs';
import deletePM from './deletePM';
import blockUser from './blockUser';
import feed from './feed';
import buySpecialSpell from './buySpecialSpell';
import purchase from './purchase';
import releasePets from './releasePets';
import releaseMounts from './releaseMounts';
import releaseBoth from './releaseBoth';
import buy from './buy';
import buyQuest from './buyQuest';
import buyMysterySet from './buyMysterySet';
import hourglassPurchase from './hourglassPurchase';
import sell from './sell';
import equip from './equip';
import hatch from './hatch';
import unlock from './unlock';
import changeClass from './changeClass';
import disableClasses from './disableClasses';
import allocate from './allocate';
import readCard from './readCard'; import readCard from './readCard';
import openMysteryItem from './openMysteryItem';
import score from './score';
module.exports = { module.exports = {
update,
sleep,
revive,
reset,
reroll,
rebirth,
allocateNow,
clearCompleted,
sortTask,
updateTask,
deleteTask,
addTask,
addTag,
sortTag,
getTags,
getTag,
updateTag,
deleteTag,
addWebhook,
updateWebhook,
deleteWebhook,
addPushDevice,
clearPMs,
deletePM,
blockUser,
feed,
buySpecialSpell,
purchase,
releasePets,
releaseMounts,
releaseBoth,
buy,
buyQuest,
buyMysterySet,
hourglassPurchase,
sell,
equip,
hatch,
unlock,
changeClass,
disableClasses,
allocate,
readCard, readCard,
openMysteryItem,
score,
}; };

View File

@@ -0,0 +1,32 @@
import content from '../content/index';
module.exports = function (user, req, cb, analytics) {
var analyticsData, item, ref, ref1;
item = (ref = user.purchased.plan) !== null ? (ref1 = ref.mysteryItems) !== null ? ref1.shift() : void 0 : void 0;
if (!item) {
return typeof cb === 'function' ? cb({
code: 400,
message: 'Empty'
}) : void 0;
}
item = content.gear.flat[item];
user.items.gear.owned[item.key] = true;
if (typeof user.markModified === 'function') {
user.markModified('purchased.plan.mysteryItems');
}
item.notificationType = 'Mystery';
analyticsData = {
uuid: user._id,
itemKey: item,
itemType: 'Subscriber Gear',
acquireMethod: 'Subscriber',
category: 'behavior'
};
if (analytics !== null) {
analytics.track('open mystery item', analyticsData);
}
if (typeof window !== 'undefined') {
(user._tmp !== null ? user._tmp : user._tmp = {}).drop = item;
}
return typeof cb === 'function' ? cb(null, user.items.gear.owned) : void 0;
};

View File

@@ -0,0 +1,107 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
import planGemLimits from '../libs/planGemLimits';
module.exports = function (user, req, cb, analytics) {
var analyticsData, convCap, convRate, item, key, price, ref, ref1, ref2, ref3, type;
ref = req.params, type = ref.type, key = ref.key;
if (type === 'gems' && key === 'gem') {
ref1 = planGemLimits, convRate = ref1.convRate, convCap = ref1.convCap;
convCap += user.purchased.plan.consecutive.gemCapExtra;
if (!((ref2 = user.purchased) !== null ? (ref3 = ref2.plan) !== null ? ref3.customerId : void 0 : void 0)) {
return typeof cb === 'function' ? cb({
code: 401,
message: 'Must subscribe to purchase gems with GP'
}, req) : void 0;
}
if (!(user.stats.gp >= convRate)) {
return typeof cb === 'function' ? cb({
code: 401,
message: 'Not enough Gold'
}) : void 0;
}
if (user.purchased.plan.gemsBought >= convCap) {
return typeof cb === 'function' ? cb({
code: 401,
message: 'You\'ve reached the Gold=>Gem conversion cap (' + convCap + ') for this month. We have this to prevent abuse / farming. The cap will reset within the first three days of next month.'
}) : void 0;
}
user.balance += .25;
user.purchased.plan.gemsBought++;
user.stats.gp -= convRate;
analyticsData = {
uuid: user._id,
itemKey: key,
acquireMethod: 'Gold',
goldCost: convRate,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('purchase gems', analyticsData);
}
return typeof cb === 'function' ? cb({
code: 200,
message: '+1 Gem'
}, _.pick(user, splitWhitespace('stats balance'))) : void 0;
}
if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food' && type !== 'quests' && type !== 'gear') {
return typeof cb === 'function' ? cb({
code: 404,
message: ':type must be in [eggs,hatchingPotions,food,quests,gear]'
}, req) : void 0;
}
if (type === 'gear') {
item = content.gear.flat[key];
if (user.items.gear.owned[key]) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('alreadyHave', req.language)
}) : void 0;
}
price = (item.twoHanded || item.gearSet === 'animal' ? 2 : 1) / 4;
} else {
item = content[type][key];
price = item.value / 4;
}
if (!item) {
return typeof cb === 'function' ? cb({
code: 404,
message: ':key not found for Content.' + type
}, req) : void 0;
}
if (!item.canBuy(user)) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('messageNotAvailable', req.language)
}) : void 0;
}
if ((user.balance < price) || !user.balance) {
return typeof cb === 'function' ? cb({
code: 403,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
}
user.balance -= price;
if (type === 'gear') {
user.items.gear.owned[key] = true;
} else {
if (!(user.items[type][key] > 0)) {
user.items[type][key] = 0;
}
user.items[type][key]++;
}
analyticsData = {
uuid: user._id,
itemKey: key,
itemType: 'Market',
acquireMethod: 'Gems',
gemCost: item.value,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('acquire item', analyticsData);
}
return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('items balance'))) : void 0;
};

View File

@@ -0,0 +1,100 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
import { capByLevel } from '../statHelpers';
import { MAX_LEVEL } from '../constants';
module.exports = function (user, req, cb, analytics) {
var analyticsData, flags, gear, lvl, stats;
if (user.balance < 2 && user.stats.lvl < MAX_LEVEL) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
}
analyticsData = {
uuid: user._id,
category: 'behavior'
};
if (user.stats.lvl < MAX_LEVEL) {
user.balance -= 2;
analyticsData.acquireMethod = 'Gems';
analyticsData.gemCost = 8;
} else {
analyticsData.gemCost = 0;
analyticsData.acquireMethod = '> 100';
}
if (analytics !== null) {
analytics.track('Rebirth', analyticsData);
}
lvl = capByLevel(user.stats.lvl);
_.each(user.tasks, function (task) {
if (task.type !== 'reward') {
task.value = 0;
}
if (task.type === 'daily') {
return task.streak = 0;
}
});
stats = user.stats;
stats.buffs = {};
stats.hp = 50;
stats.lvl = 1;
stats['class'] = 'warrior';
_.each(['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'], function (value) {
return stats[value] = 0;
});
// TODO during refactoring: move all gear code from rebirth() to its own function and then call it in reset() as well
gear = user.items.gear;
_.each(['equipped', 'costume'], function (type) {
gear[type] = {};
gear[type].armor = 'armor_base_0';
gear[type].weapon = 'weapon_warrior_0';
gear[type].head = 'head_base_0';
return gear[type].shield = 'shield_base_0';
});
if (user.items.currentPet) {
user.ops.equip({
params: {
type: 'pet',
key: user.items.currentPet
}
});
}
if (user.items.currentMount) {
user.ops.equip({
params: {
type: 'mount',
key: user.items.currentMount
}
});
}
_.each(gear.owned, function (v, k) {
if (gear.owned[k] && content.gear.flat[k].value) {
gear.owned[k] = false;
return true;
}
});
gear.owned.weapon_warrior_0 = true;
if (typeof user.markModified === 'function') {
user.markModified('items.gear.owned');
}
user.preferences.costume = false;
flags = user.flags;
if (!user.achievements.beastMaster) {
flags.rebirthEnabled = false;
}
flags.itemsEnabled = false;
flags.dropsEnabled = false;
flags.classSelected = false;
flags.levelDrops = {};
if (!user.achievements.rebirths) {
user.achievements.rebirths = 1;
user.achievements.rebirthLevel = lvl;
} else if (lvl > user.achievements.rebirthLevel || lvl === 100) {
user.achievements.rebirths++;
user.achievements.rebirthLevel = lvl;
}
user.stats.buffs = {};
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,50 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb, analytics) {
var analyticsData, animal, giveTriadBingo;
if (user.balance < 1.5 && !user.achievements.triadBingo) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
} else {
giveTriadBingo = true;
if (!user.achievements.triadBingo) {
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 6,
category: 'behavior'
};
if (typeof analytics !== 'undefined' && analytics !== null) {
analytics.track('release pets & mounts', analyticsData);
}
user.balance -= 1.5;
}
user.items.currentMount = '';
user.items.currentPet = '';
for (animal in content.pets) {
if (user.items.pets[animal] === -1) {
giveTriadBingo = false;
}
user.items.pets[animal] = 0;
user.items.mounts[animal] = null;
}
if (!user.achievements.beastMasterCount) {
user.achievements.beastMasterCount = 0;
}
user.achievements.beastMasterCount++;
if (!user.achievements.mountMasterCount) {
user.achievements.mountMasterCount = 0;
}
user.achievements.mountMasterCount++;
if (giveTriadBingo) {
if (!user.achievements.triadBingoCount) {
user.achievements.triadBingoCount = 0;
}
user.achievements.triadBingoCount++;
}
}
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,32 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb, analytics) {
var analyticsData, mount;
if (user.balance < 1) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
} else {
user.balance -= 1;
user.items.currentMount = '';
for (mount in content.pets) {
user.items.mounts[mount] = null;
}
if (!user.achievements.mountMasterCount) {
user.achievements.mountMasterCount = 0;
}
user.achievements.mountMasterCount++;
}
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('release mounts', analyticsData);
}
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,32 @@
import content from '../content/index';
import i18n from '../i18n';
module.exports = function (user, req, cb, analytics) {
var analyticsData, pet;
if (user.balance < 1) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
} else {
user.balance -= 1;
for (pet in content.pets) {
user.items.pets[pet] = 0;
}
if (!user.achievements.beastMasterCount) {
user.achievements.beastMasterCount = 0;
}
user.achievements.beastMasterCount++;
user.items.currentPet = '';
}
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('release pets', analyticsData);
}
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,29 @@
import i18n from '../i18n';
import _ from 'lodash';
module.exports = function (user, req, cb, analytics) {
var analyticsData;
if (user.balance < 1) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
}
user.balance--;
_.each(user.tasks, function (task) {
if (task.type !== 'reward') {
return task.value = 0;
}
});
user.stats.hp = 50;
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('Fortify Potion', analyticsData);
}
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,35 @@
import _ from 'lodash';
module.exports = function (user, req, cb) {
var gear;
user.habits = [];
user.dailys = [];
user.todos = [];
user.rewards = [];
user.stats.hp = 50;
user.stats.lvl = 1;
user.stats.gp = 0;
user.stats.exp = 0;
gear = user.items.gear;
_.each(['equipped', 'costume'], function (type) {
gear[type].armor = 'armor_base_0';
gear[type].weapon = 'weapon_base_0';
gear[type].head = 'head_base_0';
return gear[type].shield = 'shield_base_0';
});
if (typeof gear.owned === 'undefined') {
gear.owned = {};
}
_.each(gear.owned, function (v, k) {
if (gear.owned[k]) {
gear.owned[k] = false;
}
return true;
});
gear.owned.weapon_warrior_0 = true;
if (typeof user.markModified === 'function') {
user.markModified('items.gear.owned');
}
user.preferences.costume = false;
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,72 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
module.exports = function (user, req, cb, analytics) {
var analyticsData, base, cl, gearOwned, item, losableItems, lostItem, lostStat;
if (!(user.stats.hp <= 0)) {
return typeof cb === 'function' ? cb({
code: 400,
message: 'Cannot revive if not dead'
}) : void 0;
}
_.merge(user.stats, {
hp: 50,
exp: 0,
gp: 0
});
if (user.stats.lvl > 1) {
user.stats.lvl--;
}
lostStat = user.fns.randomVal(_.reduce(['str', 'con', 'per', 'int'], (function (m, k) {
if (user.stats[k]) {
m[k] = k;
}
return m;
}), {}));
if (lostStat) {
user.stats[lostStat]--;
}
cl = user.stats['class'];
gearOwned = (typeof (base = user.items.gear.owned).toObject === 'function' ? base.toObject() : void 0) || user.items.gear.owned;
losableItems = {};
_.each(gearOwned, function (v, k) {
var itm;
if (v) {
itm = content.gear.flat['' + k];
if (itm) {
if ((itm.value > 0 || k === 'weapon_warrior_0') && (itm.klass === cl || (itm.klass === 'special' && (!itm.specialClass || itm.specialClass === cl)) || itm.klass === 'armoire')) {
return losableItems['' + k] = '' + k;
}
}
}
});
lostItem = user.fns.randomVal(losableItems);
if (item = content.gear.flat[lostItem]) {
user.items.gear.owned[lostItem] = false;
if (user.items.gear.equipped[item.type] === lostItem) {
user.items.gear.equipped[item.type] = item.type + '_base_0';
}
if (user.items.gear.costume[item.type] === lostItem) {
user.items.gear.costume[item.type] = item.type + '_base_0';
}
}
if (typeof user.markModified === 'function') {
user.markModified('items.gear');
}
analyticsData = {
uuid: user._id,
lostItem: lostItem,
gaLabel: lostItem,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('Death', analyticsData);
}
return typeof cb === 'function' ? cb((item ? {
code: 200,
message: i18n.t('messageLostItem', {
itemText: item.text(req.language)
}, req.language)
} : null), user) : void 0;
};

221
common/script/ops/score.js Normal file
View File

@@ -0,0 +1,221 @@
import moment from 'moment';
import _ from 'lodash';
import i18n from '../i18n';
module.exports = function (user, req, cb) {
var addPoints, calculateDelta, calculateReverseDelta, changeTaskValue, delta, direction, gainMP, id, multiplier, num, options, ref, stats, subtractPoints, task, th;
ref = req.params, id = ref.id, direction = ref.direction;
task = user.tasks[id];
options = req.query || {};
_.defaults(options, {
times: 1,
cron: false
});
user._tmp = {};
stats = {
gp: +user.stats.gp,
hp: +user.stats.hp,
exp: +user.stats.exp
};
task.value = +task.value;
task.streak = ~~task.streak;
if (task.priority === null) {
task.priority = 1;
}
if (task.value > stats.gp && task.type === 'reward') {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('messageNotEnoughGold', req.language)
}) : void 0;
}
delta = 0;
calculateDelta = function () {
var currVal, nextDelta, ref1;
currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value;
nextDelta = Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
if (((ref1 = task.checklist) !== null ? ref1.length : void 0) > 0) {
if (direction === 'down' && task.type === 'daily' && options.cron) {
nextDelta *= 1 - _.reduce(task.checklist, (function (m, i) {
return m + (i.completed ? 1 : 0);
}), 0) / task.checklist.length;
}
if (task.type === 'todo') {
nextDelta *= 1 + _.reduce(task.checklist, (function (m, i) {
return m + (i.completed ? 1 : 0);
}), 0);
}
}
return nextDelta;
};
calculateReverseDelta = function () {
var calc, closeEnough, currVal, diff, nextDelta, ref1, testVal;
currVal = task.value < -47.27 ? -47.27 : task.value > 21.27 ? 21.27 : task.value;
testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
closeEnough = 0.00001;
while (true) {
calc = testVal + Math.pow(0.9747, testVal);
diff = currVal - calc;
if (Math.abs(diff) < closeEnough) {
break;
}
if (diff > 0) {
testVal -= diff;
} else {
testVal += diff;
}
}
nextDelta = testVal - currVal;
if (((ref1 = task.checklist) !== null ? ref1.length : void 0) > 0) {
if (task.type === 'todo') {
nextDelta *= 1 + _.reduce(task.checklist, (function (m, i) {
return m + (i.completed ? 1 : 0);
}), 0);
}
}
return nextDelta;
};
changeTaskValue = function () {
return _.times(options.times, function () {
var nextDelta, ref1;
nextDelta = !options.cron && direction === 'down' ? calculateReverseDelta() : calculateDelta();
if (task.type !== 'reward') {
if (user.preferences.automaticAllocation === true && user.preferences.allocationMode === 'taskbased' && !(task.type === 'todo' && direction === 'down')) {
user.stats.training[task.attribute] += nextDelta;
}
if (direction === 'up') {
user.party.quest.progress.up = user.party.quest.progress.up || 0;
if ((ref1 = task.type) === 'daily' || ref1 === 'todo') {
user.party.quest.progress.up += nextDelta * (1 + (user._statsComputed.str / 200));
}
if (task.type === 'habit') {
user.party.quest.progress.up += nextDelta * (0.5 + (user._statsComputed.str / 400));
}
}
task.value += nextDelta;
}
return delta += nextDelta;
});
};
addPoints = function () {
var _crit, afterStreak, currStreak, gpMod, intBonus, perBonus, streakBonus;
_crit = (delta > 0 ? user.fns.crit() : 1);
if (_crit > 1) {
user._tmp.crit = _crit;
}
intBonus = 1 + (user._statsComputed.int * .025);
stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6);
perBonus = 1 + user._statsComputed.per * .02;
gpMod = delta * task.priority * _crit * perBonus;
return stats.gp += task.streak ? (currStreak = direction === 'down' ? task.streak - 1 : task.streak, streakBonus = currStreak / 100 + 1, afterStreak = gpMod * streakBonus, currStreak > 0 ? gpMod > 0 ? user._tmp.streakBonus = afterStreak - gpMod : void 0 : void 0, afterStreak) : gpMod;
};
subtractPoints = function () {
var conBonus, hpMod;
conBonus = 1 - (user._statsComputed.con / 250);
if (conBonus < .1) {
conBonus = 0.1;
}
hpMod = delta * conBonus * task.priority * 2;
return stats.hp += Math.round(hpMod * 10) / 10;
};
gainMP = function (delta) {
delta *= user._tmp.crit || 1;
user.stats.mp += delta;
if (user.stats.mp >= user._statsComputed.maxMP) {
user.stats.mp = user._statsComputed.maxMP;
}
if (user.stats.mp < 0) {
return user.stats.mp = 0;
}
};
switch (task.type) {
case 'habit':
changeTaskValue();
if (delta > 0) {
addPoints();
} else {
subtractPoints();
}
gainMP(_.max([0.25, .0025 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1));
th = (task.history !== null ? task.history : task.history = []);
if (th[th.length - 1] && moment(th[th.length - 1].date).isSame(new Date, 'day')) {
th[th.length - 1].value = task.value;
} else {
th.push({
date: +(new Date),
value: task.value
});
}
if (typeof user.markModified === 'function') {
user.markModified('habits.' + (_.findIndex(user.habits, {
id: task.id
})) + '.history');
}
break;
case 'daily':
if (options.cron) {
changeTaskValue();
subtractPoints();
if (!user.stats.buffs.streaks) {
task.streak = 0;
}
} else {
changeTaskValue();
if (direction === 'down') {
delta = calculateDelta();
}
addPoints();
gainMP(_.max([1, .01 * user._statsComputed.maxMP]) * (direction === 'down' ? -1 : 1));
if (direction === 'up') {
task.streak = task.streak ? task.streak + 1 : 1;
if ((task.streak % 21) === 0) {
user.achievements.streak = user.achievements.streak ? user.achievements.streak + 1 : 1;
}
} else {
if ((task.streak % 21) === 0) {
user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0;
}
task.streak = task.streak ? task.streak - 1 : 0;
}
}
break;
case 'todo':
if (options.cron) {
changeTaskValue();
} else {
task.dateCompleted = direction === 'up' ? new Date : void 0;
changeTaskValue();
if (direction === 'down') {
delta = calculateDelta();
}
addPoints();
multiplier = _.max([
_.reduce(task.checklist, (function (m, i) {
return m + (i.completed ? 1 : 0);
}), 1), 1
]);
gainMP(_.max([multiplier, .01 * user._statsComputed.maxMP * multiplier]) * (direction === 'down' ? -1 : 1));
}
break;
case 'reward':
changeTaskValue();
stats.gp -= Math.abs(task.value);
num = parseFloat(task.value).toFixed(2);
if (stats.gp < 0) {
stats.hp += stats.gp;
stats.gp = 0;
}
}
user.fns.updateStats(stats, req);
if (typeof window === 'undefined') {
if (direction === 'up') {
user.fns.randomDrop({
task: task,
delta: delta
}, req);
}
}
if (typeof cb === 'function') {
cb(null, user);
}
return delta;
};

23
common/script/ops/sell.js Normal file
View File

@@ -0,0 +1,23 @@
import content from '../content/index';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function (user, req, cb) {
var key, ref, type;
ref = req.params, key = ref.key, type = ref.type;
if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food') {
return typeof cb === 'function' ? cb({
code: 404,
message: ':type not found. Must bes in [eggs, hatchingPotions, food]'
}) : void 0;
}
if (!user.items[type][key]) {
return typeof cb === 'function' ? cb({
code: 404,
message: ':key not found for user.items.' + type
}) : void 0;
}
user.items[type][key]--;
user.stats.gp += content[type][key].value;
return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('stats items'))) : void 0;
};

View File

@@ -0,0 +1,4 @@
module.exports = function (user, req, cb) {
user.preferences.sleep = !user.preferences.sleep;
return typeof cb === 'function' ? cb(null, {}) : void 0;
};

View File

@@ -0,0 +1,9 @@
module.exports = function (user, req, cb) {
var from, ref, to;
ref = req.query, to = ref.to, from = ref.from;
if (!((to !== null) && (from !== null))) {
return typeof cb === 'function' ? cb('?to=__&from=__ are required') : void 0;
}
user.tags.splice(to, 0, user.tags.splice(from, 1)[0]);
return typeof cb === 'function' ? cb(null, user.tags) : void 0;
};

View File

@@ -0,0 +1,39 @@
import i18n from '../i18n';
import preenTodos from '../libs/preenTodos';
module.exports = function (user, req, cb) {
var from, id, movedTask, preenedTasks, ref, task, tasks, to;
id = req.params.id;
ref = req.query, to = ref.to, from = ref.from;
task = user.tasks[id];
if (!task) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTaskNotFound', req.language)
}) : void 0;
}
if (!((to !== null) && (from !== null))) {
return typeof cb === 'function' ? cb('?to=__&from=__ are required') : void 0;
}
tasks = user[task.type + 's'];
if (task.type === 'todo' && tasks[from] !== task) {
preenedTasks = preenTodos(tasks);
if (to !== -1) {
to = tasks.indexOf(preenedTasks[to]);
}
from = tasks.indexOf(preenedTasks[from]);
}
if (tasks[from] !== task) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTaskNotFound', req.language)
}) : void 0;
}
movedTask = tasks.splice(from, 1)[0];
if (to === -1) {
tasks.push(movedTask);
} else {
tasks.splice(to, 0, movedTask);
}
return typeof cb === 'function' ? cb(null, tasks) : void 0;
};

View File

@@ -0,0 +1,63 @@
import i18n from '../i18n';
import _ from 'lodash';
import splitWhitespace from '../libs/splitWhitespace';
module.exports = function (user, req, cb, analytics) {
var alreadyOwns, analyticsData, cost, fullSet, k, path, split, v;
path = req.query.path;
fullSet = ~path.indexOf(',');
cost = ~path.indexOf('background.') ? fullSet ? 3.75 : 1.75 : fullSet ? 1.25 : 0.5;
alreadyOwns = !fullSet && user.fns.dotGet('purchased.' + path) === true;
if ((user.balance < cost || !user.balance) && !alreadyOwns) {
return typeof cb === 'function' ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
}
if (fullSet) {
_.each(path.split(','), function (p) {
if (~path.indexOf('gear.')) {
user.fns.dotSet('' + p, true);
true;
} else {
}
user.fns.dotSet('purchased.' + p, true);
return true;
});
} else {
if (alreadyOwns) {
split = path.split('.');
v = split.pop();
k = split.join('.');
if (k === 'background' && v === user.preferences.background) {
v = '';
}
user.fns.dotSet('preferences.' + k, v);
return typeof cb === 'function' ? cb(null, req) : void 0;
}
user.fns.dotSet('purchased.' + path, true);
}
user.balance -= cost;
if (~path.indexOf('gear.')) {
if (typeof user.markModified === 'function') {
user.markModified('gear.owned');
}
} else {
if (typeof user.markModified === 'function') {
user.markModified('purchased');
}
}
analyticsData = {
uuid: user._id,
itemKey: path,
itemType: 'customization',
acquireMethod: 'Gems',
gemCost: cost / .25,
category: 'behavior'
};
if (analytics !== null) {
analytics.track('acquire item', analyticsData);
}
return typeof cb === 'function' ? cb(null, _.pick(user, splitWhitespace('purchased preferences items'))) : void 0;
};

View File

@@ -0,0 +1,9 @@
import _ from 'lodash';
module.exports = function (user, req, cb) {
_.each(req.body, function (v, k) {
user.fns.dotSet(k, v);
return true;
});
return typeof cb === 'function' ? cb(null, user) : void 0;
};

View File

@@ -0,0 +1,18 @@
import i18n from '../i18n';
import _ from 'lodash';
module.exports = function (user, req, cb) {
var i, tid;
tid = req.params.id;
i = _.findIndex(user.tags, {
id: tid
});
if (!~i) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTagNotFound', req.language)
}) : void 0;
}
user.tags[i].name = req.body.name;
return typeof cb === 'function' ? cb(null, user.tags[i]) : void 0;
};

View File

@@ -0,0 +1,20 @@
import i18n from '../i18n';
import _ from 'lodash';
module.exports = function (user, req, cb) {
var ref, task;
if (!(task = user.tasks[(ref = req.params) !== null ? ref.id : void 0])) {
return typeof cb === 'function' ? cb({
code: 404,
message: i18n.t('messageTaskNotFound', req.language)
}) : void 0;
}
_.merge(task, _.omit(req.body, ['checklist', 'id', 'type']));
if (req.body.checklist) {
task.checklist = req.body.checklist;
}
if (typeof task.markModified === 'function') {
task.markModified('tags');
}
return typeof cb === 'function' ? cb(null, task) : void 0;
};

View File

@@ -0,0 +1,9 @@
import _ from 'lodash';
module.exports = function (user, req, cb) {
_.merge(user.preferences.webhooks[req.params.id], req.body);
if (typeof user.markModified === 'function') {
user.markModified('preferences.webhooks');
}
return typeof cb === 'function' ? cb(null, user.preferences.webhooks) : void 0;
};

View File

@@ -107,7 +107,7 @@ describe('user.ops.hourglassPurchase', () => {
}); });
}); });
it('buys a mount', (done) => { it.only('buys a mount', (done) => {
user.purchased.plan.consecutive.trinkets = 2; user.purchased.plan.consecutive.trinkets = 2;
user.ops.hourglassPurchase({params: {type: 'mounts', key: 'MantisShrimp-Base'}}, (response) => { user.ops.hourglassPurchase({params: {type: 'mounts', key: 'MantisShrimp-Base'}}, (response) => {