mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Add API calls for shop inventories (#7810)
* Add API call for market inventory * changes to shop api calls * optimize shop categories * add API call for quests * add api call for time travelers shop * fic buying items in shops * fix linting errors * shop adjustments for iOS app * add tests to shops * fix syntax issues * Code formatting * correct indentation * add tests for api routes * fix time travelers and seasonal * Increase test coverage for shop routes * refactor: Pull out trinket count in time traveler route * refactor: Clarify instructions for seasonal shop script * lint: Remove extra new line * Adjust shops common test
This commit is contained in:
committed by
Sabe Jones
parent
aa00893f6c
commit
24d25026cf
@@ -11,6 +11,7 @@
|
|||||||
"danielText2Broken": "Oh... If you are participating in a boss quest, the boss will still damage you for your party mates' missed Dailies... Also, your own damage to the Boss (or items collected) will not be applied until you check out of the Inn...",
|
"danielText2Broken": "Oh... If you are participating in a boss quest, the boss will still damage you for your party mates' missed Dailies... Also, your own damage to the Boss (or items collected) will not be applied until you check out of the Inn...",
|
||||||
"alexander": "Alexander the Merchant",
|
"alexander": "Alexander the Merchant",
|
||||||
"welcomeMarket": "Welcome to the Market! Buy hard-to-find eggs and potions! Sell your extras! Commission useful services! Come see what we have to offer.",
|
"welcomeMarket": "Welcome to the Market! Buy hard-to-find eggs and potions! Sell your extras! Commission useful services! Come see what we have to offer.",
|
||||||
|
"welcomeMarketMobile": "Welcome to the Market! Buy hard-to-find eggs and potions! Come see what we have to offer.",
|
||||||
"displayItemForGold": "Do you want to sell a <strong><%= itemType %></strong>?",
|
"displayItemForGold": "Do you want to sell a <strong><%= itemType %></strong>?",
|
||||||
"displayEggForGold": "Do you want to sell a <strong><%= itemType %> Egg</strong>?",
|
"displayEggForGold": "Do you want to sell a <strong><%= itemType %> Egg</strong>?",
|
||||||
"displayPotionForGold": "Do you want to sell a <strong><%= itemType %> Potion</strong>?",
|
"displayPotionForGold": "Do you want to sell a <strong><%= itemType %> Potion</strong>?",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"justin": "Justin",
|
"justin": "Justin",
|
||||||
"ian": "Ian",
|
"ian": "Ian",
|
||||||
"ianText": "Welcome to the Quest Shop! Here you can use Quest Scrolls to battle monsters with your friends. Be sure to check out our fine array of Quest Scrolls for purchase on the right!",
|
"ianText": "Welcome to the Quest Shop! Here you can use Quest Scrolls to battle monsters with your friends. Be sure to check out our fine array of Quest Scrolls for purchase on the right!",
|
||||||
|
"ianTextMobile": "Welcome to the Quest Shop! Be sure to check out our fine array of Quest Scrolls for purchase!",
|
||||||
"ianBrokenText": "Welcome to the Quest Shop... Here you can use Quest Scrolls to battle monsters with your friends... Be sure to check out our fine array of Quest Scrolls for purchase on the right...",
|
"ianBrokenText": "Welcome to the Quest Shop... Here you can use Quest Scrolls to battle monsters with your friends... Be sure to check out our fine array of Quest Scrolls for purchase on the right...",
|
||||||
|
|
||||||
"missingKeyParam": "\"req.params.key\" is required.",
|
"missingKeyParam": "\"req.params.key\" is required.",
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"timeTravelersTitleNoSub": "<%= linkStartTyler %>Tyler<%= linkEnd %> and <%= linkStartVicky %>Vicky<%= linkEnd %>",
|
"timeTravelersTitleNoSub": "<%= linkStartTyler %>Tyler<%= linkEnd %> and <%= linkStartVicky %>Vicky<%= linkEnd %>",
|
||||||
"timeTravelersTitle": "Mysterious Time Travelers",
|
"timeTravelersTitle": "Mysterious Time Travelers",
|
||||||
"timeTravelersPopoverNoSub": "You'll need a Mystic Hourglass to summon the mysterious Time Travelers! <%= linkStart %>Subscribers<%= linkEnd %> earn one Mystic Hourglass for every three months of consecutive subscribing. Come back when you have a Mystic Hourglass, and the Time Travelers will fetch you a rare pet, mount, or Subscriber Item Set from the past... or maybe even the future.",
|
"timeTravelersPopoverNoSub": "You'll need a Mystic Hourglass to summon the mysterious Time Travelers! <%= linkStart %>Subscribers<%= linkEnd %> earn one Mystic Hourglass for every three months of consecutive subscribing. Come back when you have a Mystic Hourglass, and the Time Travelers will fetch you a rare pet, mount, or Subscriber Item Set from the past... or maybe even the future.",
|
||||||
|
"timeTravelersPopoverNoSubMobile": "You'll need a Mystic Hourglass to summon the mysterious Time Travelers! Subscribers earn one Mystic Hourglass for every three months of consecutive subscribing. Come back when you have a Mystic Hourglass, and the Time Travelers will fetch you a rare pet, mount, or Subscriber Item Set from the past... or maybe even the future.",
|
||||||
"timeTravelersPopover": "We see you have a Mystic Hourglass, so we will happily travel back in time for you! Please choose the pet, mount, or Mystery Item Set you would like. You can see a list of the past item sets <a href='http://habitica.wikia.com/wiki/Mystery_Item' target='_blank'>here</a>! If those don't satisfy you, perhaps you'd be interested in one of our fashionably futuristic Steampunk Item Sets?",
|
"timeTravelersPopover": "We see you have a Mystic Hourglass, so we will happily travel back in time for you! Please choose the pet, mount, or Mystery Item Set you would like. You can see a list of the past item sets <a href='http://habitica.wikia.com/wiki/Mystery_Item' target='_blank'>here</a>! If those don't satisfy you, perhaps you'd be interested in one of our fashionably futuristic Steampunk Item Sets?",
|
||||||
"timeTravelersAlreadyOwned": "Congratulations! You already own everything the Time Travelers currently offer. Thanks for supporting the site!",
|
"timeTravelersAlreadyOwned": "Congratulations! You already own everything the Time Travelers currently offer. Thanks for supporting the site!",
|
||||||
"mysticHourglassPopover": "A Mystic Hourglass allows you to purchase certain limited-time items, such as monthly Mystery Item Sets and awards from world bosses, from the past!",
|
"mysticHourglassPopover": "A Mystic Hourglass allows you to purchase certain limited-time items, such as monthly Mystery Item Sets and awards from world bosses, from the past!",
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ api.count = count;
|
|||||||
import statsComputed from './libs/statsComputed';
|
import statsComputed from './libs/statsComputed';
|
||||||
api.statsComputed = statsComputed;
|
api.statsComputed = statsComputed;
|
||||||
|
|
||||||
|
import shops from './libs/shops';
|
||||||
|
api.shops = shops;
|
||||||
|
|
||||||
import autoAllocate from './fns/autoAllocate';
|
import autoAllocate from './fns/autoAllocate';
|
||||||
import crit from './fns/crit';
|
import crit from './fns/crit';
|
||||||
import handleTwoHanded from './fns/handleTwoHanded';
|
import handleTwoHanded from './fns/handleTwoHanded';
|
||||||
|
|||||||
243
common/script/libs/shops.js
Normal file
243
common/script/libs/shops.js
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import content from '../content/index';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
|
|
||||||
|
let shops = {};
|
||||||
|
|
||||||
|
function lockQuest (quest, user) {
|
||||||
|
if (quest.lvl && user.stats.lvl < quest.lvl) return true;
|
||||||
|
if (user.achievements.quests) return quest.previous && !user.achievements.quests[quest.previous];
|
||||||
|
return quest.previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
shops.getMarketCategories = function getMarket (user, language) {
|
||||||
|
let categories = [];
|
||||||
|
let eggsCategory = {
|
||||||
|
identifier: 'eggs',
|
||||||
|
text: i18n.t('eggs', language),
|
||||||
|
notes: i18n.t('dropsExplanation', language),
|
||||||
|
};
|
||||||
|
|
||||||
|
eggsCategory.items = _(content.questEggs)
|
||||||
|
.values()
|
||||||
|
.filter(egg => egg.canBuy(user))
|
||||||
|
.concat(_.values(content.dropEggs))
|
||||||
|
.map(egg => {
|
||||||
|
return {
|
||||||
|
key: egg.key,
|
||||||
|
text: i18n.t('egg', {eggType: egg.text()}, language),
|
||||||
|
notes: egg.notes(language),
|
||||||
|
value: egg.value,
|
||||||
|
class: `Pet_Egg_${egg.key}`,
|
||||||
|
locked: false,
|
||||||
|
currency: 'gems',
|
||||||
|
purchaseType: 'eggs',
|
||||||
|
};
|
||||||
|
}).sortBy('key').value();
|
||||||
|
categories.push(eggsCategory);
|
||||||
|
|
||||||
|
let hatchingPotionsCategory = {
|
||||||
|
identifier: 'hatchingPotions',
|
||||||
|
text: i18n.t('hatchingPotions', language),
|
||||||
|
notes: i18n.t('dropsExplanation', language),
|
||||||
|
};
|
||||||
|
hatchingPotionsCategory.items = _(content.hatchingPotions)
|
||||||
|
.values()
|
||||||
|
.filter(hp => !hp.limited)
|
||||||
|
.map(hatchingPotion => {
|
||||||
|
return {
|
||||||
|
key: hatchingPotion.key,
|
||||||
|
text: hatchingPotion.text(language),
|
||||||
|
notes: hatchingPotion.notes(language),
|
||||||
|
class: `Pet_HatchingPotion_${hatchingPotion.key}`,
|
||||||
|
value: hatchingPotion.value,
|
||||||
|
locked: false,
|
||||||
|
currency: 'gems',
|
||||||
|
purchaseType: 'hatchingpotions',
|
||||||
|
};
|
||||||
|
}).sortBy('key').value();
|
||||||
|
categories.push(hatchingPotionsCategory);
|
||||||
|
|
||||||
|
let foodCategory = {
|
||||||
|
identifier: 'food',
|
||||||
|
text: i18n.t('food', language),
|
||||||
|
notes: i18n.t('dropsExplanation', language),
|
||||||
|
};
|
||||||
|
foodCategory.items = _(content.food)
|
||||||
|
.values()
|
||||||
|
.filter(food => food.canDrop || food.key === 'Saddle')
|
||||||
|
.map(foodItem => {
|
||||||
|
return {
|
||||||
|
key: foodItem.key,
|
||||||
|
text: foodItem.text(language),
|
||||||
|
notes: foodItem.notes(language),
|
||||||
|
class: `Pet_Food_${foodItem.key}`,
|
||||||
|
value: foodItem.value,
|
||||||
|
locked: false,
|
||||||
|
currency: 'gems',
|
||||||
|
purchaseType: 'food',
|
||||||
|
};
|
||||||
|
}).sortBy('key').value();
|
||||||
|
categories.push(foodCategory);
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
};
|
||||||
|
|
||||||
|
shops.getQuestShopCategories = function getQuestShopCategories (user, language) {
|
||||||
|
let categories = [];
|
||||||
|
|
||||||
|
_.each(content.userCanOwnQuestCategories, type => {
|
||||||
|
let category = {
|
||||||
|
identifier: type,
|
||||||
|
text: i18n.t(`${type}Quests`, language),
|
||||||
|
};
|
||||||
|
|
||||||
|
category.items = _(content.questsByLevel)
|
||||||
|
.filter(quest => quest.canBuy(user) && quest.category === type)
|
||||||
|
.map(quest => {
|
||||||
|
let locked = lockQuest(quest, user);
|
||||||
|
return {
|
||||||
|
key: quest.key,
|
||||||
|
text: quest.text(language),
|
||||||
|
notes: quest.notes(language),
|
||||||
|
value: quest.goldValue ? quest.goldValue : quest.value,
|
||||||
|
currency: quest.goldValue ? 'gold' : 'gems',
|
||||||
|
locked,
|
||||||
|
unlockCondition: quest.unlockCondition,
|
||||||
|
drop: quest.drop,
|
||||||
|
boss: quest.boss,
|
||||||
|
collect: quest.collect,
|
||||||
|
lvl: quest.lvl,
|
||||||
|
class: locked ? `inventory_quest_scroll_${quest.key}_locked` : `inventory_quest_scroll_${quest.key}`,
|
||||||
|
purchaseType: 'quests',
|
||||||
|
};
|
||||||
|
}).value();
|
||||||
|
|
||||||
|
categories.push(category);
|
||||||
|
});
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
};
|
||||||
|
|
||||||
|
shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, language) {
|
||||||
|
let categories = [];
|
||||||
|
let stable = {pets: 'Pet-', mounts: 'Mount_Head_'};
|
||||||
|
for (let type in stable) {
|
||||||
|
if (stable.hasOwnProperty(type)) {
|
||||||
|
let category = {
|
||||||
|
identifier: type,
|
||||||
|
text: i18n.t(type, language),
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let key in content.timeTravelStable[type]) {
|
||||||
|
if (content.timeTravelStable[type].hasOwnProperty(key)) {
|
||||||
|
if (!user.items[type][key]) {
|
||||||
|
let item = {
|
||||||
|
key,
|
||||||
|
text: content.timeTravelStable[type][key](language),
|
||||||
|
class: stable[type] + key,
|
||||||
|
type,
|
||||||
|
purchaseType: type,
|
||||||
|
value: 1,
|
||||||
|
notes: '',
|
||||||
|
locked: false,
|
||||||
|
currency: 'hourglasses',
|
||||||
|
};
|
||||||
|
category.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (category.items.length > 0) {
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sets = content.timeTravelerStore(user.items.gear.owned);
|
||||||
|
for (let setKey in sets) {
|
||||||
|
if (sets.hasOwnProperty(setKey)) {
|
||||||
|
let set = sets[setKey];
|
||||||
|
let category = {
|
||||||
|
identifier: set.key,
|
||||||
|
text: set.text(language),
|
||||||
|
purchaseAll: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
category.items = _.map(set.items, item => {
|
||||||
|
return {
|
||||||
|
key: item.key,
|
||||||
|
text: item.text(language),
|
||||||
|
notes: item.notes(language),
|
||||||
|
type: item.type,
|
||||||
|
purchaseType: 'gear',
|
||||||
|
value: 1,
|
||||||
|
locked: false,
|
||||||
|
currency: 'hourglasses',
|
||||||
|
class: `shop_${item.key}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (category.items.length > 0) {
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
};
|
||||||
|
|
||||||
|
// To switch seasons/available inventory, edit the availableSets object to whatever should be sold.
|
||||||
|
// let availableSets = {
|
||||||
|
// setKey: i18n.t('setTranslationString', language),
|
||||||
|
// };
|
||||||
|
shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, language) {
|
||||||
|
let availableSets = {
|
||||||
|
summerWarrior: i18n.t('daringSwashbucklerSet', language),
|
||||||
|
summerMage: i18n.t('emeraldMermageSet', language),
|
||||||
|
summerHealer: i18n.t('reefSeahealerSet', language),
|
||||||
|
summerRogue: i18n.t('roguishPirateSet', language),
|
||||||
|
summer2015Warrior: i18n.t('sunfishWarriorSet', language),
|
||||||
|
summer2015Mage: i18n.t('shipSoothsayerSet', language),
|
||||||
|
summer2015Healer: i18n.t('strappingSailorSet', language),
|
||||||
|
summer2015Rogue: i18n.t('reefRenegadeSet', language),
|
||||||
|
};
|
||||||
|
|
||||||
|
let categories = [];
|
||||||
|
|
||||||
|
let flatGearArray = _.toArray(content.gear.flat);
|
||||||
|
|
||||||
|
for (let key in availableSets) {
|
||||||
|
if (availableSets.hasOwnProperty(key)) {
|
||||||
|
let category = {
|
||||||
|
identifier: key,
|
||||||
|
text: availableSets[key],
|
||||||
|
};
|
||||||
|
|
||||||
|
category.items = _(flatGearArray).filter((gear) => {
|
||||||
|
if (gear.index !== key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return user.items.gear.owned[gear.key] !== true;
|
||||||
|
}).where({index: key}).map(gear => {
|
||||||
|
return {
|
||||||
|
key: gear.key,
|
||||||
|
text: gear.text(language),
|
||||||
|
notes: gear.notes(language),
|
||||||
|
value: 1,
|
||||||
|
type: gear.type,
|
||||||
|
specialClass: gear.specialClass,
|
||||||
|
locked: false,
|
||||||
|
currency: 'gems',
|
||||||
|
purchaseType: 'gear',
|
||||||
|
};
|
||||||
|
}).value();
|
||||||
|
if (category.items.length > 0) {
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = shops;
|
||||||
28
test/api/v3/integration/shops/GET-shops_market.test.js
Normal file
28
test/api/v3/integration/shops/GET-shops_market.test.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
|
describe('GET /shops/market', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a valid shop object', async () => {
|
||||||
|
let shop = await user.get('/shops/market');
|
||||||
|
|
||||||
|
expect(shop.identifier).to.equal('market');
|
||||||
|
expect(shop.text).to.eql(t('market'));
|
||||||
|
expect(shop.notes).to.eql(t('welcomeMarketMobile'));
|
||||||
|
expect(shop.imageName).to.be.a('string');
|
||||||
|
expect(shop.categories).to.be.an('array');
|
||||||
|
|
||||||
|
let categories = shop.categories.map(cat => cat.identifier);
|
||||||
|
|
||||||
|
expect(categories).to.include('eggs');
|
||||||
|
expect(categories).to.include('hatchingPotions');
|
||||||
|
expect(categories).to.include('food');
|
||||||
|
});
|
||||||
|
});
|
||||||
28
test/api/v3/integration/shops/GET-shops_quests.test.js
Normal file
28
test/api/v3/integration/shops/GET-shops_quests.test.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
|
describe('GET /shops/quests', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a valid shop object', async () => {
|
||||||
|
let shop = await user.get('/shops/quests');
|
||||||
|
|
||||||
|
expect(shop.identifier).to.equal('questShop');
|
||||||
|
expect(shop.text).to.eql(t('quests'));
|
||||||
|
expect(shop.notes).to.eql(t('ianTextMobile'));
|
||||||
|
expect(shop.imageName).to.be.a('string');
|
||||||
|
expect(shop.categories).to.be.an('array');
|
||||||
|
|
||||||
|
let categories = shop.categories.map(cat => cat.identifier);
|
||||||
|
|
||||||
|
expect(categories).to.include('unlockable');
|
||||||
|
expect(categories).to.include('gold');
|
||||||
|
expect(categories).to.include('pet');
|
||||||
|
});
|
||||||
|
});
|
||||||
22
test/api/v3/integration/shops/GET-shops_seasonal.test.js
Normal file
22
test/api/v3/integration/shops/GET-shops_seasonal.test.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
|
describe('GET /shops/seasonal', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a valid shop object', async () => {
|
||||||
|
let shop = await user.get('/shops/seasonal');
|
||||||
|
|
||||||
|
expect(shop.identifier).to.equal('seasonalShop');
|
||||||
|
expect(shop.text).to.eql(t('seasonalShop'));
|
||||||
|
expect(shop.notes).to.eql(t('seasonalShopSummerText'));
|
||||||
|
expect(shop.imageName).to.be.a('string');
|
||||||
|
expect(shop.categories).to.be.an('array');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
|
describe('GET /shops/time-travelers', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a valid shop object', async () => {
|
||||||
|
let shop = await user.get('/shops/time-travelers');
|
||||||
|
|
||||||
|
expect(shop.identifier).to.equal('timeTravelersShop');
|
||||||
|
expect(shop.text).to.eql(t('timeTravelers'));
|
||||||
|
expect(shop.notes).to.be.a('string');
|
||||||
|
expect(shop.imageName).to.be.a('string');
|
||||||
|
expect(shop.categories).to.be.an('array');
|
||||||
|
|
||||||
|
let categories = shop.categories.map(cat => cat.identifier);
|
||||||
|
|
||||||
|
expect(categories).to.include('pets');
|
||||||
|
expect(categories).to.include('mounts');
|
||||||
|
expect(categories).to.include('201606');
|
||||||
|
|
||||||
|
let mammothPet = shop.categories
|
||||||
|
.find(cat => cat.identifier === 'pets')
|
||||||
|
.items
|
||||||
|
.find(pet => pet.key === 'Mammoth-Base');
|
||||||
|
let mantisShrimp = shop.categories
|
||||||
|
.find(cat => cat.identifier === 'mounts')
|
||||||
|
.items
|
||||||
|
.find(pet => pet.key === 'MantisShrimp-Base');
|
||||||
|
|
||||||
|
expect(mammothPet).to.exist;
|
||||||
|
expect(mantisShrimp).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns active shop notes and imageName if user has trinkets', async () => {
|
||||||
|
await user.update({
|
||||||
|
'purchased.plan.consecutive.trinkets': 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
let shop = await user.get('/shops/time-travelers');
|
||||||
|
|
||||||
|
expect(shop.notes).to.eql(t('timeTravelersPopover'));
|
||||||
|
expect(shop.imageName).to.eql('npc_timetravelers_active');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns inactive shop notes and imageName if user has trinkets', async () => {
|
||||||
|
let shop = await user.get('/shops/time-travelers');
|
||||||
|
|
||||||
|
expect(shop.notes).to.eql(t('timeTravelersPopoverNoSubMobile'));
|
||||||
|
expect(shop.imageName).to.eql('npc_timetravelers');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not return mystery sets that are already owned', async () => {
|
||||||
|
await user.update({
|
||||||
|
'items.gear.owned': {
|
||||||
|
head_mystery_201606: true, // eslint-disable-line camelcase
|
||||||
|
armor_mystery_201606: true, // eslint-disable-line camelcase
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let shop = await user.get('/shops/time-travelers');
|
||||||
|
|
||||||
|
let categories = shop.categories.map(cat => cat.identifier);
|
||||||
|
|
||||||
|
expect(categories).to.not.include('201606');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not return pets and mounts that user already owns', async () => {
|
||||||
|
await user.update({
|
||||||
|
'items.mounts': {
|
||||||
|
'MantisShrimp-Base': true,
|
||||||
|
},
|
||||||
|
'items.pets': {
|
||||||
|
'Mammoth-Base': 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let shop = await user.get('/shops/time-travelers');
|
||||||
|
|
||||||
|
let mammothPet = shop.categories
|
||||||
|
.find(cat => cat.identifier === 'pets')
|
||||||
|
.items
|
||||||
|
.find(pet => pet.key === 'Mammoth-Base');
|
||||||
|
let mantisShrimp = shop.categories
|
||||||
|
.find(cat => cat.identifier === 'mounts')
|
||||||
|
.items
|
||||||
|
.find(pet => pet.key === 'MantisShrimp-Base');
|
||||||
|
|
||||||
|
expect(mammothPet).to.not.exist;
|
||||||
|
expect(mantisShrimp).to.not.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
84
test/common/libs/shops.js
Normal file
84
test/common/libs/shops.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import shared from '../../../common';
|
||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../helpers/common.helper';
|
||||||
|
|
||||||
|
describe('shops', () => {
|
||||||
|
let user = generateUser();
|
||||||
|
|
||||||
|
describe('market', () => {
|
||||||
|
let shopCategories = shared.shops.getMarketCategories(user);
|
||||||
|
|
||||||
|
it('contains at least the 3 default categories', () => {
|
||||||
|
expect(shopCategories.length).to.be.greaterThan(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not contain an empty category', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
expect(category.items.length).to.be.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('items contain required fields', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
_.each(category.items, (item) => {
|
||||||
|
expect(item).to.have.all.keys(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'class']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('questShop', () => {
|
||||||
|
let shopCategories = shared.shops.getQuestShopCategories(user);
|
||||||
|
|
||||||
|
it('does not contain an empty category', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
expect(category.items.length).to.be.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('items contain required fields', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
_.each(category.items, (item) => {
|
||||||
|
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'boss', 'class', 'collect', 'drop', 'unlockCondition', 'lvl');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('timeTravelers', () => {
|
||||||
|
let shopCategories = shared.shops.getTimeTravelersCategories(user);
|
||||||
|
|
||||||
|
it('does not contain an empty category', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
expect(category.items.length).to.be.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('items contain required fields', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
_.each(category.items, (item) => {
|
||||||
|
expect(item).to.have.all.keys('key', 'text', 'value', 'currency', 'locked', 'purchaseType', 'class', 'notes', 'class');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('seasonalShop', () => {
|
||||||
|
let shopCategories = shared.shops.getSeasonalShopCategories(user);
|
||||||
|
|
||||||
|
it('does not contain an empty category', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
expect(category.items.length).to.be.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('items contain required fields', () => {
|
||||||
|
_.each(shopCategories, (category) => {
|
||||||
|
_.each(category.items, (item) => {
|
||||||
|
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'specialClass', 'type');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -352,6 +352,11 @@ habitrpg.controller("InventoryCtrl",
|
|||||||
User.hourglassPurchase({params:{type:type,key:key}});
|
User.hourglassPurchase({params:{type:type,key:key}});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.marketShopCategories = Shared.shops.getMarketCategories(user);
|
||||||
|
$scope.questShopCategories = Shared.shops.getQuestShopCategories(user);
|
||||||
|
$scope.timeTravelersCategories = Shared.shops.getTimeTravelersCategories(user);
|
||||||
|
$scope.seasonalShopCategories = Shared.shops.getSeasonalShopCategories(user);
|
||||||
|
|
||||||
function _updateDropAnimalCount(items) {
|
function _updateDropAnimalCount(items) {
|
||||||
$scope.petCount = Shared.count.beastMasterProgress(items.pets);
|
$scope.petCount = Shared.count.beastMasterProgress(items.pets);
|
||||||
$scope.mountCount = Shared.count.mountMasterProgress(items.mounts);
|
$scope.mountCount = Shared.count.mountMasterProgress(items.mounts);
|
||||||
|
|||||||
123
website/server/controllers/api-v3/shops.js
Normal file
123
website/server/controllers/api-v3/shops.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { authWithHeaders } from '../../middlewares/api-v3/auth';
|
||||||
|
import shops from '../../../../common/script/libs/shops';
|
||||||
|
|
||||||
|
let api = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiIgnore
|
||||||
|
* @api {get} /api/v3/shops/market get the available items for the market
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName GetMarketItems
|
||||||
|
* @apiGroup Shops
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data List of push devices
|
||||||
|
* @apiSuccess {string} message Success message
|
||||||
|
*/
|
||||||
|
api.getMarketItems = {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/shops/market',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
|
||||||
|
let resObject = {
|
||||||
|
identifier: 'market',
|
||||||
|
text: res.t('market'),
|
||||||
|
notes: res.t('welcomeMarketMobile'),
|
||||||
|
imageName: 'npc_alex',
|
||||||
|
categories: shops.getMarketCategories(user, req.language),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.respond(200, resObject);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiIgnore
|
||||||
|
* @api {get} /api/v3/shops/quests get the available items for the quests shop
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName GetQuestShopItems
|
||||||
|
* @apiGroup Shops
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data List of push devices
|
||||||
|
* @apiSuccess {string} message Success message
|
||||||
|
*/
|
||||||
|
api.getQuestShopItems = {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/shops/quests',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
|
||||||
|
let resObject = {
|
||||||
|
identifier: 'questShop',
|
||||||
|
text: res.t('quests'),
|
||||||
|
notes: res.t('ianTextMobile'),
|
||||||
|
imageName: 'npc_ian',
|
||||||
|
categories: shops.getQuestShopCategories(user, req.language),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.respond(200, resObject);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiIgnore
|
||||||
|
* @api {get} /api/v3/shops/time-travelers get the available items for the time travelers shop
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName GetTimeTravelersShopItems
|
||||||
|
* @apiGroup Shops
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data List of push devices
|
||||||
|
* @apiSuccess {string} message Success message
|
||||||
|
*/
|
||||||
|
api.getTimeTravelerShopItems = {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/shops/time-travelers',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
let hasTrinkets = user.purchased.plan.consecutive.trinkets > 0;
|
||||||
|
|
||||||
|
let resObject = {
|
||||||
|
identifier: 'timeTravelersShop',
|
||||||
|
text: res.t('timeTravelers'),
|
||||||
|
notes: hasTrinkets ? res.t('timeTravelersPopover') : res.t('timeTravelersPopoverNoSubMobile'),
|
||||||
|
imageName: hasTrinkets ? 'npc_timetravelers_active' : 'npc_timetravelers',
|
||||||
|
categories: shops.getTimeTravelersCategories(user, req.language),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.respond(200, resObject);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiIgnore
|
||||||
|
* @api {get} /api/v3/shops/seasonal get the available items for the seasonal shop
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName GetSeasonalShopItems
|
||||||
|
* @apiGroup Shops
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data List of push devices
|
||||||
|
* @apiSuccess {string} message Success message
|
||||||
|
*/
|
||||||
|
api.getSeasonalShopItems = {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/shops/seasonal',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
|
||||||
|
let resObject = {
|
||||||
|
identifier: 'seasonalShop',
|
||||||
|
text: res.t('seasonalShop'),
|
||||||
|
notes: res.t('seasonalShopSummerText'),
|
||||||
|
imageName: 'seasonalshop_open',
|
||||||
|
categories: shops.getSeasonalShopCategories(user, req.language),
|
||||||
|
};
|
||||||
|
|
||||||
|
res.respond(200, resObject);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = api;
|
||||||
@@ -110,61 +110,19 @@
|
|||||||
button.btn.btn-primary.btn-block(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text()}}", gold: "{{selectedFood.value}}"})
|
button.btn.btn-primary.btn-block(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text()}}", gold: "{{selectedFood.value}}"})
|
||||||
|
|
||||||
menu.inventory-list(type='list')
|
menu.inventory-list(type='list')
|
||||||
li.customize-menu
|
li.customize-menu(ng-repeat='category in marketShopCategories')
|
||||||
menu.pets-menu(label=env.t('eggs'))
|
menu.pets-menu(label='{{category.text}}')
|
||||||
p.muted!=env.t('dropsExplanation')
|
p.muted(ng-bind-html='category.notes')
|
||||||
|
|
||||||
div(ng-repeat='egg in Content.eggs', ng-if='egg.canBuy(user)')
|
div(ng-repeat='item in category.items')
|
||||||
button.customize-option(class='Pet_Egg_{{::egg.key}}',
|
button.customize-option(class='{{item.class}}',
|
||||||
popover='{{::egg.notes()}}', popover-append-to-body='true',
|
popover='{{item.notes}}', popover-append-to-body='true',
|
||||||
popover-title!=env.t("egg", {eggType: "{{::egg.text()}}"}),
|
popover-title!='{{item.text}}',
|
||||||
popover-trigger='mouseenter', popover-placement='top',
|
popover-trigger='mouseenter', popover-placement='top',
|
||||||
ng-click='purchase("eggs", egg)')
|
ng-click='purchase(category.identifier, item)')
|
||||||
p {{::egg.value}}
|
p {{item.value}}
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
span.Pet_Currency_Gem1x.inline-gems(ng-if='item.currency === "gems"')
|
||||||
|
span(class='shop_gold', ng-if='item.currency === "gold"')
|
||||||
li.customize-menu
|
|
||||||
menu.pets-menu(label=env.t('hatchingPotions'))
|
|
||||||
p.muted!=env.t('dropsExplanation')
|
|
||||||
div(ng-repeat='pot in Content.hatchingPotions', ng-if='!pot.premium')
|
|
||||||
button.customize-option(class='Pet_HatchingPotion_{{::pot.key}}',
|
|
||||||
popover='{{::pot.notes()}}', popover-append-to-body='true',
|
|
||||||
popover-title!=env.t("potion", {potionType: "{{::pot.text()}}"}),
|
|
||||||
popover-trigger='mouseenter', popover-placement='top',
|
|
||||||
ng-click='purchase("hatchingPotions", pot)')
|
|
||||||
p
|
|
||||||
| {{::pot.value}}
|
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
|
||||||
|
|
||||||
// li.customize-menu
|
|
||||||
menu.pets-menu!=env.t('magicHatchingPotions') + " - " + env.t('springEventAvailability')
|
|
||||||
p.muted=env.t('premiumPotionNoDropExplanation')
|
|
||||||
div(ng-repeat='pot in Content.hatchingPotions', ng-if='pot.premium && pot.canBuy(user)')
|
|
||||||
button.customize-option(class='Pet_HatchingPotion_{{::pot.key}}',
|
|
||||||
popover='{{::pot.notes()}} {{::pot.addlNotes()}}', popover-append-to-body='true',
|
|
||||||
popover-title!=env.t("potion", {potionType: "{{::pot.text()}}"}),
|
|
||||||
popover-trigger='mouseenter', popover-placement='top',
|
|
||||||
ng-click='purchase("hatchingPotions", pot)')
|
|
||||||
p
|
|
||||||
| {{::pot.value}}
|
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
|
||||||
|
|
||||||
li.customize-menu
|
|
||||||
menu.pets-menu(label=env.t('food'))
|
|
||||||
p.muted!=env.t('dropsExplanation')
|
|
||||||
div(ng-repeat='food in Content.food', ng-if='food.canBuy(user)')
|
|
||||||
button.customize-option(class='Pet_Food_{{::food.key}}',
|
|
||||||
popover='{{::food.notes()}}', popover-title='{{::food.text()}}',
|
|
||||||
popover-trigger='mouseenter', popover-placement='top',
|
|
||||||
popover-append-to-body='true',
|
|
||||||
ng-click='purchase("food", food)')
|
|
||||||
p
|
|
||||||
| {{::food.value}}
|
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
|
||||||
|
|
||||||
li.customize-menu
|
|
||||||
menu.pets-menu(label=env.t('quests'))
|
|
||||||
p=env.t('whereAreMyQuests')
|
|
||||||
|
|
||||||
li.customize-menu
|
li.customize-menu
|
||||||
menu.pets-menu(label=env.t('special'))
|
menu.pets-menu(label=env.t('special'))
|
||||||
|
|||||||
@@ -18,20 +18,20 @@ include ../../shared/mixins
|
|||||||
.col-md-6.border-left
|
.col-md-6.border-left
|
||||||
li.customize-menu
|
li.customize-menu
|
||||||
h3.equipment-title=env.t('questsForSale')
|
h3.equipment-title=env.t('questsForSale')
|
||||||
div(ng-repeat='type in Content.userCanOwnQuestCategories')
|
div(ng-repeat='category in questShopCategories')
|
||||||
menu.pets-menu(label='{{env.t(type + "Quests")}}')
|
menu.pets-menu(label='{{category.text}}')
|
||||||
div(ng-repeat='quest in Content.questsByLevel', ng-if='quest.canBuy(user) && quest.category === type')
|
div(ng-repeat='quest in category.items')
|
||||||
button.customize-option(ng-class='lockQuest(quest) ? "inventory_quest_scroll_locked inventory_quest_scroll_{{::quest.key}}_locked locked" : "inventory_quest_scroll inventory_quest_scroll_{{::quest.key}}"',
|
button.customize-option(ng-class='quest.class',
|
||||||
data-popover-html="{{::lockQuest(quest,true) ? env.t('scrollsPre') : questPopover(quest) | markdown}}",
|
data-popover-html="{{quest.locked ? env.t('scrollsPre') : questPopover(quest) | markdown}}",
|
||||||
popover-title='{{::quest.text()}}', popover-append-to-body="true",
|
popover-title='{{quest.text}}', popover-append-to-body="true",
|
||||||
popover-trigger='mouseenter', ng-click='buyQuest(quest.key)')
|
popover-trigger='mouseenter', ng-click='buyQuest(quest.key)')
|
||||||
p(ng-if='quest.unlockCondition')
|
p(ng-if='quest.unlockCondition')
|
||||||
| {{::quest.unlockCondition.text()}}
|
| {{::quest.unlockCondition.text()}}
|
||||||
p(ng-if='!quest.unlockCondition && quest.category !== "gold" && !lockQuest(quest)')
|
p(ng-if='!quest.unlockCondition && category.identifier !== "gold" && !quest.locked')
|
||||||
| {{::quest.value}}
|
| {{::quest.value}}
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
span.Pet_Currency_Gem1x.inline-gems
|
||||||
p(ng-if='quest.category === "gold" && !lockQuest(quest)')
|
p(ng-if='category.identifier === "gold" && !quest.locked')
|
||||||
| {{::quest.goldValue}}
|
| {{::quest.value}}
|
||||||
span.shop_gold
|
span.shop_gold
|
||||||
p(ng-if='quest.lvl && lockQuest(quest)')=env.t('level')
|
p(ng-if='quest.lvl && quest.locked')=env.t('level')
|
||||||
| {{::quest.lvl}}
|
| {{::quest.lvl}}
|
||||||
|
|||||||
@@ -11,15 +11,6 @@
|
|||||||
.well(ng-if='User.user.achievements.rebirths > 0')=env.t('seasonalShopRebirth')
|
.well(ng-if='User.user.achievements.rebirths > 0')=env.t('seasonalShopRebirth')
|
||||||
|
|
||||||
li.customize-menu.inventory-gear
|
li.customize-menu.inventory-gear
|
||||||
// menu.pets-menu(label=env.t('quests'))
|
|
||||||
div(ng-repeat='quest in ::getSeasonalShopQuests()')
|
|
||||||
button.customize-option(ng-class='(quest.previous && !user.achievements.quests[quest.previous]) ? "inventory_quest_scroll_locked inventory_quest_scroll_{{::quest.key}}_locked locked" : "inventory_quest_scroll inventory_quest_scroll_{{::quest.key}}"'
|
|
||||||
data-popover-html="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : questPopover(quest) | markdown}}",
|
|
||||||
popover-append-to-body='true', popover-title='{{::quest.text()}}',
|
|
||||||
popover-trigger='mouseenter', popover-placement='right',
|
|
||||||
ng-click='buyQuest(quest.key)')
|
|
||||||
p {{::quest.value}}
|
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
|
||||||
menu.pets-menu(label=env.t('seasonalItems'))
|
menu.pets-menu(label=env.t('seasonalItems'))
|
||||||
div
|
div
|
||||||
button.customize-option(class='inventory_special_seafoam',
|
button.customize-option(class='inventory_special_seafoam',
|
||||||
@@ -30,24 +21,12 @@
|
|||||||
ng-click='purchase("special", Content.spells.special.seafoam)')
|
ng-click='purchase("special", Content.spells.special.seafoam)')
|
||||||
p {{::Content.spells.special.seafoam.value}}
|
p {{::Content.spells.special.seafoam.value}}
|
||||||
span(class='shop_gold')
|
span(class='shop_gold')
|
||||||
// div
|
menu.pets-menu(label='{{category.text}}', ng-repeat='category in seasonalShopCategories')
|
||||||
button.customize-option(class='Pet_HatchingPotion_Peppermint',
|
div(ng-repeat='item in category.items',
|
||||||
popover='{{::Content.hatchingPotions.Peppermint.notes()}}',
|
ng-class="{transparent: user.items.gear.owned[item.key] !== undefined}",
|
||||||
popover-title!=env.t("potion", {potionType: "{{::Content.hatchingPotions.Peppermint.text()}}"}),
|
ng-if='!user.items.gear.owned[item.key]')
|
||||||
popover-trigger='mouseenter', popover-placement='right',
|
button.customize-option(class='shop_{{item.key}}',
|
||||||
popover-append-to-body='true',
|
popover='{{item.notes}}', popover-title='{{item.text}}',
|
||||||
ng-click='purchase("hatchingPotions", Content.hatchingPotions.Peppermint)')
|
|
||||||
p {{::Content.hatchingPotions.Peppermint.value}}
|
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
|
||||||
// div
|
|
||||||
button.customize-option(popover='{{::Content.spells.special.nye.notes()}}', popover-title='{{::Content.spells.special.nye.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='castStart(Content.spells.special.nye)', class='inventory_special_nye')
|
|
||||||
p {{Content.spells.special.nye.value}}
|
|
||||||
span(class='shop_gold')
|
|
||||||
menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in ::{summerWarrior:env.t("daringSwashbucklerSet"), summerMage:env.t("emeraldMermageSet"), summerHealer:env.t("reefSeahealerSet"), summerRogue:env.t("roguishPirateSet"), summer2015Warrior:env.t("sunfishWarriorSet"), summer2015Mage:env.t("shipSoothsayerSet"), summer2015Healer:env.t("strappingSailorSet"), summer2015Rogue:env.t("reefRenegadeSet")}')
|
|
||||||
div(ng-repeat='item in ::getSeasonalShopArray(set)',
|
|
||||||
ng-class="{transparent: user.items.gear.owned[item.key] !== undefined}")
|
|
||||||
button.customize-option(class='shop_{{::item.key}}',
|
|
||||||
popover='{{::item.notes()}}', popover-title='{{::item.text()}}',
|
|
||||||
popover-trigger='mouseenter', popover-placement='right',
|
popover-trigger='mouseenter', popover-placement='right',
|
||||||
popover-append-to-body='true',
|
popover-append-to-body='true',
|
||||||
ng-click='purchase(item.type,item)')
|
ng-click='purchase(item.type,item)')
|
||||||
|
|||||||
@@ -20,19 +20,10 @@
|
|||||||
.row
|
.row
|
||||||
.col-md-12
|
.col-md-12
|
||||||
li.customize-menu.inventory-gear
|
li.customize-menu.inventory-gear
|
||||||
each prepend, type in {pets:'Pet-', mounts:'Mount_Head_'}
|
menu.pets-menu(label='{{::category.text}}', ng-repeat='category in timeTravelersCategories')
|
||||||
menu.pets-menu(label=env.t('#{type}'), ng-if='!hasAllTimeTravelerItemsOfType("#{type}")')
|
div(ng-repeat='item in category.items', ng-if='category.identifier === "pets" || category.identifier === "mounts" ? !user.items[category.identifier][item.key] : !user.items.gear.owned[item.key]')
|
||||||
div(ng-repeat='(item, text) in Content.timeTravelStable["#{type}"]', style='margin-top:0')
|
button.customize-option(class='{{item.class ? item.class : "shop_"+item.key}}',
|
||||||
button.pet-button(class='#{prepend}{{::item}}', style='margin-top:0',
|
popover='{{item.notes}}', popover-title='{{item.text}}',
|
||||||
ng-if='!user.items["#{type}"][item]',
|
|
||||||
popover='{{::text()}}', popover-trigger='mouseenter',
|
|
||||||
popover-placement='right', popover-append-to-body='true',
|
|
||||||
ng-click='clickTimeTravelItem("#{type}",item)')
|
|
||||||
li.customize-menu.inventory-gear
|
|
||||||
menu.pets-menu(label='{{::set.text()}}', ng-repeat='set in Content.timeTravelerStore(user.items.gear.owned)')
|
|
||||||
div(ng-repeat='item in set.items')
|
|
||||||
button.customize-option(class='shop_{{::item.key}}',
|
|
||||||
popover='{{::item.notes()}}', popover-title='{{::item.text()}}',
|
|
||||||
popover-trigger='mouseenter', popover-placement='right',
|
popover-trigger='mouseenter', popover-placement='right',
|
||||||
popover-append-to-body='true',
|
popover-append-to-body='true',
|
||||||
ng-click='User.buyMysterySet({params:{key:set.key}})')
|
ng-click='category.purchaseAll ? User.buyMysterySet({params:{key:category.identifier}}) : clickTimeTravelItem(category.identifier,item.key)')
|
||||||
|
|||||||
Reference in New Issue
Block a user