mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 05:37:22 +01:00
Add special spells to Seasonal Shop API (#8138)
* WIP(shops): add spells to Seasonal API * refactor(shops): remove superfluous if * feat(shops): handle spell purchasing * fix(test): proper required fields check Also corrects a linting error. * refactor(shops): use constants
This commit is contained in:
@@ -70,6 +70,7 @@
|
|||||||
"less-loader": "^2.2.3",
|
"less-loader": "^2.2.3",
|
||||||
"lodash": "^3.10.1",
|
"lodash": "^3.10.1",
|
||||||
"lodash.setwith": "^4.2.0",
|
"lodash.setwith": "^4.2.0",
|
||||||
|
"lodash.pickby": "^4.2.0",
|
||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"method-override": "^2.3.5",
|
"method-override": "^2.3.5",
|
||||||
"moment": "^2.13.0",
|
"moment": "^2.13.0",
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ describe('shops', () => {
|
|||||||
it('items contain required fields', () => {
|
it('items contain required fields', () => {
|
||||||
_.each(shopCategories, (category) => {
|
_.each(shopCategories, (category) => {
|
||||||
_.each(category.items, (item) => {
|
_.each(category.items, (item) => {
|
||||||
expect(item).to.have.all.keys(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'class']);
|
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'class'], (key) => {
|
||||||
|
expect(_.has(item, key)).to.eql(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -46,7 +48,9 @@ describe('shops', () => {
|
|||||||
it('items contain required fields', () => {
|
it('items contain required fields', () => {
|
||||||
_.each(shopCategories, (category) => {
|
_.each(shopCategories, (category) => {
|
||||||
_.each(category.items, (item) => {
|
_.each(category.items, (item) => {
|
||||||
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'boss', 'class', 'collect', 'drop', 'unlockCondition', 'lvl');
|
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'boss', 'class', 'collect', 'drop', 'unlockCondition', 'lvl'], (key) => {
|
||||||
|
expect(_.has(item, key)).to.eql(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -70,7 +74,9 @@ describe('shops', () => {
|
|||||||
it('items contain required fields', () => {
|
it('items contain required fields', () => {
|
||||||
_.each(shopCategories, (category) => {
|
_.each(shopCategories, (category) => {
|
||||||
_.each(category.items, (item) => {
|
_.each(category.items, (item) => {
|
||||||
expect(item).to.have.all.keys('key', 'text', 'value', 'currency', 'locked', 'purchaseType', 'class', 'notes', 'class');
|
_.each(['key', 'text', 'value', 'currency', 'locked', 'purchaseType', 'class', 'notes', 'class'], (key) => {
|
||||||
|
expect(_.has(item, key)).to.eql(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -94,7 +100,9 @@ describe('shops', () => {
|
|||||||
it('items contain required fields', () => {
|
it('items contain required fields', () => {
|
||||||
_.each(shopCategories, (category) => {
|
_.each(shopCategories, (category) => {
|
||||||
_.each(category.items, (item) => {
|
_.each(category.items, (item) => {
|
||||||
expect(item).to.have.all.keys('key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'specialClass', 'type');
|
_.each(['key', 'text', 'notes', 'value', 'currency', 'locked', 'purchaseType', 'type'], (key) => {
|
||||||
|
expect(_.has(item, key)).to.eql(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import pickBy from 'lodash.pickby'; // Not available in lodash 3
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
|
|
||||||
@@ -208,12 +209,12 @@ shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, la
|
|||||||
return categories;
|
return categories;
|
||||||
};
|
};
|
||||||
|
|
||||||
// To switch seasons/available inventory, edit the availableSets object to whatever should be sold.
|
// To switch seasons/available inventory, edit the AVAILABLE_SETS object to whatever should be sold.
|
||||||
// let availableSets = {
|
// let AVAILABLE_SETS = {
|
||||||
// setKey: i18n.t('setTranslationString', language),
|
// setKey: i18n.t('setTranslationString', language),
|
||||||
// };
|
// };
|
||||||
shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, language) {
|
shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, language) {
|
||||||
let availableSets = {
|
const AVAILABLE_SETS = {
|
||||||
fallHealer: i18n.t('mummyMedicSet', language),
|
fallHealer: i18n.t('mummyMedicSet', language),
|
||||||
fall2015Healer: i18n.t('potionerSet', language),
|
fall2015Healer: i18n.t('potionerSet', language),
|
||||||
fallMage: i18n.t('witchyWizardSet', language),
|
fallMage: i18n.t('witchyWizardSet', language),
|
||||||
@@ -224,15 +225,45 @@ shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, lang
|
|||||||
fall2015Warrior: i18n.t('scarecrowWarriorSet', language),
|
fall2015Warrior: i18n.t('scarecrowWarriorSet', language),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AVAILABLE_SPELLS = [
|
||||||
|
'spookySparkles',
|
||||||
|
];
|
||||||
|
|
||||||
let categories = [];
|
let categories = [];
|
||||||
|
|
||||||
let flatGearArray = _.toArray(content.gear.flat);
|
let flatGearArray = _.toArray(content.gear.flat);
|
||||||
|
|
||||||
for (let key in availableSets) {
|
let spells = pickBy(content.spells.special, (spell, key) => {
|
||||||
if (availableSets.hasOwnProperty(key)) {
|
return _.indexOf(AVAILABLE_SPELLS, key) !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.keys(spells).length > 0) {
|
||||||
|
let category = {
|
||||||
|
identifier: 'spells',
|
||||||
|
text: i18n.t('seasonalItems', language),
|
||||||
|
};
|
||||||
|
|
||||||
|
category.items = _.map(spells, (spell, key) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
text: spell.text(language),
|
||||||
|
notes: spell.notes(language),
|
||||||
|
value: spell.value,
|
||||||
|
type: 'special',
|
||||||
|
currency: 'gold',
|
||||||
|
locked: false,
|
||||||
|
purchaseType: 'spells',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key in AVAILABLE_SETS) {
|
||||||
|
if (AVAILABLE_SETS.hasOwnProperty(key)) {
|
||||||
let category = {
|
let category = {
|
||||||
identifier: key,
|
identifier: key,
|
||||||
text: availableSets[key],
|
text: AVAILABLE_SETS[key],
|
||||||
};
|
};
|
||||||
|
|
||||||
category.items = _(flatGearArray).filter((gear) => {
|
category.items = _(flatGearArray).filter((gear) => {
|
||||||
|
|||||||
@@ -832,7 +832,7 @@ api.purchase = {
|
|||||||
url: '/user/purchase/:type/:key',
|
url: '/user/purchase/:type/:key',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
let user = res.locals.user;
|
||||||
let purchaseRes = common.ops.purchase(user, req, res.analytics);
|
let purchaseRes = req.params.type === 'spells' ? common.ops.buySpecialSpell(user, req) : common.ops.purchase(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...purchaseRes);
|
res.respond(200, ...purchaseRes);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,16 +11,6 @@
|
|||||||
// .well=env.t('seasonalShopRebirth')
|
// .well=env.t('seasonalShopRebirth')
|
||||||
|
|
||||||
li.customize-menu.inventory-gear
|
li.customize-menu.inventory-gear
|
||||||
menu.pets-menu(label=env.t('seasonalItems'))
|
|
||||||
div
|
|
||||||
button.customize-option(class='inventory_special_spookySparkles',
|
|
||||||
popover='{{::Content.spells.special.spookySparkles.notes()}}',
|
|
||||||
popover-title='{{::Content.spells.special.spookySparkles.text()}}',
|
|
||||||
popover-trigger='mouseenter', popover-placement='right',
|
|
||||||
popover-append-to-body='true',
|
|
||||||
ng-click='purchase("special", Content.spells.special.spookySparkles)')
|
|
||||||
p {{::Content.spells.special.spookySparkles.value}}
|
|
||||||
span(class='shop_gold')
|
|
||||||
menu.pets-menu(label='{{category.text}}', ng-repeat='category in seasonalShopCategories')
|
menu.pets-menu(label='{{category.text}}', ng-repeat='category in seasonalShopCategories')
|
||||||
div(ng-repeat='item in category.items', ng-if='!user.items.gear.owned[item.key]')
|
div(ng-repeat='item in category.items', ng-if='!user.items.gear.owned[item.key]')
|
||||||
button.customize-option(class='shop_{{item.key}}',
|
button.customize-option(class='shop_{{item.key}}',
|
||||||
@@ -29,5 +19,5 @@
|
|||||||
popover-append-to-body='true',
|
popover-append-to-body='true',
|
||||||
ng-click='purchase(item.type,item)')
|
ng-click='purchase(item.type,item)')
|
||||||
div
|
div
|
||||||
| {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}
|
| {{item.value}}
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
span(ng-class="{ 'shop_gold': item.currency === 'gold', 'Pet_Currency_Gem1x inline-gems': item.currency === 'gems'}")
|
||||||
|
|||||||
Reference in New Issue
Block a user