Fix: Antidotes to Avatar Transformation Items should be added to Rewards by API (#11353)

* Fix: moved debuffPotions from vue component

- Move logic of choosing proper debuf potion from vue component to website commons
- introduce new function to get debuffSpellItems

* Fix: move debuffPotions to server

* Refactoring: move setting of debuff potion to func

* Fix: sanity

* Refactoring & Tests:

- Create test case for get and set DebuffPotionItems functions
- Fix setDebuffPotionItems function to not create duplicated debuff items
- Make debuff potion type of items unpinnable
- Move list of debuffs to constant to reuse it in tests and functions

* Fix: typo in test describe

* Fix: translation of unpin

* Fix: setDebuffPotionItems on cron buffs reset

* Fix: use full path for debuff potions
This commit is contained in:
Aleksey
2019-10-06 19:41:39 +03:00
committed by Matteo Pagliazzi
parent 85eab76a71
commit 5b57d91a9b
14 changed files with 214 additions and 35 deletions

View File

@@ -15,7 +15,7 @@ describe('GET /user/toggle-pinned-item', () => {
.to.eventually.be.rejected.and.eql({ .to.eventually.be.rejected.and.eql({
code: 400, code: 400,
error: 'BadRequest', error: 'BadRequest',
message: t('cannotUnpinArmoirPotion'), message: t('cannotUnpinItem'),
}); });
}); });

View File

@@ -0,0 +1,47 @@
import {
generateUser,
} from '../../helpers/common.helper';
import getDebuffPotionItems from '../../../website/common/script/libs/getDebuffPotionItems';
import { TRANSFORMATION_DEBUFFS_LIST } from '../../../website/common/script/constants';
describe('getDebuffPotionItems', () => {
let user;
beforeEach(() => {
user = generateUser();
});
for (let key in TRANSFORMATION_DEBUFFS_LIST) {
const debuff = TRANSFORMATION_DEBUFFS_LIST[key];
// Here we itterate whole object to dynamicaly create test suites as it described in dock of mocha
// https://mochajs.org/#dynamically-generating-tests
// That's why we have eslint-disable here
// eslint-disable-next-line no-loop-func
it(`Should return the ${debuff} on ${key} buff`, () => {
user.stats.buffs[key] = true;
let result = getDebuffPotionItems(user);
expect(result).to.be.an('array').that.deep.includes({path: `spells.special.${debuff}`, type: 'debuffPotion'});
});
}
it('Should return all debuff potions for all buffs', () => {
user.stats.buffs.seafoam = true;
user.stats.buffs.spookySparkles = true;
user.stats.buffs.snowball = true;
user.stats.buffs.shinySeed = true;
let result = getDebuffPotionItems(user);
expect(result).to.be.an('array').that.deep.include.members([
{path: 'spells.special.sand', type: 'debuffPotion'},
{path: 'spells.special.petalFreePotion', type: 'debuffPotion'},
{path: 'spells.special.salt', type: 'debuffPotion'},
{path: 'spells.special.opaquePotion', type: 'debuffPotion'},
]);
});
});

View File

@@ -0,0 +1,53 @@
import {
generateUser,
} from '../../helpers/common.helper';
import setDebuffPotionItems from '../../../website/common/script/libs/setDebuffPotionItems';
describe('setDebuffPotionItems', () => {
let user;
beforeEach(() => {
user = generateUser();
});
it('Should push the debuff item to pinned items of user', () => {
user.stats.buffs.spookySparkles = true;
const previousPinnedItemsLength = user.pinnedItems.length;
let result = setDebuffPotionItems(user);
expect(result.pinnedItems.length).to.be.greaterThan(previousPinnedItemsLength);
});
it('Shouldn\'t create duplicate of already added debuff potion', () => {
user.stats.buffs.spookySparkles = true;
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(firstSetResult).to.be.deep.equal(secondSetResult);
});
it('Should remove all debuff items from pinnedItems of the user if user have no buffs', () => {
user.stats.buffs.seafoam = true;
user.stats.buffs.spookySparkles = true;
user.stats.buffs.snowball = true;
user.stats.buffs.shinySeed = true;
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(firstSetResult).to.have.lengthOf(4);
user.stats.buffs.seafoam = false;
user.stats.buffs.spookySparkles = false;
user.stats.buffs.snowball = false;
user.stats.buffs.shinySeed = false;
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(secondSetResult).to.have.lengthOf(0);
});
});

View File

