mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-14 21:27:23 +01:00
start fixing commong
This commit is contained in:
@@ -72,7 +72,7 @@
|
||||
"npm": "^6"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js .",
|
||||
"lint": "eslint --ext .js --fix ./website/server",
|
||||
"test": "npm run lint && gulp test && gulp apidoc",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
|
||||
@@ -7,4 +7,4 @@ if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-e
|
||||
pathToCommon = './script/index';
|
||||
}
|
||||
|
||||
module.exports = require(pathToCommon);
|
||||
module.exports = require(pathToCommon); // eslint-disable-line import/no-dynamic-require
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"habitrpg/lib/node",
|
||||
]
|
||||
}
|
||||
5
website/common/script/.eslintrc.js
Normal file
5
website/common/script/.eslintrc.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"habitrpg/lib/node",
|
||||
]
|
||||
}
|
||||
@@ -12,7 +12,8 @@ export const MIN_SHORTNAME_SIZE_FOR_CHALLENGES = 3;
|
||||
|
||||
export const CHAT_FLAG_LIMIT_FOR_HIDING = 2; // hide posts that have this many flags
|
||||
export const CHAT_FLAG_FROM_MOD = 5; // a flag from a moderator counts as this many flags
|
||||
export const CHAT_FLAG_FROM_SHADOW_MUTE = 10; // a shadow-muted user's post starts with this many flags
|
||||
// a shadow-muted user's post starts with this many flags
|
||||
export const CHAT_FLAG_FROM_SHADOW_MUTE = 10;
|
||||
// @TODO use those constants to replace hard-coded numbers
|
||||
|
||||
export const SUPPORTED_SOCIAL_NETWORKS = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import prefill from './prefill.js';
|
||||
import prefill from './prefill';
|
||||
|
||||
export default prefill({
|
||||
none: {},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import prefill from '../prefill.js';
|
||||
import prefill from '../prefill';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefill from '../prefill.js';
|
||||
import sets from '../sets.js';
|
||||
import prefill from '../prefill';
|
||||
import sets from '../sets';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sets from '../sets.js';
|
||||
import prefill from '../prefill.js';
|
||||
import sets from '../sets';
|
||||
import prefill from '../prefill';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sets from '../sets.js';
|
||||
import prefill from '../prefill.js';
|
||||
import sets from '../sets';
|
||||
import prefill from '../prefill';
|
||||
|
||||
export default prefill({
|
||||
white: {},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import prefill from '../prefill.js';
|
||||
import prefill from '../prefill';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import bangs from './bangs.js';
|
||||
import base from './base.js';
|
||||
import beard from './beard.js';
|
||||
import color from './color.js';
|
||||
import flower from './flower.js';
|
||||
import mustache from './mustache.js';
|
||||
import bangs from './bangs';
|
||||
import base from './base';
|
||||
import beard from './beard';
|
||||
import color from './color';
|
||||
import flower from './flower';
|
||||
import mustache from './mustache';
|
||||
|
||||
export default {
|
||||
color,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sets from '../sets.js';
|
||||
import prefill from '../prefill.js';
|
||||
import sets from '../sets';
|
||||
import prefill from '../prefill';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefill from '../prefill.js';
|
||||
import sets from '../sets.js';
|
||||
import prefill from '../prefill';
|
||||
import sets from '../sets';
|
||||
|
||||
export default prefill({
|
||||
0: {},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import forOwn from 'lodash/forOwn';
|
||||
import clone from 'lodash/clone';
|
||||
import hair from './hair';
|
||||
import shirts from './shirt.js';
|
||||
import skins from './skin.js';
|
||||
import sizes from './size.js';
|
||||
import backgrounds from './backgrounds.js';
|
||||
import chairs from './chair.js';
|
||||
import shirts from './shirt';
|
||||
import skins from './skin';
|
||||
import sizes from './size';
|
||||
import backgrounds from './backgrounds';
|
||||
import chairs from './chair';
|
||||
|
||||
const reorderedBgs = {};
|
||||
|
||||
|
||||
@@ -2,22 +2,36 @@ import t from '../translation';
|
||||
import prefill from './prefill';
|
||||
|
||||
export default prefill({
|
||||
baseHair1: {setPrice: 5, text: t('hairSet1')},
|
||||
baseHair2: {setPrice: 5, text: t('hairSet2')},
|
||||
baseHair3: {setPrice: 5, text: t('hairSet3')},
|
||||
facialHair: {setPrice: 5, text: t('bodyFacialHair')},
|
||||
specialShirts: {setPrice: 5, text: t('specialShirts')},
|
||||
winterHairColors: {setPrice: 5, availableUntil: '2016-01-01'},
|
||||
pastelHairColors: {setPrice: 5, availableUntil: '2016-01-01'},
|
||||
rainbowHairColors: {setPrice: 5, text: t('rainbowColors')},
|
||||
shimmerHairColors: {setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('shimmerColors')},
|
||||
hauntedHairColors: {setPrice: 5, availableFrom: '2019-10-08', availableUntil: '2019-11-02', text: t('hauntedColors')},
|
||||
winteryHairColors: {setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winteryColors')},
|
||||
rainbowSkins: {setPrice: 5, text: t('rainbowSkins')},
|
||||
animalSkins: {setPrice: 5, text: t('animalSkins')},
|
||||
pastelSkins: {setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('pastelSkins')},
|
||||
spookySkins: {setPrice: 5, availableUntil: '2016-01-01', text: t('spookySkins')},
|
||||
supernaturalSkins: {setPrice: 5, availableFrom: '2019-10-08', availableUntil: '2019-11-02', text: t('supernaturalSkins')},
|
||||
splashySkins: {setPrice: 5, availableFrom: '2019-07-02', availableUntil: '2019-08-02', text: t('splashySkins')},
|
||||
winterySkins: {setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winterySkins')},
|
||||
baseHair1: { setPrice: 5, text: t('hairSet1') },
|
||||
baseHair2: { setPrice: 5, text: t('hairSet2') },
|
||||
baseHair3: { setPrice: 5, text: t('hairSet3') },
|
||||
facialHair: { setPrice: 5, text: t('bodyFacialHair') },
|
||||
specialShirts: { setPrice: 5, text: t('specialShirts') },
|
||||
winterHairColors: { setPrice: 5, availableUntil: '2016-01-01' },
|
||||
pastelHairColors: { setPrice: 5, availableUntil: '2016-01-01' },
|
||||
rainbowHairColors: { setPrice: 5, text: t('rainbowColors') },
|
||||
shimmerHairColors: {
|
||||
setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('shimmerColors'),
|
||||
},
|
||||
hauntedHairColors: {
|
||||
setPrice: 5, availableFrom: '2019-10-08', availableUntil: '2019-11-02', text: t('hauntedColors'),
|
||||
},
|
||||
winteryHairColors: {
|
||||
setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winteryColors'),
|
||||
},
|
||||
rainbowSkins: { setPrice: 5, text: t('rainbowSkins') },
|
||||
animalSkins: { setPrice: 5, text: t('animalSkins') },
|
||||
pastelSkins: {
|
||||
setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('pastelSkins'),
|
||||
},
|
||||
spookySkins: { setPrice: 5, availableUntil: '2016-01-01', text: t('spookySkins') },
|
||||
supernaturalSkins: {
|
||||
setPrice: 5, availableFrom: '2019-10-08', availableUntil: '2019-11-02', text: t('supernaturalSkins'),
|
||||
},
|
||||
splashySkins: {
|
||||
setPrice: 5, availableFrom: '2019-07-02', availableUntil: '2019-08-02', text: t('splashySkins'),
|
||||
},
|
||||
winterySkins: {
|
||||
setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winterySkins'),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sets from './sets.js';
|
||||
import prefill from './prefill.js';
|
||||
import sets from './sets';
|
||||
import prefill from './prefill';
|
||||
|
||||
export default prefill({
|
||||
black: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefill from './prefill.js';
|
||||
import sets from './sets.js';
|
||||
import prefill from './prefill';
|
||||
import sets from './sets';
|
||||
|
||||
export default prefill({
|
||||
/* eslint-disable quote-props */
|
||||
|
||||
@@ -10,7 +10,7 @@ const faq = {
|
||||
},
|
||||
};
|
||||
|
||||
for (let i = 0; i <= NUMBER_OF_QUESTIONS; i++) {
|
||||
for (let i = 0; i <= NUMBER_OF_QUESTIONS; i += 1) {
|
||||
const question = {
|
||||
question: t(`faqQuestion${i}`),
|
||||
ios: t(`iosFaqAnswer${i}`),
|
||||
|
||||
@@ -29,7 +29,8 @@ const gear = {
|
||||
};
|
||||
|
||||
/*
|
||||
The gear is exported as a tree (defined above), and a flat list (eg, {weapon_healer_1: .., shield_special_0: ...}) since
|
||||
The gear is exported as a tree (defined above), and a flat list
|
||||
(eg, {weapon_healer_1: .., shield_special_0: ...}) since
|
||||
they are needed in different forms at different points in the app
|
||||
*/
|
||||
const flat = {};
|
||||
@@ -61,8 +62,11 @@ each(GEAR_TYPES, type => {
|
||||
|
||||
item.canOwn = user => {
|
||||
const userHasOwnedItem = ownsItem(key)(user);
|
||||
const eventIsCurrent = moment().isAfter(item.event.start) && moment().isBefore(item.event.end);
|
||||
const compatibleWithUserClass = item.specialClass ? user.stats.class === item.specialClass : true;
|
||||
const eventIsCurrent = moment()
|
||||
.isAfter(item.event.start) && moment().isBefore(item.event.end);
|
||||
const compatibleWithUserClass = item.specialClass
|
||||
? user.stats.class === item.specialClass
|
||||
: true;
|
||||
|
||||
return _canOwn(user) && (userHasOwnedItem || eventIsCurrent) && compatibleWithUserClass;
|
||||
};
|
||||
|
||||
@@ -261,7 +261,10 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
110: {
|
||||
rewardKey: ['Pet_Egg_Cactus', 'Pet_Egg_Dragon', 'Pet_Egg_Wolf'],
|
||||
reward: [api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig, api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf],
|
||||
reward: [
|
||||
api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig,
|
||||
api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf,
|
||||
],
|
||||
rewardName: 'oneOfAllPetEggs',
|
||||
assignReward: function assignReward (user) {
|
||||
if (!user.items.eggs.BearCub) user.items.eggs.BearCub = 0;
|
||||
@@ -388,7 +391,10 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
150: {
|
||||
rewardKey: ['shop_head_special_clandestineCowl', 'shop_armor_special_sneakthiefRobes'],
|
||||
reward: [api.gear.flat.head_special_clandestineCowl, api.gear.flat.armor_special_sneakthiefRobes],
|
||||
reward: [
|
||||
api.gear.flat.head_special_clandestineCowl,
|
||||
api.gear.flat.armor_special_sneakthiefRobes,
|
||||
],
|
||||
assignReward: function assignReward (user) {
|
||||
user.items.gear.owned.head_special_clandestineCowl = true; // eslint-disable-line camelcase
|
||||
user.items.gear.owned.armor_special_sneakthiefRobes = true; // eslint-disable-line camelcase
|
||||
@@ -406,10 +412,13 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
170: {
|
||||
rewardKey: ['shop_head_special_snowSovereignCrown', 'shop_armor_special_snowSovereignRobes'],
|
||||
reward: [api.gear.flat.head_special_snowSovereignCrown, api.gear.flat.armor_special_snowSovereignRobes],
|
||||
reward: [
|
||||
api.gear.flat.head_special_snowSovereignCrown,
|
||||
api.gear.flat.armor_special_snowSovereignRobes,
|
||||
],
|
||||
assignReward: function assignReward (user) {
|
||||
user.items.gear.owned.head_special_snowSovereignCrown = true; // eslint-disable-line camelcase
|
||||
user.items.gear.owned.armor_special_snowSovereignRobes = true; // eslint-disable-line camelcase
|
||||
user.items.gear.owned.head_special_snowSovereignCrown = true; // eslint-disable-line camelcase, max-len
|
||||
user.items.gear.owned.armor_special_snowSovereignRobes = true; // eslint-disable-line camelcase, max-len
|
||||
if (user.markModified) user.markModified('items.gear.owned');
|
||||
},
|
||||
},
|
||||
@@ -451,7 +460,10 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
240: {
|
||||
rewardKey: ['shop_weapon_special_nomadsScimitar', 'shop_armor_special_nomadsCuirass'],
|
||||
reward: [api.gear.flat.weapon_special_nomadsScimitar, api.gear.flat.armor_special_nomadsCuirass],
|
||||
reward: [
|
||||
api.gear.flat.weapon_special_nomadsScimitar,
|
||||
api.gear.flat.armor_special_nomadsCuirass,
|
||||
],
|
||||
assignReward: function assignReward (user) {
|
||||
user.items.gear.owned.weapon_special_nomadsScimitar = true; // eslint-disable-line camelcase
|
||||
user.items.gear.owned.armor_special_nomadsCuirass = true; // eslint-disable-line camelcase
|
||||
@@ -468,7 +480,11 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
280: {
|
||||
rewardKey: ['Pet_Food_Meat', 'Pet_Food_Potatoe', 'Pet_Food_Milk'],
|
||||
reward: [api.food.Meat, api.food.CottonCandyBlue, api.food.CottonCandyPink, api.food.Potatoe, api.food.Honey, api.food.Strawberry, api.food.Chocolate, api.food.Fish, api.food.Milk, api.food.RottenMeat],
|
||||
reward: [
|
||||
api.food.Meat, api.food.CottonCandyBlue, api.food.CottonCandyPink,
|
||||
api.food.Potatoe, api.food.Honey, api.food.Strawberry, api.food.Chocolate,
|
||||
api.food.Fish, api.food.Milk, api.food.RottenMeat,
|
||||
],
|
||||
rewardName: 'threeOfEachFood',
|
||||
assignReward: function assignReward (user) {
|
||||
if (!user.items.food.Meat) user.items.food.Meat = 0;
|
||||
@@ -496,7 +512,10 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
300: {
|
||||
rewardKey: ['Pet_Egg_Cactus', 'Pet_Egg_Dragon', 'Pet_Egg_Wolf'],
|
||||
reward: [api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig, api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf],
|
||||
reward: [
|
||||
api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig,
|
||||
api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf,
|
||||
],
|
||||
rewardName: 'twoOfAllPetEggs',
|
||||
assignReward: function assignReward (user) {
|
||||
if (!user.items.eggs.BearCub) user.items.eggs.BearCub = 0;
|
||||
@@ -549,7 +568,10 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
380: {
|
||||
rewardKey: ['Pet_Egg_Cactus', 'Pet_Egg_Dragon', 'Pet_Egg_Wolf'],
|
||||
reward: [api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig, api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf],
|
||||
reward: [
|
||||
api.eggs.BearCub, api.eggs.Cactus, api.eggs.Dragon, api.eggs.FlyingPig,
|
||||
api.eggs.Fox, api.eggs.LionCub, api.eggs.PandaCub, api.eggs.TigerCub, api.eggs.Wolf,
|
||||
],
|
||||
rewardName: 'threeOfAllPetEggs',
|
||||
assignReward: function assignReward (user) {
|
||||
if (!user.items.eggs.BearCub) user.items.eggs.BearCub = 0;
|
||||
@@ -575,7 +597,11 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
400: {
|
||||
rewardKey: ['Pet_Food_Meat', 'Pet_Food_Potatoe', 'Pet_Food_Milk'],
|
||||
reward: [api.food.Meat, api.food.CottonCandyBlue, api.food.CottonCandyPink, api.food.Potatoe, api.food.Honey, api.food.Strawberry, api.food.Chocolate, api.food.Fish, api.food.Milk, api.food.RottenMeat],
|
||||
reward: [
|
||||
api.food.Meat, api.food.CottonCandyBlue, api.food.CottonCandyPink,
|
||||
api.food.Potatoe, api.food.Honey, api.food.Strawberry, api.food.Chocolate,
|
||||
api.food.Fish, api.food.Milk, api.food.RottenMeat,
|
||||
],
|
||||
rewardName: 'fourOfEachFood',
|
||||
assignReward: function assignReward (user) {
|
||||
if (!user.items.food.Meat) user.items.food.Meat = 0;
|
||||
@@ -638,10 +664,12 @@ export default function getLoginIncentives (api) {
|
||||
},
|
||||
},
|
||||
};
|
||||
// When the final check-in prize is added here, change checkinReceivedAllRewardsMessage in website/common/locales/en/loginIncentives.json
|
||||
// When the final check-in prize is added here,
|
||||
// change checkinReceivedAllRewardsMessage in website/common/locales/en/loginIncentives.json
|
||||
// to say "You have received the final Check-In prize!". Confirm the message with Lemoness first.
|
||||
|
||||
// Add reference link to next reward and add filler days so we have a map to reference the next reward from any day
|
||||
// Add reference link to next reward and add filler days
|
||||
// so we have a map to reference the next reward from any day
|
||||
// We could also, use a list, but then we would be cloning each of the rewards.
|
||||
// Create a new array if we want the loginIncentives to be immutable in the future
|
||||
let nextRewardKey;
|
||||
|
||||
@@ -3505,14 +3505,15 @@ const quests = {
|
||||
};
|
||||
|
||||
each(quests, (v, key) => {
|
||||
let b;
|
||||
defaults(v, {
|
||||
key,
|
||||
canBuy () {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
b = v.boss;
|
||||
|
||||
const b = v.boss;
|
||||
|
||||
if (b) {
|
||||
defaults(b, {
|
||||
str: 1,
|
||||
|
||||
@@ -11,23 +11,32 @@ import updateStats from '../fns/updateStats';
|
||||
---------------------------------------------------------------
|
||||
Text, notes, and mana are obvious. The rest:
|
||||
|
||||
* {target}: one of [task, self, party, user]. This is very important, because if the cast() function is expecting one
|
||||
thing and receives another, it will cause errors. `self` is used for self buffs, multi-task debuffs, AOEs (eg, meteor-shower),
|
||||
* {target}: one of [task, self, party, user].
|
||||
* This is very important, because if the cast() function is expecting one
|
||||
thing and receives another, it will cause errors.
|
||||
`self` is used for self buffs, multi-task debuffs, AOEs (eg, meteor-shower),
|
||||
etc. Basically, use self for anything that's not [task, party, user] and is an instant-cast
|
||||
|
||||
* {cast}: the function that's run to perform the ability's action. This is pretty slick - because this is exported to the
|
||||
web, this function can be performed on the client and on the server. `user` param is self (needed for determining your
|
||||
own stats for effectiveness of cast), and `target` param is one of [task, party, user]. In the case of `self` skills,
|
||||
you act on `user` instead of `target`. You can trust these are the correct objects, as long as the `target` attr of the
|
||||
spell is correct. Take a look at habitrpg/website/server/models/user.js and habitrpg/website/server/models/task.js for what attributes are
|
||||
available on each model. Note `task.value` is its "redness". If party is passed in, it's an array of users,
|
||||
* {cast}: the function that's run to perform the ability's action.
|
||||
This is pretty slick - because this is exported to the
|
||||
web, this function can be performed on the client and on the server.
|
||||
`user` param is self (needed for determining your
|
||||
own stats for effectiveness of cast), and `target` param is one of [task, party, user].
|
||||
In the case of `self` skills,
|
||||
you act on `user` instead of `target`. You can trust these are the correct objects,
|
||||
as long as the `target` attr of the
|
||||
spell is correct. Take a look at habitrpg/website/server/models/user.js and
|
||||
habitrpg/website/server/models/task.js for what attributes are
|
||||
available on each model. Note `task.value` is its "redness".
|
||||
If party is passed in, it's an array of users,
|
||||
so you'll want to iterate over them like: `_.each(target,function(member){...})`
|
||||
|
||||
Note, user.stats.mp is docked after automatically (it's appended to functions automatically down below in an _.each)
|
||||
Note, user.stats.mp is docked after automatically
|
||||
(it's appended to functions automatically down below in an _.each)
|
||||
*/
|
||||
|
||||
function diminishingReturns (bonus, max, halfway) {
|
||||
if (!halfway) halfway = max / 2;
|
||||
if (!halfway) halfway = max / 2; // eslint-disable-line no-param-reassign
|
||||
return max * (bonus / (bonus + halfway));
|
||||
}
|
||||
|
||||
@@ -274,8 +283,8 @@ spells.special = {
|
||||
target.stats.buffs.shinySeed = false;
|
||||
target.stats.buffs.seafoam = false;
|
||||
if (!target.achievements.snowball) target.achievements.snowball = 0;
|
||||
target.achievements.snowball++;
|
||||
user.items.special.snowball--;
|
||||
target.achievements.snowball += 1;
|
||||
user.items.special.snowball -= 1;
|
||||
},
|
||||
},
|
||||
salt: {
|
||||
@@ -305,8 +314,8 @@ spells.special = {
|
||||
target.stats.buffs.shinySeed = false;
|
||||
target.stats.buffs.seafoam = false;
|
||||
if (!target.achievements.spookySparkles) target.achievements.spookySparkles = 0;
|
||||
target.achievements.spookySparkles++;
|
||||
user.items.special.spookySparkles--;
|
||||
target.achievements.spookySparkles += 1;
|
||||
user.items.special.spookySparkles -= 1;
|
||||
},
|
||||
},
|
||||
opaquePotion: {
|
||||
@@ -336,8 +345,8 @@ spells.special = {
|
||||
target.stats.buffs.shinySeed = true;
|
||||
target.stats.buffs.seafoam = false;
|
||||
if (!target.achievements.shinySeed) target.achievements.shinySeed = 0;
|
||||
target.achievements.shinySeed++;
|
||||
user.items.special.shinySeed--;
|
||||
target.achievements.shinySeed += 1;
|
||||
user.items.special.shinySeed -= 1;
|
||||
},
|
||||
},
|
||||
petalFreePotion: {
|
||||
@@ -367,8 +376,8 @@ spells.special = {
|
||||
target.stats.buffs.shinySeed = false;
|
||||
target.stats.buffs.seafoam = true;
|
||||
if (!target.achievements.seafoam) target.achievements.seafoam = 0;
|
||||
target.achievements.seafoam++;
|
||||
user.items.special.seafoam--;
|
||||
target.achievements.seafoam += 1;
|
||||
user.items.special.seafoam -= 1;
|
||||
},
|
||||
},
|
||||
sand: {
|
||||
@@ -395,11 +404,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.nye) user.achievements.nye = 0;
|
||||
user.achievements.nye++;
|
||||
user.achievements.nye += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.nye) u.achievements.nye = 0;
|
||||
u.achievements.nye++;
|
||||
u.achievements.nye += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -432,11 +441,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.valentine) user.achievements.valentine = 0;
|
||||
user.achievements.valentine++;
|
||||
user.achievements.valentine += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.valentine) u.achievements.valentine = 0;
|
||||
u.achievements.valentine++;
|
||||
u.achievements.valentine += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -469,11 +478,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.greeting) user.achievements.greeting = 0;
|
||||
user.achievements.greeting++;
|
||||
user.achievements.greeting += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.greeting) u.achievements.greeting = 0;
|
||||
u.achievements.greeting++;
|
||||
u.achievements.greeting += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -506,11 +515,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.thankyou) user.achievements.thankyou = 0;
|
||||
user.achievements.thankyou++;
|
||||
user.achievements.thankyou += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.thankyou) u.achievements.thankyou = 0;
|
||||
u.achievements.thankyou++;
|
||||
u.achievements.thankyou += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -543,11 +552,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.birthday) user.achievements.birthday = 0;
|
||||
user.achievements.birthday++;
|
||||
user.achievements.birthday += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.birthday) u.achievements.birthday = 0;
|
||||
u.achievements.birthday++;
|
||||
u.achievements.birthday += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -580,11 +589,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.congrats) user.achievements.congrats = 0;
|
||||
user.achievements.congrats++;
|
||||
user.achievements.congrats += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.congrats) u.achievements.congrats = 0;
|
||||
u.achievements.congrats++;
|
||||
u.achievements.congrats += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -617,11 +626,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.getwell) user.achievements.getwell = 0;
|
||||
user.achievements.getwell++;
|
||||
user.achievements.getwell += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.getwell) u.achievements.getwell = 0;
|
||||
u.achievements.getwell++;
|
||||
u.achievements.getwell += 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -654,11 +663,11 @@ spells.special = {
|
||||
cast (user, target) {
|
||||
if (user === target) {
|
||||
if (!user.achievements.goodluck) user.achievements.goodluck = 0;
|
||||
user.achievements.goodluck++;
|
||||
user.achievements.goodluck += 1;
|
||||
} else {
|
||||
each([user, target], u => {
|
||||
if (!u.achievements.goodluck) u.achievements.goodluck = 0;
|
||||
u.achievements.goodluck++;
|
||||
u.achievements.goodluck += 1;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,6 @@ const subscriptionBlocks = {
|
||||
},
|
||||
};
|
||||
|
||||
each(subscriptionBlocks, (b, k) => b.key = k);
|
||||
each(subscriptionBlocks, (b, k) => { b.key = k; });
|
||||
|
||||
export default subscriptionBlocks;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import t from './translation';
|
||||
|
||||
export const tasksByCategory = {
|
||||
export const tasksByCategory = { // eslint-disable-line import/prefer-default-export
|
||||
work: [
|
||||
{
|
||||
type: 'habit',
|
||||
|
||||
@@ -9,9 +9,11 @@ import gear from './gear';
|
||||
|
||||
const mystery = mysterySets;
|
||||
|
||||
each(mystery, (v, k) => v.items = filter(gear.flat, {
|
||||
each(mystery, (v, k) => {
|
||||
v.items = filter(gear.flat, {
|
||||
mystery: k,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
const timeTravelerStore = user => {
|
||||
let ownedKeys;
|
||||
|
||||
@@ -10,7 +10,7 @@ export function beastMasterProgress (pets = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, animal => {
|
||||
if (pets[animal] > 0 || pets[animal] === -1) count++;
|
||||
if (pets[animal] > 0 || pets[animal] === -1) count += 1;
|
||||
});
|
||||
|
||||
return count;
|
||||
@@ -20,7 +20,7 @@ export function beastCount (pets = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, animal => {
|
||||
if (pets[animal] > 0) count++;
|
||||
if (pets[animal] > 0) count += 1;
|
||||
});
|
||||
|
||||
return count;
|
||||
@@ -30,7 +30,7 @@ export function dropPetsCurrentlyOwned (pets = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, animal => {
|
||||
if (pets[animal] > 0) count++;
|
||||
if (pets[animal] > 0) count += 1;
|
||||
});
|
||||
|
||||
return count;
|
||||
@@ -40,7 +40,7 @@ export function mountMasterProgress (mounts = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, animal => {
|
||||
if (mounts[animal]) count++;
|
||||
if (mounts[animal]) count += 1;
|
||||
});
|
||||
|
||||
return count;
|
||||
|
||||
@@ -23,8 +23,10 @@ export const DAY_MAPPING_STRING_TO_NUMBER = invert(DAY_MAPPING);
|
||||
|
||||
/*
|
||||
Each time we perform date maths (cron, task-due-days, etc), we need to consider user preferences.
|
||||
Specifically {dayStart} (custom day start) and {timezoneOffset}. This function sanitizes / defaults those values.
|
||||
{now} is also passed in for various purposes, one example being the test scripts scripts testing different "now" times.
|
||||
Specifically {dayStart} (custom day start) and {timezoneOffset}.
|
||||
This function sanitizes / defaults those values.
|
||||
{now} is also passed in for various purposes,
|
||||
one example being the test scripts scripts testing different "now" times.
|
||||
*/
|
||||
|
||||
function sanitizeOptions (o) {
|
||||
@@ -34,7 +36,7 @@ function sanitizeOptions (o) {
|
||||
let timezoneOffset;
|
||||
const timezoneOffsetDefault = Number(moment().zone());
|
||||
|
||||
if (isFinite(o.timezoneOffsetOverride)) {
|
||||
if (Number.isFinite(o.timezoneOffsetOverride)) {
|
||||
timezoneOffset = Number(o.timezoneOffsetOverride);
|
||||
} else if (Number.isFinite(o.timezoneOffset)) {
|
||||
timezoneOffset = Number(o.timezoneOffset);
|
||||
@@ -62,10 +64,17 @@ export function startOfWeek (options = {}) {
|
||||
}
|
||||
|
||||
/*
|
||||
This is designed for use with any date that has an important time portion (e.g., when comparing the current date-time with the previous cron's date-time for determing if cron should run now).
|
||||
It changes the time portion of the date-time to be the Custom Day Start hour, so that the date-time is now the user's correct start of day.
|
||||
It SUBTRACTS a day if the date-time's original hour is before CDS (e.g., if your CDS is 5am and it's currently 4am, it's still the previous day).
|
||||
This is NOT suitable for manipulating any dates that are displayed to the user as a date with no time portion, such as a Daily's Start Dates (e.g., a Start Date of today shows only the date, so it should be considered to be today even if the hidden time portion is before CDS).
|
||||
This is designed for use with any date that has an important time portion
|
||||
(e.g., when comparing the current date-time with the previous cron's date-time
|
||||
for determing if cron should run now).
|
||||
It changes the time portion of the date-time to be the Custom Day Start hour,
|
||||
so that the date-time is now the user's correct start of day.
|
||||
It SUBTRACTS a day if the date-time's original hour is before CDS
|
||||
(e.g., if your CDS is 5am and it's currently 4am, it's still the previous day).
|
||||
This is NOT suitable for manipulating any dates that are displayed to the user
|
||||
as a date with no time portion, such as a Daily's Start Dates
|
||||
(e.g., a Start Date of today shows only the date,
|
||||
so it should be considered to be today even if the hidden time portion is before CDS).
|
||||
*/
|
||||
|
||||
export function startOfDay (options = {}) {
|
||||
@@ -92,7 +101,8 @@ export function daysSince (yesterday, options = {}) {
|
||||
}
|
||||
|
||||
/*
|
||||
Should the user do this task on this date, given the task's repeat options and user.preferences.dayStart?
|
||||
Should the user do this task on this date,
|
||||
given the task's repeat options and user.preferences.dayStart?
|
||||
*/
|
||||
|
||||
export function shouldDo (day, dailyTask, options = {}) {
|
||||
@@ -102,9 +112,12 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
const o = sanitizeOptions(options);
|
||||
const startOfDayWithCDSTime = startOfDay(defaults({ now: day }, o));
|
||||
|
||||
// The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it.
|
||||
// Therefore, we must also ignore the time portion of the user's day start (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times.
|
||||
// NB: The user's day start date has already been converted to the PREVIOUS day's date if the time portion was before CDS.
|
||||
// The time portion of the Start Date is never visible to
|
||||
// or modifiable by the user so we must ignore it.
|
||||
// Therefore, we must also ignore the time portion of the user's day start
|
||||
// (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times.
|
||||
// NB: The user's day start date has already been converted to the PREVIOUS
|
||||
// day's date if the time portion was before CDS.
|
||||
|
||||
const startDate = moment(dailyTask.startDate).zone(o.timezoneOffset).startOf('day');
|
||||
|
||||
@@ -115,7 +128,7 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
const daysOfTheWeek = [];
|
||||
if (dailyTask.repeat) {
|
||||
for (const [repeatDay, active] of Object.entries(dailyTask.repeat)) {
|
||||
if (!isFinite(DAY_MAPPING_STRING_TO_NUMBER[repeatDay])) continue; // eslint-disable-line no-continue
|
||||
if (!Number.isFinite(DAY_MAPPING_STRING_TO_NUMBER[repeatDay])) continue; // eslint-disable-line no-continue
|
||||
if (active) daysOfTheWeek.push(parseInt(DAY_MAPPING_STRING_TO_NUMBER[repeatDay], 10));
|
||||
}
|
||||
}
|
||||
@@ -127,7 +140,7 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
|
||||
if (options.nextDue) {
|
||||
const filteredDates = [];
|
||||
for (let i = 1; filteredDates.length < 6; i++) {
|
||||
for (let i = 1; filteredDates.length < 6; i += 1) {
|
||||
const calcDate = moment(startDate).add(dailyTask.everyX * i, 'days');
|
||||
if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate);
|
||||
}
|
||||
@@ -145,8 +158,8 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
schedule = schedule.every(daysOfTheWeek).daysOfWeek();
|
||||
if (options.nextDue) {
|
||||
const filteredDates = [];
|
||||
for (let i = 0; filteredDates.length < 6; i++) {
|
||||
for (let j = 0; j < daysOfTheWeek.length && filteredDates.length < 6; j++) {
|
||||
for (let i = 0; filteredDates.length < 6; i += 1) {
|
||||
for (let j = 0; j < daysOfTheWeek.length && filteredDates.length < 6; j += 1) {
|
||||
const calcDate = moment(startDate).day(daysOfTheWeek[j]).add(dailyTask.everyX * i, 'weeks');
|
||||
if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate);
|
||||
}
|
||||
@@ -177,7 +190,7 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
|
||||
if (options.nextDue) {
|
||||
const filteredDates = [];
|
||||
for (let i = 1; filteredDates.length < 6; i++) {
|
||||
for (let i = 1; filteredDates.length < 6; i += 1) {
|
||||
const recurDate = moment(startDate).add(dailyTask.everyX * i, 'months');
|
||||
const calcDate = recurDate.clone();
|
||||
calcDate.day(daysOfTheWeek[0]);
|
||||
@@ -193,8 +206,11 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
|
||||
calcDateWeek = Math.ceil(calcDate.date() / 7);
|
||||
|
||||
if (calcDate >= startOfDayWithCDSTime
|
||||
&& calcDateWeek === startDateWeek && calcDate.month() === recurDate.month()) filteredDates.push(calcDate);
|
||||
if (
|
||||
calcDate >= startOfDayWithCDSTime
|
||||
&& calcDateWeek === startDateWeek
|
||||
&& calcDate.month() === recurDate.month()
|
||||
) filteredDates.push(calcDate);
|
||||
}
|
||||
return filteredDates;
|
||||
}
|
||||
@@ -204,7 +220,7 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
schedule = schedule.every(dailyTask.daysOfMonth).daysOfMonth();
|
||||
if (options.nextDue) {
|
||||
const filteredDates = [];
|
||||
for (let i = 1; filteredDates.length < 6; i++) {
|
||||
for (let i = 1; filteredDates.length < 6; i += 1) {
|
||||
const calcDate = moment(startDate).add(dailyTask.everyX * i, 'months');
|
||||
if (calcDate >= startOfDayWithCDSTime) filteredDates.push(calcDate);
|
||||
}
|
||||
@@ -220,7 +236,7 @@ export function shouldDo (day, dailyTask, options = {}) {
|
||||
|
||||
if (options.nextDue) {
|
||||
const filteredDates = [];
|
||||
for (let i = 1; filteredDates.length < 6; i++) {
|
||||
for (let i = 1; filteredDates.length < 6; i += 1) {
|
||||
const calcDate = moment(startDate).add(dailyTask.everyX * i, 'years');
|
||||
if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import splitWhitespace from '../libs/splitWhitespace';
|
||||
/*
|
||||
Updates user stats with new stats. Handles death, leveling up, etc
|
||||
{stats} new stats
|
||||
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
|
||||
{update} if aggregated changes, pass in userObj as update.
|
||||
otherwise commits will be made immediately
|
||||
*/
|
||||
|
||||
function getStatToAllocate (user) {
|
||||
@@ -53,9 +54,7 @@ function getStatToAllocate (user) {
|
||||
statsObj[preference[3]] - ideal[3],
|
||||
];
|
||||
|
||||
suggested = findIndex(diff, val => {
|
||||
if (val === min(diff)) return true;
|
||||
});
|
||||
suggested = findIndex(diff, val => val === min(diff));
|
||||
|
||||
return suggested !== -1 ? preference[suggested] : 'str';
|
||||
}
|
||||
@@ -77,6 +76,6 @@ function getStatToAllocate (user) {
|
||||
|
||||
export default function autoAllocate (user) {
|
||||
const statToIncrease = getStatToAllocate(user);
|
||||
|
||||
return user.stats[statToIncrease]++;
|
||||
user.stats[statToIncrease] += 1;
|
||||
return user.stats[statToIncrease];
|
||||
}
|
||||
|
||||
@@ -2,17 +2,19 @@ import omit from 'lodash/omit';
|
||||
import reduce from 'lodash/reduce';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
|
||||
// Because the same op needs to be performed on the client and the server (critical hits, item drops, etc),
|
||||
// Because the same op needs to be performed on the client and the server
|
||||
// (critical hits, item drops, etc),
|
||||
// we need things to be "random", but technically predictable so that they don't go out-of-sync
|
||||
|
||||
export default function predictableRandom (user, seed) {
|
||||
if (!seed || seed === Math.PI) {
|
||||
let stats = user.stats.toObject ? user.stats.toObject() : user.stats;
|
||||
// These items are not part of the stat object but exists on the server (see controllers/user#getUser)
|
||||
// These items are not part of the stat object but exists on the server
|
||||
// (see controllers/user#getUser)
|
||||
// we remove them in order to use the same user.stats both on server and on client
|
||||
stats = omit(stats, ['toNextLevel', 'maxHealth', 'maxMP']);
|
||||
|
||||
seed = reduce(stats, (accumulator, val) => {
|
||||
seed = reduce(stats, (accumulator, val) => { // eslint-disable-line no-param-reassign
|
||||
if (isNumber(val)) {
|
||||
return accumulator + val;
|
||||
}
|
||||
@@ -20,6 +22,7 @@ export default function predictableRandom (user, seed) {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
const x = Math.sin(seed++) * 10000;
|
||||
seed += 1; // eslint-disable-line no-param-reassign
|
||||
const x = Math.sin(seed) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ import statsComputed from '../libs/statsComputed';
|
||||
// TODO This is only used on the server
|
||||
// move to user model as an instance method?
|
||||
|
||||
// Clone a drop object maintaining its functions so that we can change it without affecting the original item
|
||||
// Clone a drop object maintaining its functions
|
||||
// so that we can change it without affecting the original item
|
||||
function cloneDropItem (drop) {
|
||||
return cloneDeepWith(drop, val => (isFunction(val) ? val : undefined), // undefined will be handled by lodash
|
||||
);
|
||||
return cloneDeepWith(drop, val => (isFunction(val) ? val : undefined));
|
||||
}
|
||||
|
||||
function trueRandom () {
|
||||
@@ -40,14 +40,17 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
||||
* (1 + (user.contributor.level / 40 || 0)) // Contrib levels: +2.5% per level
|
||||
* (1 + (user.achievements.rebirths / 20 || 0)) // Rebirths: +5% per achievement
|
||||
* (1 + (user.achievements.streak / 200 || 0)) // Streak achievements: +0.5% per achievement
|
||||
* (user._tmp.crit || 1) * (1 + 0.5 * (reduce(task.checklist, (m, i) => // +50% per checklist item complete. TODO: make this into X individual drop chances instead
|
||||
m + (i.completed ? 1 : 0), // eslint-disable-line indent
|
||||
0) || 0)); // eslint-disable-line indent
|
||||
// +50% per checklist item complete. TODO: make this into X individual drop chances instead
|
||||
* (user._tmp.crit || 1)
|
||||
* (1 + 0.5 * (reduce(
|
||||
task.checklist, (m, i) => m + (i.completed ? 1 : 0), // eslint-disable-line indent
|
||||
0,
|
||||
) || 0)); // eslint-disable-line indent
|
||||
chance = diminishingReturns(chance, 0.75);
|
||||
|
||||
if (predictableRandom() < chance) {
|
||||
user.party.quest.progress.collectedItems = user.party.quest.progress.collectedItems || 0;
|
||||
user.party.quest.progress.collectedItems++;
|
||||
user.party.quest.progress.collectedItems += 1;
|
||||
user._tmp.quest = user._tmp.quest || {};
|
||||
user._tmp.quest.collection = 1;
|
||||
if (user.markModified) user.markModified('party.quest.progress');
|
||||
@@ -59,8 +62,10 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
||||
dropMultiplier = 1;
|
||||
}
|
||||
|
||||
if (daysSince(user.items.lastDrop.date, user.preferences) === 0
|
||||
&& user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(statsComputed(user).per / 25) + (user.contributor.level || 0))) {
|
||||
if (
|
||||
daysSince(user.items.lastDrop.date, user.preferences) === 0
|
||||
&& user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(statsComputed(user).per / 25) + (user.contributor.level || 0))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,7 +90,7 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
||||
drop = cloneDropItem(randomVal(content.dropEggs));
|
||||
|
||||
user.items.eggs[drop.key] = user.items.eggs[drop.key] || 0;
|
||||
user.items.eggs[drop.key]++;
|
||||
user.items.eggs[drop.key] += 1;
|
||||
if (user.markModified) user.markModified('items.eggs');
|
||||
|
||||
drop.type = 'Egg';
|
||||
@@ -103,10 +108,12 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
||||
} else { // common, 40% of 30%
|
||||
acceptableDrops = ['Base', 'White', 'Desert'];
|
||||
}
|
||||
drop = cloneDropItem(randomVal(pickBy(content.hatchingPotions, (v, k) => acceptableDrops.indexOf(k) >= 0)));
|
||||
drop = cloneDropItem(
|
||||
randomVal(pickBy(content.hatchingPotions, (v, k) => acceptableDrops.indexOf(k) >= 0)),
|
||||
);
|
||||
|
||||
user.items.hatchingPotions[drop.key] = user.items.hatchingPotions[drop.key] || 0;
|
||||
user.items.hatchingPotions[drop.key]++;
|
||||
user.items.hatchingPotions[drop.key] += 1;
|
||||
if (user.markModified) user.markModified('items.hatchingPotions');
|
||||
|
||||
drop.type = 'HatchingPotion';
|
||||
@@ -128,6 +135,6 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
||||
|
||||
user._tmp.drop = drop;
|
||||
user.items.lastDrop.date = Number(new Date());
|
||||
user.items.lastDrop.count++;
|
||||
user.items.lastDrop.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import includes from 'lodash/includes';
|
||||
import content from '../content/index';
|
||||
|
||||
export default function ultimateGear (user) {
|
||||
const owned = user.items.gear.owned.toObject ? user.items.gear.owned.toObject() : user.items.gear.owned;
|
||||
const owned = user.items.gear.owned.toObject
|
||||
? user.items.gear.owned.toObject()
|
||||
: user.items.gear.owned;
|
||||
|
||||
content.classes.forEach(klass => {
|
||||
if (user.achievements.ultimateGearSets[klass] !== true) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function updateStats (user, stats, req = {}, analytics) {
|
||||
|
||||
while (stats.exp >= experienceToNextLevel) {
|
||||
stats.exp -= experienceToNextLevel;
|
||||
user.stats.lvl++;
|
||||
user.stats.lvl += 1;
|
||||
|
||||
experienceToNextLevel = toNextLevel(user.stats.lvl);
|
||||
user.stats.hp = MAX_HEALTH;
|
||||
@@ -71,7 +71,7 @@ export default function updateStats (user, stats, req = {}, analytics) {
|
||||
if (user.addNotification) user.addNotification('DROPS_ENABLED');
|
||||
|
||||
if (user.items.eggs.Wolf > 0) {
|
||||
user.items.eggs.Wolf++;
|
||||
user.items.eggs.Wolf += 1;
|
||||
} else {
|
||||
user.items.eggs.Wolf = 1;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export default function updateStats (user, stats, req = {}, analytics) {
|
||||
if (user.markModified) user.markModified('flags.levelDrops');
|
||||
|
||||
if (!user.items.quests[k]) user.items.quests[k] = 0;
|
||||
user.items.quests[k]++;
|
||||
user.items.quests[k] += 1;
|
||||
if (user.markModified) user.markModified('items.quests');
|
||||
|
||||
if (analytics) {
|
||||
|
||||
@@ -9,14 +9,15 @@ const i18n = {
|
||||
};
|
||||
|
||||
function t (stringName) {
|
||||
let vars = arguments[1];
|
||||
const args = Array.from(arguments); // eslint-disable-line prefer-rest-params
|
||||
let vars = args[1];
|
||||
let locale;
|
||||
|
||||
if (isString(arguments[1])) {
|
||||
if (isString(args[1])) {
|
||||
vars = null;
|
||||
locale = arguments[1];
|
||||
} else if (arguments[2]) {
|
||||
locale = arguments[2];
|
||||
locale = args[1]; // eslint-disable-line prefer-destructuring
|
||||
} else if (args[2]) {
|
||||
locale = args[2]; // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
|
||||
const i18nNotSetup = !i18n.strings && !i18n.translations[locale];
|
||||
|
||||
@@ -7,8 +7,9 @@ const achievsContent = content.achievements;
|
||||
let index = 0;
|
||||
|
||||
function contribText (contrib, backer, language) {
|
||||
if (!contrib && !backer) return;
|
||||
if (!contrib && !backer) return null;
|
||||
if (backer && backer.npc) return backer.npc;
|
||||
|
||||
const lvl = contrib && contrib.level;
|
||||
if (lvl && lvl > 0) {
|
||||
let contribTitle = '';
|
||||
@@ -29,6 +30,8 @@ function contribText (contrib, backer, language) {
|
||||
|
||||
return `${contribTitle} ${contrib.text}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function _add (result, data) {
|
||||
@@ -38,7 +41,7 @@ function _add (result, data) {
|
||||
icon: data.icon,
|
||||
earned: data.earned,
|
||||
value: data.value,
|
||||
index: index++,
|
||||
index: index += 1,
|
||||
optionalCount: data.optionalCount,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import extendableBuiltin from './extendableBuiltin';
|
||||
|
||||
// Base class for custom application errors
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// TODO use https://babeljs.io/docs/en/babel-plugin-transform-classes
|
||||
// Babel 6 doesn't support extending native class (Error, Array, ...)
|
||||
// This function makes it possible to extend native classes with the same results as Babel 5
|
||||
export default function extendableBuiltin (klass) {
|
||||
function ExtendableBuiltin () {
|
||||
klass.apply(this, arguments);
|
||||
klass.apply(this, arguments); // eslint-disable-line prefer-rest-params
|
||||
}
|
||||
ExtendableBuiltin.prototype = Object.create(klass.prototype);
|
||||
Object.setPrototypeOf(ExtendableBuiltin, klass);
|
||||
|
||||
@@ -20,7 +20,9 @@ function lockQuest (quest, user) {
|
||||
}
|
||||
|
||||
function isItemSuggested (officialPinnedItems, itemInfo) {
|
||||
return officialPinnedItems.findIndex(officialItem => officialItem.type === itemInfo.pinType && officialItem.path === itemInfo.path) > -1;
|
||||
return officialPinnedItems.findIndex(officialItem => { // eslint-disable-line arrow-body-style
|
||||
return officialItem.type === itemInfo.pinType && officialItem.path === itemInfo.path;
|
||||
}) > -1;
|
||||
}
|
||||
|
||||
function getDefaultGearProps (item, language) {
|
||||
@@ -46,12 +48,12 @@ function getDefaultGearProps (item, language) {
|
||||
|
||||
export default function getItemInfo (user, type, item, officialPinnedItems, language = 'en') {
|
||||
if (officialPinnedItems === undefined) {
|
||||
officialPinnedItems = getOfficialPinnedItems(user);
|
||||
officialPinnedItems = getOfficialPinnedItems(user); // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
let itemInfo;
|
||||
|
||||
switch (type) {
|
||||
switch (type) { // eslint-disable-line default-case
|
||||
case 'eggs':
|
||||
itemInfo = {
|
||||
key: item.key,
|
||||
@@ -131,7 +133,9 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang
|
||||
group: item.group,
|
||||
value: item.goldValue ? item.goldValue : item.value,
|
||||
locked,
|
||||
previous: content.quests[item.previous] ? content.quests[item.previous].text(language) : null,
|
||||
previous: content.quests[item.previous]
|
||||
? content.quests[item.previous].text(language)
|
||||
: null,
|
||||
unlockCondition: item.unlockCondition,
|
||||
drop: item.drop,
|
||||
boss: item.boss,
|
||||
|
||||
@@ -14,7 +14,9 @@ export default function getOfficialPinnedItems (user) {
|
||||
|
||||
// pinnedSets == current seasonal class set are always gold purchaseable
|
||||
|
||||
flatGearArray.filter(gear => user.items.gear.owned[gear.key] === undefined && gear.set === setToAdd).map(gear => {
|
||||
flatGearArray
|
||||
.filter(gear => user.items.gear.owned[gear.key] === undefined && gear.set === setToAdd)
|
||||
.forEach(gear => {
|
||||
officialItemsArray.push({
|
||||
type: 'marketGear',
|
||||
path: `gear.flat.${gear.key}`,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
export default function isPinned (user, item, checkOfficialPinnedItems /* getOfficialPinnedItems */) {
|
||||
export default function isPinned (user, item, checkOfficialPinnedItems) {
|
||||
if (user === null) return false;
|
||||
|
||||
const isPinnedOfficial = checkOfficialPinnedItems !== undefined && checkOfficialPinnedItems.findIndex(pinned => pinned.path === item.path) > -1;
|
||||
const isItemUnpinned = user.unpinnedItems !== undefined && user.unpinnedItems.findIndex(unpinned => unpinned.path === item.path) > -1;
|
||||
const isItemPinned = user.pinnedItems !== undefined && user.pinnedItems.findIndex(pinned => pinned.path === item.path) > -1;
|
||||
const isPinnedOfficial = checkOfficialPinnedItems !== undefined
|
||||
&& checkOfficialPinnedItems.findIndex(pinned => pinned.path === item.path) > -1;
|
||||
const isItemUnpinned = user.unpinnedItems !== undefined
|
||||
&& user.unpinnedItems.findIndex(unpinned => unpinned.path === item.path) > -1;
|
||||
const isItemPinned = user.pinnedItems !== undefined
|
||||
&& user.pinnedItems.findIndex(pinned => pinned.path === item.path) > -1;
|
||||
|
||||
if (isPinnedOfficial && !isItemUnpinned) return true;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function percent (x, y, dir) {
|
||||
roundFn = Math.round;
|
||||
}
|
||||
if (x === 0) {
|
||||
x = 1;
|
||||
x = 1; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
return Math.max(0, roundFn(x / y * 100));
|
||||
return Math.max(0, roundFn((x / y) * 100));
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import values from 'lodash/values';
|
||||
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
|
||||
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:
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
export default function splitWhitespace (s) {
|
||||
return s.split(' ');
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { v4 as uuid } from 'uuid';
|
||||
import defaults from 'lodash/defaults';
|
||||
import moment from 'moment';
|
||||
|
||||
// Even though Mongoose handles task defaults, we want to make sure defaults are set on the client-side before
|
||||
// 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 move to client code?
|
||||
|
||||
@@ -17,7 +17,10 @@ export default function updateStore (user) {
|
||||
let changes = [];
|
||||
|
||||
each(content.gearTypes, type => {
|
||||
const found = lodashFind(content.gear.tree[type][user.stats.class], item => !user.items.gear.owned[item.key]);
|
||||
const found = lodashFind(
|
||||
content.gear.tree[type][user.stats.class],
|
||||
item => !user.items.gear.owned[item.key],
|
||||
);
|
||||
|
||||
if (found) changes.push(found);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import _merge from 'lodash/merge';
|
||||
import _get from 'lodash/get';
|
||||
import i18n from '../../i18n';
|
||||
@@ -61,7 +62,7 @@ export class AbstractBuyOperation {
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
i18n (key, params = {}) {
|
||||
return i18n.t.apply(null, [...arguments, this.req.language]);
|
||||
return i18n.t.apply(null, [...arguments, this.req.language]); // eslint-disable-line prefer-rest-params, max-len
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,9 @@ import { BuyHourglassMountOperation } from './buyMount';
|
||||
|
||||
// @TODO: when we are sure buy is the only function used, let's move the buy files to a folder
|
||||
|
||||
export default function buy (user, req = {}, analytics, options = { quantity: 1, hourglass: false }) {
|
||||
export default function buy (
|
||||
user, req = {}, analytics, options = { quantity: 1, hourglass: false },
|
||||
) {
|
||||
const key = get(req, 'params.key');
|
||||
const { hourglass } = options;
|
||||
const { quantity } = options;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
const YIELD_EQUIPMENT_THRESHOLD = 0.6;
|
||||
const YIELD_FOOD_THRESHOLD = 0.8;
|
||||
|
||||
export class BuyArmoireOperation extends AbstractGoldItemOperation {
|
||||
export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -39,9 +39,15 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation {
|
||||
const eligibleEquipment = filter(content.gear.flat, eligible => eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key]);
|
||||
const armoireHasEquipment = !isEmpty(eligibleEquipment);
|
||||
|
||||
if (armoireHasEquipment && (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)) {
|
||||
if (
|
||||
armoireHasEquipment
|
||||
&& (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)
|
||||
) {
|
||||
result = this._gearResult(user, eligibleEquipment);
|
||||
} else if ((armoireHasEquipment && armoireResult < YIELD_FOOD_THRESHOLD) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
||||
} else if (
|
||||
(armoireHasEquipment && armoireResult < YIELD_FOOD_THRESHOLD)
|
||||
|| armoireResult < 0.5
|
||||
) {
|
||||
result = this._foodResult(user);
|
||||
} else {
|
||||
result = this._experienceResult(user);
|
||||
@@ -49,7 +55,8 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation {
|
||||
|
||||
this.subtractCurrency(user, item);
|
||||
|
||||
let { message, armoireResp } = result;
|
||||
let { message } = result;
|
||||
const { armoireResp } = result;
|
||||
|
||||
if (!message) {
|
||||
message = this.i18n('messageBought', {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
import planGemLimits from '../../libs/planGemLimits';
|
||||
|
||||
export class BuyGemOperation extends AbstractGoldItemOperation {
|
||||
export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -30,7 +30,8 @@ export class BuyGemOperation extends AbstractGoldItemOperation {
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
const key = this.key = get(req, 'params.key');
|
||||
this.key = get(req, 'params.key');
|
||||
const { key } = this.key;
|
||||
if (!key) throw new BadRequest(this.i18n('missingKeyParam'));
|
||||
|
||||
let { convCap } = planGemLimits;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
|
||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
|
||||
export class BuyHealthPotionOperation extends AbstractGoldItemOperation {
|
||||
export class BuyHealthPotionOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { removePinnedGearAddPossibleNewOnes } from '../pinnedGearUtils';
|
||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
import errorMessage from '../../libs/errorMessage';
|
||||
|
||||
export class BuyMarketGearOperation extends AbstractGoldItemOperation {
|
||||
export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -37,7 +37,8 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation {
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
const key = this.key = get(req, 'params.key');
|
||||
this.key = get(req, 'params.key');
|
||||
const { key } = this.key;
|
||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||
|
||||
const item = content.gear.flat[key];
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
import { AbstractHourglassItemOperation } from './abstractBuyOperation';
|
||||
|
||||
export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
||||
export class BuyHourglassMountOperation extends AbstractHourglassItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -19,7 +19,8 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
const key = this.key = get(req, 'params.key');
|
||||
this.key = get(req, 'params.key');
|
||||
const { key } = this;
|
||||
if (!key) throw new BadRequest(this.i18n('missingKeyParam'));
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function buyMysterySet (user, req = {}, analytics) {
|
||||
|
||||
if (user.markModified) user.markModified('items.gear.owned');
|
||||
|
||||
user.purchased.plan.consecutive.trinkets--;
|
||||
user.purchased.plan.consecutive.trinkets -= 1;
|
||||
|
||||
return [
|
||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||
|
||||
@@ -9,7 +9,7 @@ import content from '../../content/index';
|
||||
import errorMessage from '../../libs/errorMessage';
|
||||
import { AbstractGemItemOperation } from './abstractBuyOperation';
|
||||
|
||||
export class BuyQuestWithGemOperation extends AbstractGemItemOperation {
|
||||
export class BuyQuestWithGemOperation extends AbstractGemItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -31,7 +31,8 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation {
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
const key = this.key = get(req, 'params.key');
|
||||
this.key = get(req, 'params.key');
|
||||
const { key } = this.key;
|
||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||
|
||||
const item = content.quests[key];
|
||||
@@ -46,7 +47,10 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation {
|
||||
}
|
||||
|
||||
executeChanges (user, item, req) {
|
||||
if (!user.items.quests[item.key] || user.items.quests[item.key] < 0) user.items.quests[item.key] = 0;
|
||||
if (
|
||||
!user.items.quests[item.key]
|
||||
|| user.items.quests[item.key] < 0
|
||||
) user.items.quests[item.key] = 0;
|
||||
user.items.quests[item.key] += this.quantity;
|
||||
if (user.markModified) user.markModified('items.quests');
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import content from '../../content/index';
|
||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
import errorMessage from '../../libs/errorMessage';
|
||||
|
||||
export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation {
|
||||
export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -66,7 +66,10 @@ export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation {
|
||||
}
|
||||
|
||||
executeChanges (user, item, req) {
|
||||
if (!user.items.quests[item.key] || user.items.quests[item.key] < 0) user.items.quests[item.key] = 0;
|
||||
if (
|
||||
!user.items.quests[item.key]
|
||||
|| user.items.quests[item.key] < 0
|
||||
) user.items.quests[item.key] = 0;
|
||||
user.items.quests[item.key] += this.quantity;
|
||||
if (user.markModified) user.markModified('items.quests');
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||
import errorMessage from '../../libs/errorMessage';
|
||||
|
||||
export class BuySpellOperation extends AbstractGoldItemOperation {
|
||||
export class BuySpellOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
@@ -27,7 +27,8 @@ export class BuySpellOperation extends AbstractGoldItemOperation {
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
const key = this.key = get(req, 'params.key');
|
||||
this.key = get(req, 'params.key');
|
||||
const { key } = this;
|
||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||
|
||||
const item = content.special[key];
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity =
|
||||
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||
}
|
||||
|
||||
user.purchased.plan.consecutive.trinkets--;
|
||||
user.purchased.plan.consecutive.trinkets -= 1;
|
||||
|
||||
if (type === 'pets') {
|
||||
user.items.pets[key] = 5;
|
||||
|
||||
@@ -54,14 +54,14 @@ function purchaseItem (user, item, price, type, key) {
|
||||
if (!user.items[subType][bundledKey] || user.items[subType][key] < 0) {
|
||||
user.items[subType][bundledKey] = 0;
|
||||
}
|
||||
user.items[subType][bundledKey]++;
|
||||
user.items[subType][bundledKey] += 1;
|
||||
});
|
||||
if (user.markModified) user.markModified(`items.${subType}`);
|
||||
} else {
|
||||
if (!user.items[type][key] || user.items[type][key] < 0) {
|
||||
user.items[type][key] = 0;
|
||||
}
|
||||
user.items[type][key]++;
|
||||
user.items[type][key] += 1;
|
||||
if (user.markModified) user.markModified(`items.${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ export default function changeClass (user, req = {}, analytics) {
|
||||
if (user.stats.lvl < 10) {
|
||||
throw new NotAuthorized(i18n.t('lvl10ChangeClass', req.language));
|
||||
} else if (!klass) {
|
||||
// if no class is specified, reset points and set user.flags.classSelected to false. User will have paid 3 gems and will be prompted to select class.
|
||||
// if no class is specified, reset points and set user.flags.classSelected to false.
|
||||
// User will have paid 3 gems and will be prompted to select class.
|
||||
balanceRemoved = resetClass(user, req);
|
||||
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
||||
if (user.flags.classSelected) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function equip (user, req = {}) {
|
||||
|
||||
let message;
|
||||
|
||||
switch (type) {
|
||||
switch (type) { // eslint-disable-line default-case
|
||||
case 'mount': {
|
||||
if (!user.items.mounts[key]) {
|
||||
throw new NotFound(i18n.t('mountNotOwned', req.language));
|
||||
@@ -49,7 +49,11 @@ export default function equip (user, req = {}) {
|
||||
if (user.items.gear[type][item.type] === key) {
|
||||
user.items.gear[type] = {
|
||||
|
||||
...(user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type]),
|
||||
...(
|
||||
user.items.gear[type].toObject
|
||||
? user.items.gear[type].toObject()
|
||||
: user.items.gear[type]
|
||||
),
|
||||
[item.type]: `${item.type}_base_0`,
|
||||
};
|
||||
if (user.markModified && type === 'owned') user.markModified('items.gear.owned');
|
||||
@@ -60,7 +64,11 @@ export default function equip (user, req = {}) {
|
||||
} else {
|
||||
user.items.gear[type] = {
|
||||
|
||||
...(user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type]),
|
||||
...(
|
||||
user.items.gear[type].toObject
|
||||
? user.items.gear[type].toObject()
|
||||
: user.items.gear[type]
|
||||
),
|
||||
[item.type]: item.key,
|
||||
};
|
||||
if (user.markModified && type === 'owned') user.markModified('items.gear.owned');
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function feed (user, req = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
user.items.food[food.key]--;
|
||||
user.items.food[food.key] -= 1;
|
||||
if (user.markModified) user.markModified('items.food');
|
||||
|
||||
forEach(content.animalColorAchievements, achievement => {
|
||||
|
||||
@@ -24,7 +24,13 @@ export default function hatch (user, req = {}) {
|
||||
throw new NotFound(i18n.t('messageMissingEggPotion', req.language));
|
||||
}
|
||||
|
||||
if ((content.hatchingPotions[hatchingPotion].premium || content.hatchingPotions[hatchingPotion].wacky) && !content.dropEggs[egg]) {
|
||||
if (
|
||||
(
|
||||
content.hatchingPotions[hatchingPotion].premium
|
||||
|| content.hatchingPotions[hatchingPotion].wacky
|
||||
)
|
||||
&& !content.dropEggs[egg]
|
||||
) {
|
||||
throw new BadRequest(i18n.t('messageInvalidEggPotionCombo', req.language));
|
||||
}
|
||||
|
||||
@@ -35,8 +41,8 @@ export default function hatch (user, req = {}) {
|
||||
}
|
||||
|
||||
user.items.pets[pet] = 5;
|
||||
user.items.eggs[egg]--;
|
||||
user.items.hatchingPotions[hatchingPotion]--;
|
||||
user.items.eggs[egg] -= 1;
|
||||
user.items.hatchingPotions[hatchingPotion] -= 1;
|
||||
if (user.markModified) {
|
||||
user.markModified('items.pets');
|
||||
user.markModified('items.eggs');
|
||||
@@ -45,7 +51,10 @@ export default function hatch (user, req = {}) {
|
||||
|
||||
forEach(content.animalColorAchievements, achievement => {
|
||||
if (!user.achievements[achievement.petAchievement]) {
|
||||
const petIndex = findIndex(keys(content.dropEggs), animal => isNaN(user.items.pets[`${animal}-${achievement.color}`]) || user.items.pets[`${animal}-${achievement.color}`] <= 0);
|
||||
const petIndex = findIndex(
|
||||
keys(content.dropEggs),
|
||||
animal => Number.isNaN(user.items.pets[`${animal}-${achievement.color}`]) || user.items.pets[`${animal}-${achievement.color}`] <= 0,
|
||||
);
|
||||
if (petIndex === -1) {
|
||||
user.achievements[achievement.petAchievement] = true;
|
||||
if (user.addNotification) {
|
||||
|
||||
@@ -37,7 +37,10 @@ export function selectGearToPin (user) {
|
||||
const changes = [];
|
||||
|
||||
each(content.gearTypes, type => {
|
||||
const found = lodashFind(content.gear.tree[type][user.stats.class], item => !user.items.gear.owned[item.key]);
|
||||
const found = lodashFind(
|
||||
content.gear.tree[type][user.stats.class],
|
||||
item => !user.items.gear.owned[item.key],
|
||||
);
|
||||
|
||||
if (found) changes.push(found);
|
||||
});
|
||||
@@ -59,11 +62,10 @@ export function addPinnedGear (user, type, path) {
|
||||
export function addPinnedGearByClass (user) {
|
||||
const newPinnedItems = selectGearToPin(user);
|
||||
|
||||
for (const item of newPinnedItems) {
|
||||
newPinnedItems.forEach(item => {
|
||||
const itemInfo = getItemInfo(user, 'marketGear', item);
|
||||
|
||||
addPinnedGear(user, itemInfo.pinType, itemInfo.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removeItemByPath (user, path) {
|
||||
@@ -80,11 +82,10 @@ export function removeItemByPath (user, path) {
|
||||
export function removePinnedGearByClass (user) {
|
||||
const currentPinnedItems = selectGearToPin(user);
|
||||
|
||||
for (const item of currentPinnedItems) {
|
||||
currentPinnedItems.forEach(item => {
|
||||
const itemInfo = getItemInfo(user, 'marketGear', item);
|
||||
|
||||
removeItemByPath(user, itemInfo.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removePinnedGearAddPossibleNewOnes (user, itemPath, newItemKey) {
|
||||
@@ -100,11 +101,12 @@ export function removePinnedGearAddPossibleNewOnes (user, itemPath, newItemKey)
|
||||
addPinnedGearByClass(user);
|
||||
|
||||
// update the version, so that vue can refresh the seasonal shop
|
||||
user._v++;
|
||||
user._v += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes all pinned gear that the user already owns (like class starter gear which has been pinned before)
|
||||
* removes all pinned gear that the user already owns
|
||||
*(like class starter gear which has been pinned before)
|
||||
* @param user
|
||||
*/
|
||||
export function removePinnedItemsByOwnedGear (user) {
|
||||
@@ -126,9 +128,9 @@ export function togglePinnedItem (user, { item, type, path }, req = {}) {
|
||||
|
||||
if (!path) {
|
||||
// If path isn't passed it means an item was passed
|
||||
path = getItemInfo(user, type, item, officialPinnedItems, req.language).path;
|
||||
path = getItemInfo(user, type, item, officialPinnedItems, req.language).path; // eslint-disable-line no-param-reassign, max-len
|
||||
} else {
|
||||
item = getItemByPathAndType(type, path);
|
||||
item = getItemByPathAndType(type, path); // eslint-disable-line no-param-reassign
|
||||
|
||||
if (!item && PATHS_WITHOUT_ITEM.indexOf(path) === -1) {
|
||||
// path not exists in our content structure
|
||||
|
||||
@@ -16,6 +16,8 @@ function markNotificationAsRead (user, cardType) {
|
||||
&& notification.data
|
||||
&& notification.data.card === cardType
|
||||
) return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (indexToRemove !== -1) user.notifications.splice(indexToRemove, 1);
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) {
|
||||
user.achievements.rebirths = 1;
|
||||
user.achievements.rebirthLevel = lvl;
|
||||
} else if (lvl > user.achievements.rebirthLevel || lvl === MAX_LEVEL) {
|
||||
user.achievements.rebirths++;
|
||||
user.achievements.rebirths += 1;
|
||||
user.achievements.rebirthLevel = lvl;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
|
||||
export default function releaseBoth (user, req = {}) {
|
||||
let animal;
|
||||
|
||||
if (!user.achievements.triadBingo) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughPetsMounts', req.language));
|
||||
}
|
||||
|
||||
if (beastMasterProgress(user.items.pets) !== 90 || mountMasterProgress(user.items.mounts) !== 90) {
|
||||
if (
|
||||
beastMasterProgress(user.items.pets) !== 90
|
||||
|| mountMasterProgress(user.items.mounts) !== 90
|
||||
) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughPetsMounts', req.language));
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ export default function releaseBoth (user, req = {}) {
|
||||
user.items.currentPet = '';
|
||||
}
|
||||
|
||||
for (animal in content.pets) {
|
||||
Object.keys(content.pets).forEach(animal => {
|
||||
if (user.items.pets[animal] === -1) {
|
||||
giveTriadBingo = false;
|
||||
} else if (!user.items.pets[animal]) {
|
||||
@@ -61,7 +62,8 @@ export default function releaseBoth (user, req = {}) {
|
||||
|
||||
user.items.pets[animal] = 0;
|
||||
user.items.mounts[animal] = null;
|
||||
}
|
||||
});
|
||||
|
||||
if (user.markModified) {
|
||||
user.markModified('items.pets');
|
||||
user.markModified('items.mounts');
|
||||
@@ -71,21 +73,21 @@ export default function releaseBoth (user, req = {}) {
|
||||
if (!user.achievements.beastMasterCount) {
|
||||
user.achievements.beastMasterCount = 0;
|
||||
}
|
||||
user.achievements.beastMasterCount++;
|
||||
user.achievements.beastMasterCount += 1;
|
||||
}
|
||||
|
||||
if (giveMountMasterAchievement) {
|
||||
if (!user.achievements.mountMasterCount) {
|
||||
user.achievements.mountMasterCount = 0;
|
||||
}
|
||||
user.achievements.mountMasterCount++;
|
||||
user.achievements.mountMasterCount += 1;
|
||||
}
|
||||
|
||||
if (giveTriadBingo) {
|
||||
if (!user.achievements.triadBingoCount) {
|
||||
user.achievements.triadBingoCount = 0;
|
||||
}
|
||||
user.achievements.triadBingoCount++;
|
||||
user.achievements.triadBingoCount += 1;
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -24,19 +24,20 @@ export default function releaseMounts (user, req = {}, analytics) {
|
||||
user.items.currentMount = '';
|
||||
}
|
||||
|
||||
for (const mount in content.pets) {
|
||||
Object.keys(content.pets).forEach(mount => {
|
||||
if (user.items.mounts[mount] === null || user.items.mounts[mount] === undefined) {
|
||||
giveMountMasterAchievement = false;
|
||||
}
|
||||
user.items.mounts[mount] = null;
|
||||
}
|
||||
});
|
||||
|
||||
if (user.markModified) user.markModified('items.mounts');
|
||||
|
||||
if (giveMountMasterAchievement) {
|
||||
if (!user.achievements.mountMasterCount) {
|
||||
user.achievements.mountMasterCount = 0;
|
||||
}
|
||||
user.achievements.mountMasterCount++;
|
||||
user.achievements.mountMasterCount += 1;
|
||||
}
|
||||
|
||||
if (analytics) {
|
||||
|
||||
@@ -24,19 +24,20 @@ export default function releasePets (user, req = {}, analytics) {
|
||||
user.items.currentPet = '';
|
||||
}
|
||||
|
||||
for (const pet in content.pets) {
|
||||
Object.keys(content.pets).forEach(pet => {
|
||||
if (!user.items.pets[pet]) {
|
||||
giveBeastMasterAchievement = false;
|
||||
}
|
||||
user.items.pets[pet] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
if (user.markModified) user.markModified('items.pets');
|
||||
|
||||
if (giveBeastMasterAchievement) {
|
||||
if (!user.achievements.beastMasterCount) {
|
||||
user.achievements.beastMasterCount = 0;
|
||||
}
|
||||
user.achievements.beastMasterCount++;
|
||||
user.achievements.beastMasterCount += 1;
|
||||
}
|
||||
|
||||
if (analytics) {
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function reroll (user, tasks = [], req = {}, analytics) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
user.balance--;
|
||||
user.balance -= 1;
|
||||
user.stats.hp = 50;
|
||||
|
||||
each(tasks, task => {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function revive (user, req = {}, analytics) {
|
||||
});
|
||||
|
||||
if (user.stats.lvl > 1) {
|
||||
user.stats.lvl--;
|
||||
user.stats.lvl -= 1;
|
||||
}
|
||||
|
||||
const lostStat = randomVal(reduce(['str', 'con', 'per', 'int'], (m, k) => {
|
||||
@@ -37,7 +37,7 @@ export default function revive (user, req = {}, analytics) {
|
||||
});
|
||||
|
||||
if (lostStat) {
|
||||
user.stats[lostStat]--;
|
||||
user.stats[lostStat] -= 1;
|
||||
}
|
||||
|
||||
const base = user.items.gear.owned;
|
||||
@@ -68,9 +68,11 @@ export default function revive (user, req = {}, analytics) {
|
||||
|
||||
const itemIsArmoire = itm.klass === 'armoire';
|
||||
|
||||
if (itemHasValueOrWarrior0 && (itemClassEqualsUserClass || itemIsSpecial || itemIsArmoire)) {
|
||||
if (
|
||||
itemHasValueOrWarrior0
|
||||
&& (itemClassEqualsUserClass || itemIsSpecial || itemIsArmoire)
|
||||
) {
|
||||
losableItems[key] = key;
|
||||
return losableItems[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ function _getTaskValue (taskValue) {
|
||||
function _calculateDelta (task, direction, cron) {
|
||||
// Min/max on task redness
|
||||
const currVal = _getTaskValue(task.value);
|
||||
let nextDelta = Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
|
||||
let nextDelta = (0.9747 ** currVal) * (direction === 'down' ? -1 : 1);
|
||||
|
||||
// Checklists
|
||||
if (task.checklist && task.checklist.length > 0) {
|
||||
@@ -52,13 +52,13 @@ function _calculateDelta (task, direction, cron) {
|
||||
// it will be a bit off
|
||||
function _calculateReverseDelta (task, direction) {
|
||||
const currVal = _getTaskValue(task.value);
|
||||
let testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1);
|
||||
let testVal = currVal + (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
|
||||
const calc = testVal + Math.pow(0.9747, testVal);
|
||||
const calc = testVal + (0.9747 ** testVal);
|
||||
const diff = currVal - calc;
|
||||
|
||||
if (Math.abs(diff) < CLOSE_ENOUGH) break;
|
||||
@@ -84,7 +84,7 @@ function _calculateReverseDelta (task, direction) {
|
||||
}
|
||||
|
||||
function _gainMP (user, val) {
|
||||
val *= user._tmp.crit || 1;
|
||||
val *= user._tmp.crit || 1; // eslint-disable-line no-param-reassign
|
||||
user.stats.mp += val;
|
||||
|
||||
if (user.stats.mp >= statsComputed(user).maxMP) user.stats.mp = statsComputed(user).maxMP;
|
||||
@@ -125,7 +125,8 @@ function _addPoints (user, task, stats, direction, delta) {
|
||||
const streakBonus = currStreak / 100 + 1; // eg, 1-day streak is 1.01, 2-day is 1.02, etc
|
||||
const 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
|
||||
// keep this on-hand for later, so we can notify streak-bonus
|
||||
user._tmp.streakBonus = afterStreak - gpMod;
|
||||
}
|
||||
|
||||
stats.gp += afterStreak;
|
||||
@@ -194,9 +195,13 @@ export default function scoreTask (options = {}, req = {}) {
|
||||
exp: user.stats.exp,
|
||||
};
|
||||
|
||||
if (task.group && task.group.approval && task.group.approval.required && !task.group.approval.approved) return 0;
|
||||
if (
|
||||
task.group && task.group.approval && task.group.approval.required
|
||||
&& !task.group.approval.approved
|
||||
) return 0;
|
||||
|
||||
// This is for setting one-time temporary flags, such as streakBonus or itemDropped. Useful for notifying
|
||||
// This is for setting one-time temporary flags,
|
||||
// such as streakBonus or itemDropped. Useful for notifying
|
||||
// the API consumer, then cleared afterwards
|
||||
user._tmp = {};
|
||||
|
||||
@@ -236,7 +241,8 @@ export default function scoreTask (options = {}, req = {}) {
|
||||
lastHistoryEntry.value = task.value;
|
||||
lastHistoryEntry.date = Number(new Date());
|
||||
|
||||
// @TODO remove this extra check after migration has run to set scoredUp and scoredDown in every task
|
||||
// @TODO remove this extra check after migration
|
||||
// has run to set scoredUp and scoredDown in every task
|
||||
lastHistoryEntry.scoredUp = lastHistoryEntry.scoredUp || 0;
|
||||
lastHistoryEntry.scoredDown = lastHistoryEntry.scoredDown || 0;
|
||||
|
||||
@@ -265,7 +271,8 @@ export default function scoreTask (options = {}, req = {}) {
|
||||
} 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
|
||||
// obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
_addPoints(user, task, stats, direction, delta);
|
||||
_gainMP(user, max([1, 0.01 * statsComputed(user).maxMP]) * (direction === 'down' ? -1 : 1));
|
||||
|
||||
if (direction === 'up') {
|
||||
@@ -286,7 +293,9 @@ export default function scoreTask (options = {}, req = {}) {
|
||||
task.history.push(historyEntry);
|
||||
} else if (direction === 'down') {
|
||||
// Remove a streak achievement if streak was a multiple of 21 and the daily was undone
|
||||
if (task.streak !== 0 && task.streak % 21 === 0) user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0;
|
||||
if (task.streak !== 0 && task.streak % 21 === 0) {
|
||||
user.achievements.streak = user.achievements.streak ? user.achievements.streak - 1 : 0;
|
||||
}
|
||||
task.streak -= 1;
|
||||
task.completed = false;
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ export default function allocate (user, req = {}) {
|
||||
}
|
||||
|
||||
if (user.stats.points > 0) {
|
||||
user.stats[stat]++;
|
||||
user.stats.points--;
|
||||
user.stats[stat] += 1;
|
||||
user.stats.points -= 1;
|
||||
if (stat === 'int') {
|
||||
user.stats.mp++;
|
||||
user.stats.mp += 1;
|
||||
}
|
||||
} else {
|
||||
throw new NotAuthorized(i18n.t('notEnoughAttrPoints', req.language));
|
||||
|
||||
@@ -34,11 +34,11 @@ export default function allocateBulk (user, req = {}) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughAttrPoints', req.language));
|
||||
}
|
||||
|
||||
for (const [stat, value] of Object.entries(stats)) {
|
||||
Object.entries(stats).forEach(([stat, value]) => {
|
||||
user.stats[stat] += value;
|
||||
user.stats.points -= value;
|
||||
if (stat === 'int') user.stats.mp += value;
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
user.stats,
|
||||
|
||||
@@ -45,13 +45,14 @@ export default function unlock (user, req = {}, analytics) {
|
||||
|
||||
each(setPaths, singlePath => {
|
||||
if (get(user, `purchased.${singlePath}`) === true) {
|
||||
alreadyOwnedItems++;
|
||||
alreadyOwnedItems += 1;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
// 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));
|
||||
|
||||
@@ -27,13 +27,15 @@ export function toNextLevel (lvl) {
|
||||
} if (lvl === 5) {
|
||||
return 150;
|
||||
}
|
||||
return Math.round((Math.pow(lvl, 2) * 0.25 + 10 * lvl + 139.75) / 10) * 10;
|
||||
return Math.round(((lvl ** 2) * 0.25 + 10 * lvl + 139.75) / 10) * 10;
|
||||
}
|
||||
|
||||
/*
|
||||
A hyperbola function that creates diminishing returns, so you can't go to infinite (eg, with Exp gain).
|
||||
A hyperbola function that creates diminishing returns,
|
||||
so you can't go to infinite (eg, with Exp gain).
|
||||
{max} The asymptote
|
||||
{bonus} All the numbers combined for your point bonus (eg, task.value * user.stats.int * critChance, etc)
|
||||
{bonus} All the numbers combined for your point bonus
|
||||
(eg, task.value * user.stats.int * critChance, etc)
|
||||
{halfway} (optional) the point at which the graph starts bending
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user