Merge branch 'release' into develop

This commit is contained in:
Sabe Jones 2020-06-09 15:28:16 -05:00
commit 3063a38d60
4 changed files with 135 additions and 16 deletions

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "habitica",
"version": "4.144.0",
"version": "4.144.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.144.0",
"version": "4.144.1",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.10.2",

View file

@ -10,6 +10,8 @@ describe('shared.ops.unlock', () => {
const unlockGearSetPath = 'items.gear.owned.headAccessory_special_bearEars,items.gear.owned.headAccessory_special_cactusEars,items.gear.owned.headAccessory_special_foxEars,items.gear.owned.headAccessory_special_lionEars,items.gear.owned.headAccessory_special_pandaEars,items.gear.owned.headAccessory_special_pigEars,items.gear.owned.headAccessory_special_tigerEars,items.gear.owned.headAccessory_special_wolfEars';
const backgroundUnlockPath = 'background.giant_florals';
const backgroundSetUnlockPath = 'background.archery_range,background.giant_florals,background.rainbows_end';
const hairUnlockPath = 'hair.color.rainbow,hair.color.yellow,hair.color.green,hair.color.purple,hair.color.blue,hair.color.TRUred';
const facialHairUnlockPath = 'hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3';
const usersStartingGems = 50 / 4;
beforeEach(() => {
@ -206,6 +208,40 @@ describe('shared.ops.unlock', () => {
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks a full set of hair items', () => {
user.purchased.hair.color = {};
const initialHairColors = Object.keys(user.purchased.hair.color).length;
const [, message] = unlock(user, { query: { path: hairUnlockPath } });
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = hairUnlockPath.split(',');
individualPaths.forEach(path => {
expect(get(user.purchased, path)).to.be.true;
});
expect(Object.keys(user.purchased.hair.color).length)
.to.equal(initialHairColors + individualPaths.length);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks the facial hair set', () => {
user.purchased.hair.mustache = {};
user.purchased.hair.beard = {};
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
const initialBeard = Object.keys(user.purchased.hair.mustache).length;
const [, message] = unlock(user, { query: { path: facialHairUnlockPath } });
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = facialHairUnlockPath.split(',');
individualPaths.forEach(path => {
expect(get(user.purchased, path)).to.be.true;
});
expect(Object.keys(user.purchased.hair.mustache).length + Object.keys(user.purchased.hair.beard).length) // eslint-disable-line max-len
.to.equal(initialMustache + initialBeard + individualPaths.length);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks a full set of gear', () => {
const initialGear = Object.keys(user.items.gear.owned).length;
const [, message] = unlock(user, { query: { path: unlockGearSetPath } });
@ -246,6 +282,37 @@ describe('shared.ops.unlock', () => {
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (hair color)', () => {
user.purchased.hair.color = {};
const path = hairUnlockPath.split(',')[0];
const initialColorHair = Object.keys(user.purchased.hair.color).length;
const [, message] = unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.hair.color).length).to.equal(initialColorHair + 1);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (facial hair)', () => {
user.purchased.hair.mustache = {};
user.purchased.hair.beard = {};
const path = facialHairUnlockPath.split(',')[0];
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
const initialBeard = Object.keys(user.purchased.hair.beard).length;
const [, message] = unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.hair.mustache).length).to.equal(initialMustache + 1);
expect(Object.keys(user.purchased.hair.beard).length).to.equal(initialBeard);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (gear)', () => {
const path = unlockGearSetPath.split(',')[0];
const initialGear = Object.keys(user.items.gear.owned).length;

View file

@ -28,12 +28,16 @@ function invalidSet (req) {
* Return an item given its path and the type of set
*/
function getItemByPath (path, setType) {
const itemKey = splitPathItem(path)[1];
const item = setType === 'gear'
? content.gear.flat[itemKey]
: content.appearances[setType][itemKey];
const [itemPathParent, itemKey] = splitPathItem(path);
return item;
if (setType === 'gear') return content.gear.flat[itemKey];
if (setType === 'hair') {
// itemPathParent is in this format: hair.purple
const hairType = itemPathParent.split('.')[1];
return content.appearances.hair[hairType][itemKey];
}
return content.appearances[setType][itemKey];
}
/**
@ -48,6 +52,25 @@ function getSetType (firstPath, req) {
return invalidSet(req);
}
/**
* Return the items and paths for a set given the set,
* a list of items of the type and a prefix for the path.
*/
function getItemsAndPathsForSet (set, itemsCollection, pathPrefix) {
const items = [];
const paths = [];
Object.keys(itemsCollection).forEach(possibleItemKey => {
const possibleItem = itemsCollection[possibleItemKey];
if (possibleItem && possibleItem.set && possibleItem.set.key === set.key) {
items.push(possibleItem);
paths.push(`${pathPrefix}.${possibleItem.key}`);
}
});
return { items, paths };
}
/**
* Return the set of items to unlock given the path of the first item in the set.
*/
@ -78,16 +101,45 @@ function getSet (setType, firstPath, req) {
const { set } = item;
if (!set || set.setPrice === 0) return invalidSet(req);
const items = [];
const paths = [];
// The facialHair set is split between hair.mustache and hair.beards
if (setType === 'hair' && set.key === 'facialHair') {
const items = [];
const paths = [];
Object.keys(content.appearances[setType]).forEach(possibleItemKey => {
const possibleItem = content.appearances[setType][possibleItemKey];
if (possibleItem && possibleItem.set && possibleItem.set.key === set.key) {
items.push(possibleItem);
paths.push(`${setType}.${possibleItem.key}`);
}
});
const { mustache } = content.appearances.hair;
const mustachePrefix = 'hair.mustache';
const {
items: mustacheItems,
paths: mustachePaths,
} = getItemsAndPathsForSet(set, mustache, mustachePrefix);
const { beard } = content.appearances.hair;
const beardPrefix = 'hair.beard';
const {
items: beardItems,
paths: beardPaths,
} = getItemsAndPathsForSet(set, beard, beardPrefix);
items.push(...beardItems, ...mustacheItems);
paths.push(...beardPaths, ...mustachePaths);
return { items, paths, set };
}
let pathPrefix = setType;
let itemsCollection = content.appearances[setType];
if (setType === 'hair') { // hair sets are nested like hair.color.item
const nestedSet = firstPath.split('.')[1];
itemsCollection = itemsCollection[nestedSet];
pathPrefix = `${pathPrefix}.${nestedSet}`;
}
const { items, paths } = getItemsAndPathsForSet(set, itemsCollection, pathPrefix);
return { items, paths, set };
}