@@ -273,7 +273,6 @@ import BuyQuestModal from 'client/components/shops/quests/buyQuestModal.vue';
import notifications from 'client/mixins/notifications'; import notifications from 'client/mixins/notifications';
import { shouldDo } from 'common/script/cron'; import { shouldDo } from 'common/script/cron';
import inAppRewards from 'common/script/libs/inAppRewards'; import inAppRewards from 'common/script/libs/inAppRewards';
import spells from 'common/script/content/spells';
import taskDefaults from 'common/script/libs/taskDefaults'; import taskDefaults from 'common/script/libs/taskDefaults';
import { import {
@@ -383,26 +382,6 @@ export default {
let watchRefresh = this.forceRefresh; // eslint-disable-line let watchRefresh = this.forceRefresh; // eslint-disable-line
let rewards = inAppRewards(this.user); let rewards = inAppRewards(this.user);
// Add season rewards if user is affected
// @TODO: Add buff conditional
const seasonalSkills = {
snowball: 'salt',
spookySparkles: 'opaquePotion',
shinySeed: 'petalFreePotion',
seafoam: 'sand',
};
for (let key in seasonalSkills) {
if (this.getUserBuffs(key)) {
let debuff = seasonalSkills[key];
let item = Object.assign({}, spells.special[debuff]);
item.text = item.text();
item.notes = item.notes();
item.class = `shop_${key}`;
rewards.push(item);
}
}
return rewards; return rewards;
}, },
hasRewardsList () { hasRewardsList () {

View File

@@ -76,7 +76,7 @@
"wrongItemType": "The item type \"<%= type %>\" is not valid.", "wrongItemType": "The item type \"<%= type %>\" is not valid.",
"wrongItemPath": "The item path \"<%= path %>\" is not valid.", "wrongItemPath": "The item path \"<%= path %>\" is not valid.",
"unpinnedItem": "You unpinned <%= item %>! It will no longer display in your Rewards column.", "unpinnedItem": "You unpinned <%= item %>! It will no longer display in your Rewards column.",
"cannotUnpinArmoirPotion": "The Health Potion and Enchanted Armoire cannot be unpinned.", "cannotUnpinItem": "This item cannot be unpinned.",
"purchasedItem": "You bought <%= itemName %>", "purchasedItem": "You bought <%= itemName %>",
"ian": "Ian", "ian": "Ian",

View File

@@ -23,3 +23,10 @@ export const SUPPORTED_SOCIAL_NETWORKS = [
export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination
export const PARTY_LIMIT_MEMBERS = 30; export const PARTY_LIMIT_MEMBERS = 30;
export const TRANSFORMATION_DEBUFFS_LIST = {
snowball: 'salt',
spookySparkles: 'opaquePotion',
shinySeed: 'petalFreePotion',
seafoam: 'sand',
};

View File

@@ -289,6 +289,8 @@ spells.special = {
cast (user) { cast (user) {
user.stats.buffs.snowball = false; user.stats.buffs.snowball = false;
user.stats.gp -= 5; user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.salt'));
}, },
}, },
spookySparkles: { spookySparkles: {
@@ -320,6 +322,8 @@ spells.special = {
cast (user) { cast (user) {
user.stats.buffs.spookySparkles = false; user.stats.buffs.spookySparkles = false;
user.stats.gp -= 5; user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.opaquePotion'));
}, },
}, },
shinySeed: { shinySeed: {
@@ -351,6 +355,8 @@ spells.special = {
cast (user) { cast (user) {
user.stats.buffs.shinySeed = false; user.stats.buffs.shinySeed = false;
user.stats.gp -= 5; user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.petalFreePotion'));
}, },
}, },
seafoam: { seafoam: {
@@ -382,6 +388,7 @@ spells.special = {
cast (user) { cast (user) {
user.stats.buffs.seafoam = false; user.stats.buffs.seafoam = false;
user.stats.gp -= 5; user.stats.gp -= 5;
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.sand'));
}, },
}, },
nye: { nye: {

View File

@@ -77,6 +77,12 @@ api.updateStore = updateStore;
import inAppRewards from './libs/inAppRewards'; import inAppRewards from './libs/inAppRewards';
api.inAppRewards = inAppRewards; api.inAppRewards = inAppRewards;
import setDebuffPotionItems from './libs/setDebuffPotionItems';
api.setDebuffPotionItems = setDebuffPotionItems;
import getDebuffPotionItems from './libs/getDebuffPotionItems';
api.getDebuffPotionItems = getDebuffPotionItems;
import uuid from './libs/uuid'; import uuid from './libs/uuid';
api.uuid = uuid; api.uuid = uuid;

View File

@@ -0,0 +1,22 @@
import { TRANSFORMATION_DEBUFFS_LIST } from '../constants';
module.exports = function getDebuffPotionItems (user) {
const items = [];
const userBuffs = user.stats.buffs;
if (user) {
for (let key in TRANSFORMATION_DEBUFFS_LIST) {
if (userBuffs[key]) {
let debuff = TRANSFORMATION_DEBUFFS_LIST[key];
const item = {
path: `spells.special.${debuff}`,
type: 'debuffPotion',
};
items.push(item);
}
}
return items;
}
};

View File

@@ -177,6 +177,25 @@ module.exports = function getItemInfo (user, type, item, officialPinnedItems, la
pinType: 'seasonalSpell', pinType: 'seasonalSpell',
}; };
break; break;
case 'debuffPotion':
itemInfo = {
key: item.key,
mana: item.mana,
cast: item.cast,
immediateUse: item.immediateUse,
target: item.target,
text: item.text(language),
notes: item.notes(language),
value: item.value,
type: 'debuffPotion',
currency: 'gold',
locked: false,
purchaseType: 'debuffPotion',
class: `inventory_special_${item.key}`,
path: `spells.special.${item.key}`,
pinType: 'debuffPotion',
};
break;
case 'seasonalQuest': case 'seasonalQuest':
itemInfo = { itemInfo = {
key: item.key, key: item.key,

View File

@@ -0,0 +1,30 @@
import getDebuffPotionItems from './getDebuffPotionItems';
module.exports = function setDebuffPotionItems (user) {
const debuffPotionItems = getDebuffPotionItems(user);
if (debuffPotionItems.length) {
let isPresent = false;
const isUserHaveDebuffInPinnedItems = user.pinnedItems.find(pinnedItem => {
debuffPotionItems.forEach(debuffPotion => {
if (!isPresent) {
isPresent = debuffPotion.path === pinnedItem.path;
}
});
return isPresent;
});
if (!isUserHaveDebuffInPinnedItems) {
user.pinnedItems.push(...debuffPotionItems);
}
} else {
user.pinnedItems = user.pinnedItems.filter(item => {
return item.type !== 'debuffPotion';
});
}
return user;
};

View File

@@ -146,8 +146,9 @@ function togglePinnedItem (user, {item, type, path}, req = {}) {
} }
if (path === 'armoire' || path === 'potion') { if (path === 'armoire' || path === 'potion' || type === 'debuffPotion') {
throw new BadRequest(i18n.t('cannotUnpinArmoirPotion', req.language)); // @TODO: take into considertation debuffPotion type in message
throw new BadRequest(i18n.t('cannotUnpinItem', req.language));
} }
const isOfficialPinned = pathExistsInArray(officialPinnedItems, path) !== -1; const isOfficialPinned = pathExistsInArray(officialPinnedItems, path) !== -1;

View File

@@ -456,6 +456,8 @@ export function cron (options = {}) {
user.stats.buffs = _.cloneDeep(CLEAR_BUFFS); user.stats.buffs = _.cloneDeep(CLEAR_BUFFS);
} }
common.setDebuffPotionItems(user);
// Add 10 MP, or 10% of max MP if that'd be more. Perform this after Perfect Day for maximum benefit // Add 10 MP, or 10% of max MP if that'd be more. Perform this after Perfect Day for maximum benefit
// Adjust for fraction of dailies completed // Adjust for fraction of dailies completed
if (!user.preferences.sleep) { if (!user.preferences.sleep) {

View File

@@ -68,6 +68,9 @@ async function castSelfSpell (req, user, spell, quantity = 1) {
for (let i = 0; i < quantity; i += 1) { for (let i = 0; i < quantity; i += 1) {
spell.cast(user, null, req); spell.cast(user, null, req);
} }
common.setDebuffPotionItems(user);
await user.save(); await user.save();
} }
@@ -94,34 +97,37 @@ async function castPartySpell (req, party, partyMembers, user, spell, quantity =
return partyMembers; return partyMembers;
} }
async function castUserSpell (res, req, party, partyMembers, targetId, user, spell, quantity = 1) { async function castUserSpell (res, req, party, partyMember, targetId, user, spell, quantity = 1) {
if (!party && (!targetId || user._id === targetId)) { if (!party && (!targetId || user._id === targetId)) {
partyMembers = user; partyMember = user;
} else { } else {
if (!targetId) throw new BadRequest(res.t('targetIdUUID')); if (!targetId) throw new BadRequest(res.t('targetIdUUID'));
if (!party) throw new NotFound(res.t('partyNotFound')); if (!party) throw new NotFound(res.t('partyNotFound'));
partyMembers = await User partyMember = await User
.findOne({_id: targetId, 'party._id': party._id}) .findOne({_id: targetId, 'party._id': party._id})
.select(partyMembersFields) // We need all fields due to adding debuf spell to pinned items of target of the spell
// .select(partyMembersFields)
.exec(); .exec();
} }
if (!partyMembers) throw new NotFound(res.t('userWithIDNotFound', {userId: targetId})); if (!partyMember) throw new NotFound(res.t('userWithIDNotFound', {userId: targetId}));
for (let i = 0; i < quantity; i += 1) { for (let i = 0; i < quantity; i += 1) {
spell.cast(user, partyMembers, req); spell.cast(user, partyMember, req);
} }
if (partyMembers !== user) { common.setDebuffPotionItems(partyMember);
if (partyMember !== user) {
await Promise.all([ await Promise.all([
user.save(), user.save(),
partyMembers.save(), partyMember.save(),
]); ]);
} else { } else {
await partyMembers.save(); // partyMembers is user await partyMember.save(); // partyMembers is user
} }
return partyMembers; return partyMember;
} }
async function castSpell (req, res, {isV3 = false}) { async function castSpell (req, res, {isV3 = false}) {