mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Separated out buy functions into buyGear, buyArmoire, and buyPotion (#7065)
This commit is contained in:
committed by
Matteo Pagliazzi
parent
f44dbbbd71
commit
9e3d8ba4ac
@@ -113,14 +113,13 @@
|
|||||||
"missingKeyParam": "\"req.params.key\" is required.",
|
"missingKeyParam": "\"req.params.key\" is required.",
|
||||||
"mysterySetNotFound": "Mystery set not found, or set already owned.",
|
"mysterySetNotFound": "Mystery set not found, or set already owned.",
|
||||||
"itemNotFound": "Item \"<%= key %>\" not found.",
|
"itemNotFound": "Item \"<%= key %>\" not found.",
|
||||||
"cannoyBuyItem": "You can't buy this item.",
|
"cannotBuyItem": "You can't buy this item.",
|
||||||
"missingTypeKeyEquip": "\"key\" and \"type\" are required parameters.",
|
"missingTypeKeyEquip": "\"key\" and \"type\" are required parameters.",
|
||||||
"missingPetFoodFeed": "\"pet\" and \"food\" are required parameters.",
|
"missingPetFoodFeed": "\"pet\" and \"food\" are required parameters.",
|
||||||
"invalidPetName": "Invalid pet name supplied.",
|
"invalidPetName": "Invalid pet name supplied.",
|
||||||
"missingEggHatchingPotionHatch": "\"egg\" and \"hatchingPotion\" are required parameters.",
|
"missingEggHatchingPotionHatch": "\"egg\" and \"hatchingPotion\" are required parameters.",
|
||||||
"invalidTypeEquip": "\"type\" must be one of 'equipped', 'pet', 'mount', 'costume'.",
|
"invalidTypeEquip": "\"type\" must be one of 'equipped', 'pet', 'mount', 'costume'.",
|
||||||
"cannotDeleteActiveAccount": "You have an active subscription, cancel your plan before deleting your account.",
|
"cannotDeleteActiveAccount": "You have an active subscription, cancel your plan before deleting your account.",
|
||||||
"cannoyBuyItem": "You can't buy this item",
|
|
||||||
"messageRequired": "A message is required.",
|
"messageRequired": "A message is required.",
|
||||||
"toUserIDRequired": "A toUserId is required",
|
"toUserIDRequired": "A toUserId is required",
|
||||||
"notAuthorizedToSendMessageToThisUser": "Can't send message to this user.",
|
"notAuthorizedToSendMessageToThisUser": "Can't send message to this user.",
|
||||||
@@ -170,5 +169,6 @@
|
|||||||
"pushDeviceAdded": "Push device added successfully",
|
"pushDeviceAdded": "Push device added successfully",
|
||||||
"pushDeviceAlreadyAdded": "The user already has the push device",
|
"pushDeviceAlreadyAdded": "The user already has the push device",
|
||||||
"resetComplete": "Reset completed",
|
"resetComplete": "Reset completed",
|
||||||
"lvl10ChangeClass": "To change class you must be at least level 10."
|
"lvl10ChangeClass": "To change class you must be at least level 10.",
|
||||||
|
"equipmentAlreadyOwned": "You already own that piece of equipment"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ import scoreTask from './ops/scoreTask';
|
|||||||
import sleep from './ops/sleep';
|
import sleep from './ops/sleep';
|
||||||
import allocate from './ops/allocate';
|
import allocate from './ops/allocate';
|
||||||
import buy from './ops/buy';
|
import buy from './ops/buy';
|
||||||
|
import buyGear from './ops/buyGear';
|
||||||
|
import buyPotion from './ops/buyPotion';
|
||||||
|
import buyArmoire from './ops/buyArmoire';
|
||||||
import buyMysterySet from './ops/buyMysterySet';
|
import buyMysterySet from './ops/buyMysterySet';
|
||||||
import buyQuest from './ops/buyQuest';
|
import buyQuest from './ops/buyQuest';
|
||||||
import buySpecialSpell from './ops/buySpecialSpell';
|
import buySpecialSpell from './ops/buySpecialSpell';
|
||||||
@@ -150,6 +153,9 @@ api.ops = {
|
|||||||
sleep,
|
sleep,
|
||||||
allocate,
|
allocate,
|
||||||
buy,
|
buy,
|
||||||
|
buyGear,
|
||||||
|
buyPotion,
|
||||||
|
buyArmoire,
|
||||||
buyMysterySet,
|
buyMysterySet,
|
||||||
buySpecialSpell,
|
buySpecialSpell,
|
||||||
buyQuest,
|
buyQuest,
|
||||||
@@ -266,6 +272,9 @@ api.wrap = function wrapUser (user, main = true) {
|
|||||||
releaseMounts: _.partial(importedOps.releaseMounts, user),
|
releaseMounts: _.partial(importedOps.releaseMounts, user),
|
||||||
releaseBoth: _.partial(importedOps.releaseBoth, user),
|
releaseBoth: _.partial(importedOps.releaseBoth, user),
|
||||||
buy: _.partial(importedOps.buy, user),
|
buy: _.partial(importedOps.buy, user),
|
||||||
|
buyPotion: _.partial(importedOps.buyPotion, user),
|
||||||
|
buyArmoire: _.partial(importedOps.buyArmoire, user),
|
||||||
|
buyGear: _.partial(importedOps.buyGear, user),
|
||||||
buyQuest: _.partial(importedOps.buyQuest, user),
|
buyQuest: _.partial(importedOps.buyQuest, user),
|
||||||
buyMysterySet: _.partial(importedOps.buyMysterySet, user),
|
buyMysterySet: _.partial(importedOps.buyMysterySet, user),
|
||||||
hourglassPurchase: _.partial(importedOps.hourglassPurchase, user),
|
hourglassPurchase: _.partial(importedOps.hourglassPurchase, user),
|
||||||
|
|||||||
@@ -1,142 +1,24 @@
|
|||||||
import content from '../content/index';
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import count from '../count';
|
|
||||||
import splitWhitespace from '../libs/splitWhitespace';
|
|
||||||
import {
|
import {
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotAuthorized,
|
|
||||||
NotFound,
|
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import predictableRandom from '../fns/predictableRandom';
|
import buyPotion from './buyPotion';
|
||||||
import randomVal from '../fns/randomVal';
|
import buyArmoire from './buyArmoire';
|
||||||
import handleTwoHanded from '../fns/handleTwoHanded';
|
import buyGear from './buyGear';
|
||||||
import ultimateGear from '../fns/ultimateGear';
|
|
||||||
|
|
||||||
module.exports = function buy (user, req = {}, analytics) {
|
module.exports = function buy (user, req = {}, analytics) {
|
||||||
let key = _.get(req, 'params.key');
|
let key = _.get(req, 'params.key');
|
||||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
|
|
||||||
let item;
|
let buyRes;
|
||||||
if (key === 'potion') {
|
if (key === 'potion') {
|
||||||
item = content.potion;
|
buyRes = buyPotion(user, req, analytics);
|
||||||
} else if (key === 'armoire') {
|
} else if (key === 'armoire') {
|
||||||
item = content.armoire;
|
buyRes = buyArmoire(user, req, analytics);
|
||||||
} else {
|
} else {
|
||||||
item = content.gear.flat[key];
|
buyRes = buyGear(user, req, analytics);
|
||||||
}
|
|
||||||
if (!item) throw new NotFound(i18n.t('itemNotFound', {key}, req.language));
|
|
||||||
|
|
||||||
if (user.stats.gp < item.value) {
|
|
||||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.canOwn && !item.canOwn(user)) {
|
return buyRes;
|
||||||
throw new NotAuthorized(i18n.t('cannoyBuyItem', req.language));
|
|
||||||
}
|
|
||||||
|
|
||||||
let armoireResp;
|
|
||||||
let armoireResult;
|
|
||||||
let eligibleEquipment;
|
|
||||||
let drop;
|
|
||||||
let message;
|
|
||||||
|
|
||||||
if (item.key === 'potion') {
|
|
||||||
user.stats.hp += 15;
|
|
||||||
if (user.stats.hp > 50) {
|
|
||||||
user.stats.hp = 50;
|
|
||||||
}
|
|
||||||
} else if (item.key === 'armoire') {
|
|
||||||
armoireResult = predictableRandom(user, user.stats.gp);
|
|
||||||
eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
|
||||||
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!_.isEmpty(eligibleEquipment) && (armoireResult < 0.6 || !user.flags.armoireOpened)) {
|
|
||||||
eligibleEquipment.sort();
|
|
||||||
drop = randomVal(user, eligibleEquipment);
|
|
||||||
|
|
||||||
user.items.gear.owned[drop.key] = true;
|
|
||||||
user.flags.armoireOpened = true;
|
|
||||||
message = i18n.t('armoireEquipment', {
|
|
||||||
image: `<span class="shop_${drop.key} pull-left"></span>`,
|
|
||||||
dropText: drop.text(req.language),
|
|
||||||
}, req.language);
|
|
||||||
|
|
||||||
if (count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
|
|
||||||
user.flags.armoireEmpty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
armoireResp = {
|
|
||||||
type: 'gear',
|
|
||||||
dropKey: drop.key,
|
|
||||||
dropText: drop.text(req.language),
|
|
||||||
};
|
|
||||||
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < 0.8) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
|
||||||
drop = randomVal(user, _.where(content.food, {
|
|
||||||
canDrop: true,
|
|
||||||
}));
|
|
||||||
user.items.food[drop.key] = user.items.food[drop.key] || 0;
|
|
||||||
user.items.food[drop.key] += 1;
|
|
||||||
|
|
||||||
message = i18n.t('armoireFood', {
|
|
||||||
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
|
||||||
dropArticle: drop.article,
|
|
||||||
dropText: drop.text(req.language),
|
|
||||||
}, req.language);
|
|
||||||
armoireResp = {
|
|
||||||
type: 'food',
|
|
||||||
dropKey: drop.key,
|
|
||||||
dropArticle: drop.article,
|
|
||||||
dropText: drop.text(req.language),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let armoireExp = Math.floor(predictableRandom(user, user.stats.exp) * 40 + 10);
|
|
||||||
user.stats.exp += armoireExp;
|
|
||||||
message = i18n.t('armoireExp', req.language);
|
|
||||||
armoireResp = {
|
|
||||||
type: 'experience',
|
|
||||||
value: armoireExp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (user.preferences.autoEquip) {
|
|
||||||
user.items.gear.equipped[item.type] = item.key;
|
|
||||||
message = handleTwoHanded(user, item, undefined, req);
|
|
||||||
}
|
|
||||||
user.items.gear.owned[item.key] = true;
|
|
||||||
|
|
||||||
if (item.last) ultimateGear(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.stats.gp -= item.value;
|
|
||||||
|
|
||||||
if (!message) {
|
|
||||||
message = i18n.t('messageBought', {
|
|
||||||
itemText: item.text(req.language),
|
|
||||||
}, req.language);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('acquire item', {
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: key,
|
|
||||||
acquireMethod: 'Gold',
|
|
||||||
goldCost: item.value,
|
|
||||||
category: 'behavior',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = {
|
|
||||||
data: _.pick(user, splitWhitespace('items achievements stats flags')),
|
|
||||||
message,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (armoireResp) res.armoire = armoireResp;
|
|
||||||
|
|
||||||
if (req.v2 === true) {
|
|
||||||
return res.data;
|
|
||||||
} else {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
116
common/script/ops/buyArmoire.js
Normal file
116
common/script/ops/buyArmoire.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import content from '../content/index';
|
||||||
|
import i18n from '../i18n';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import count from '../count';
|
||||||
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
} from '../libs/errors';
|
||||||
|
import predictableRandom from '../fns/predictableRandom';
|
||||||
|
import randomVal from '../fns/randomVal';
|
||||||
|
|
||||||
|
module.exports = function buyArmoire (user, req = {}, analytics) {
|
||||||
|
let item = content.armoire;
|
||||||
|
|
||||||
|
if (user.stats.gp < item.value) {
|
||||||
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.canOwn && !item.canOwn(user)) {
|
||||||
|
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
let armoireResp;
|
||||||
|
let armoireResult;
|
||||||
|
let eligibleEquipment;
|
||||||
|
let drop;
|
||||||
|
let message;
|
||||||
|
|
||||||
|
armoireResult = predictableRandom(user, user.stats.gp);
|
||||||
|
eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
||||||
|
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!_.isEmpty(eligibleEquipment) && (armoireResult < 0.6 || !user.flags.armoireOpened)) {
|
||||||
|
eligibleEquipment.sort();
|
||||||
|
drop = randomVal(user, eligibleEquipment);
|
||||||
|
|
||||||
|
if (user.items.gear.owned[drop.key]) {
|
||||||
|
throw new NotAuthorized(i18n.t('equipmentAlradyOwned', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.items.gear.owned[drop.key] = true;
|
||||||
|
user.flags.armoireOpened = true;
|
||||||
|
message = i18n.t('armoireEquipment', {
|
||||||
|
image: `<span class="shop_${drop.key} pull-left"></span>`,
|
||||||
|
dropText: drop.text(req.language),
|
||||||
|
}, req.language);
|
||||||
|
|
||||||
|
if (count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
|
||||||
|
user.flags.armoireEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
armoireResp = {
|
||||||
|
type: 'gear',
|
||||||
|
dropKey: drop.key,
|
||||||
|
dropText: drop.text(req.language),
|
||||||
|
};
|
||||||
|
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < 0.8) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
||||||
|
drop = randomVal(user, _.where(content.food, {
|
||||||
|
canDrop: true,
|
||||||
|
}));
|
||||||
|
user.items.food[drop.key] = user.items.food[drop.key] || 0;
|
||||||
|
user.items.food[drop.key] += 1;
|
||||||
|
|
||||||
|
message = i18n.t('armoireFood', {
|
||||||
|
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
||||||
|
dropArticle: drop.article,
|
||||||
|
dropText: drop.text(req.language),
|
||||||
|
}, req.language);
|
||||||
|
armoireResp = {
|
||||||
|
type: 'food',
|
||||||
|
dropKey: drop.key,
|
||||||
|
dropArticle: drop.article,
|
||||||
|
dropText: drop.text(req.language),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let armoireExp = Math.floor(predictableRandom(user, user.stats.exp) * 40 + 10);
|
||||||
|
user.stats.exp += armoireExp;
|
||||||
|
message = i18n.t('armoireExp', req.language);
|
||||||
|
armoireResp = {
|
||||||
|
type: 'experience',
|
||||||
|
value: armoireExp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
user.stats.gp -= item.value;
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
message = i18n.t('messageBought', {
|
||||||
|
itemText: item.text(req.language),
|
||||||
|
}, req.language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analytics) {
|
||||||
|
analytics.track('acquire item', {
|
||||||
|
uuid: user._id,
|
||||||
|
itemKey: 'Armoire',
|
||||||
|
acquireMethod: 'Gold',
|
||||||
|
goldCost: item.value,
|
||||||
|
category: 'behavior',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
data: _.pick(user, splitWhitespace('items flags')),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (armoireResp) res.armoire = armoireResp;
|
||||||
|
|
||||||
|
if (req.v2 === true) {
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
72
common/script/ops/buyGear.js
Normal file
72
common/script/ops/buyGear.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import content from '../content/index';
|
||||||
|
import i18n from '../i18n';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
BadRequest,
|
||||||
|
NotAuthorized,
|
||||||
|
NotFound,
|
||||||
|
} from '../libs/errors';
|
||||||
|
import handleTwoHanded from '../fns/handleTwoHanded';
|
||||||
|
import ultimateGear from '../fns/ultimateGear';
|
||||||
|
|
||||||
|
module.exports = function buyGear (user, req = {}, analytics) {
|
||||||
|
let key = _.get(req, 'params.key');
|
||||||
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
|
|
||||||
|
let item = content.gear.flat[key];
|
||||||
|
|
||||||
|
if (!item) throw new NotFound(i18n.t('itemNotFound', {key}, req.language));
|
||||||
|
|
||||||
|
if (user.stats.gp < item.value) {
|
||||||
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.canOwn && !item.canOwn(user)) {
|
||||||
|
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
let message;
|
||||||
|
|
||||||
|
if (user.items.gear.owned[item.key]) {
|
||||||
|
throw new NotAuthorized(i18n.t('equipmentAlreadyOwned', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.preferences.autoEquip) {
|
||||||
|
user.items.gear.equipped[item.type] = item.key;
|
||||||
|
message = handleTwoHanded(user, item, undefined, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.items.gear.owned[item.key] = true;
|
||||||
|
|
||||||
|
if (item.last) ultimateGear(user);
|
||||||
|
|
||||||
|
user.stats.gp -= item.value;
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
message = i18n.t('messageBought', {
|
||||||
|
itemText: item.text(req.language),
|
||||||
|
}, req.language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analytics) {
|
||||||
|
analytics.track('acquire item', {
|
||||||
|
uuid: user._id,
|
||||||
|
itemKey: key,
|
||||||
|
acquireMethod: 'Gold',
|
||||||
|
goldCost: item.value,
|
||||||
|
category: 'behavior',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
data: _.pick(user, splitWhitespace('items achievements stats flags')),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (req.v2 === true) {
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
52
common/script/ops/buyPotion.js
Normal file
52
common/script/ops/buyPotion.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import content from '../content/index';
|
||||||
|
import i18n from '../i18n';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
} from '../libs/errors';
|
||||||
|
|
||||||
|
module.exports = function buyPotion (user, req = {}, analytics) {
|
||||||
|
let item = content.potion;
|
||||||
|
|
||||||
|
if (user.stats.gp < item.value) {
|
||||||
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.canOwn && !item.canOwn(user)) {
|
||||||
|
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.stats.hp += 15;
|
||||||
|
if (user.stats.hp > 50) {
|
||||||
|
user.stats.hp = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.stats.gp -= item.value;
|
||||||
|
|
||||||
|
let message = i18n.t('messageBought', {
|
||||||
|
itemText: item.text(req.language),
|
||||||
|
}, req.language);
|
||||||
|
|
||||||
|
|
||||||
|
if (analytics) {
|
||||||
|
analytics.track('acquire item', {
|
||||||
|
uuid: user._id,
|
||||||
|
itemKey: 'Potion',
|
||||||
|
acquireMethod: 'Gold',
|
||||||
|
goldCost: item.value,
|
||||||
|
category: 'behavior',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
data: _.pick(user, splitWhitespace('stats')),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (req.v2 === true) {
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -30,6 +30,9 @@ import releasePets from './releasePets';
|
|||||||
import releaseMounts from './releaseMounts';
|
import releaseMounts from './releaseMounts';
|
||||||
import releaseBoth from './releaseBoth';
|
import releaseBoth from './releaseBoth';
|
||||||
import buy from './buy';
|
import buy from './buy';
|
||||||
|
import buyGear from './buyGear';
|
||||||
|
import buyPotion from './buyPotion';
|
||||||
|
import buyArmoire from './buyArmoire';
|
||||||
import buyQuest from './buyQuest';
|
import buyQuest from './buyQuest';
|
||||||
import buyMysterySet from './buyMysterySet';
|
import buyMysterySet from './buyMysterySet';
|
||||||
import hourglassPurchase from './hourglassPurchase';
|
import hourglassPurchase from './hourglassPurchase';
|
||||||
@@ -77,6 +80,9 @@ module.exports = {
|
|||||||
releaseMounts,
|
releaseMounts,
|
||||||
releaseBoth,
|
releaseBoth,
|
||||||
buy,
|
buy,
|
||||||
|
buyGear,
|
||||||
|
buyPotion,
|
||||||
|
buyArmoire,
|
||||||
buyQuest,
|
buyQuest,
|
||||||
buyMysterySet,
|
buyMysterySet,
|
||||||
hourglassPurchase,
|
hourglassPurchase,
|
||||||
|
|||||||
@@ -26,17 +26,28 @@ describe('POST /user/buy/:key', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys an item', async () => {
|
it('buys a potion', async () => {
|
||||||
|
await user.update({
|
||||||
|
'stats.gp': 400,
|
||||||
|
});
|
||||||
|
|
||||||
let potion = content.potion;
|
let potion = content.potion;
|
||||||
let res = await user.post('/user/buy/potion');
|
let res = await user.post('/user/buy/potion');
|
||||||
await user.sync();
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.stats.hp).to.equal(50);
|
||||||
expect(res.data).to.eql({
|
expect(res.data).to.eql({
|
||||||
items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
|
|
||||||
achievements: user.achievements,
|
|
||||||
stats: user.stats,
|
stats: user.stats,
|
||||||
flags: JSON.parse(JSON.stringify(user.flags)), // otherwise dates can't be compared
|
|
||||||
});
|
});
|
||||||
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('buys a piece of gear', async () => {
|
||||||
|
let key = 'armor_warrior_1';
|
||||||
|
|
||||||
|
await user.post(`/user/buy/${key}`);
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({ armor_warrior_1: true }); // eslint-disable-line camelcase
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
44
test/api/v3/integration/user/POST-user_buy_armoire.test.js
Normal file
44
test/api/v3/integration/user/POST-user_buy_armoire.test.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
import shared from '../../../../../common/script';
|
||||||
|
|
||||||
|
let content = shared.content;
|
||||||
|
|
||||||
|
describe('POST /user/buy-armoire', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'stats.hp': 40,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('returns an error if user does not have enough gold', async () => {
|
||||||
|
await expect(user.post('/user/buy-potion'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('messageNotEnoughGold'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('buys a piece of armoire', async () => {
|
||||||
|
await user.update({
|
||||||
|
'stats.gp': 400,
|
||||||
|
});
|
||||||
|
|
||||||
|
let potion = content.potion;
|
||||||
|
let res = await user.post('/user/buy-potion');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.stats.hp).to.equal(50);
|
||||||
|
expect(res.data).to.eql({
|
||||||
|
stats: user.stats,
|
||||||
|
});
|
||||||
|
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||||
|
});
|
||||||
|
});
|
||||||
34
test/api/v3/integration/user/POST-user_buy_gear.test.js
Normal file
34
test/api/v3/integration/user/POST-user_buy_gear.test.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
|
describe('POST /user/buy-gear/:key', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'stats.gp': 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('returns an error if the item is not found', async () => {
|
||||||
|
await expect(user.post('/user/buy-gear/notExisting'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('itemNotFound', {key: 'notExisting'}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('buys a piece of gear', async () => {
|
||||||
|
let key = 'armor_warrior_1';
|
||||||
|
|
||||||
|
await user.post(`/user/buy-gear/${key}`);
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({ armor_warrior_1: true }); // eslint-disable-line camelcase
|
||||||
|
});
|
||||||
|
});
|
||||||
44
test/api/v3/integration/user/POST-user_buy_potion.test.js
Normal file
44
test/api/v3/integration/user/POST-user_buy_potion.test.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
import shared from '../../../../../common/script';
|
||||||
|
|
||||||
|
let content = shared.content;
|
||||||
|
|
||||||
|
describe('POST /user/buy-potion', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
'stats.hp': 40,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('returns an error if user does not have enough gold', async () => {
|
||||||
|
await expect(user.post('/user/buy-potion'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('messageNotEnoughGold'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('buys a potion', async () => {
|
||||||
|
await user.update({
|
||||||
|
'stats.gp': 400,
|
||||||
|
});
|
||||||
|
|
||||||
|
let potion = content.potion;
|
||||||
|
let res = await user.post('/user/buy-potion');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.stats.hp).to.equal(50);
|
||||||
|
expect(res.data).to.eql({
|
||||||
|
stats: user.stats,
|
||||||
|
});
|
||||||
|
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
|
||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../helpers/common.helper';
|
} from '../../helpers/common.helper';
|
||||||
import count from '../../../common/script/count';
|
|
||||||
import buy from '../../../common/script/ops/buy';
|
import buy from '../../../common/script/ops/buy';
|
||||||
import shared from '../../../common/script';
|
|
||||||
import content from '../../../common/script/content/index';
|
|
||||||
import {
|
import {
|
||||||
NotAuthorized,
|
BadRequest,
|
||||||
} from '../../../common/script/libs/errors';
|
} from '../../../common/script/libs/errors';
|
||||||
import i18n from '../../../common/script/i18n';
|
import i18n from '../../../common/script/i18n';
|
||||||
|
|
||||||
@@ -30,277 +25,27 @@ describe('shared.ops.buy', () => {
|
|||||||
},
|
},
|
||||||
stats: { gp: 200 },
|
stats: { gp: 200 },
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.stub(shared.fns, 'randomVal');
|
|
||||||
sinon.stub(shared.fns, 'predictableRandom');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
it('returns error when key is not provided', (done) => {
|
||||||
shared.fns.randomVal.restore();
|
try {
|
||||||
shared.fns.predictableRandom.restore();
|
buy(user);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
|
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
||||||
|
done();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Potion', () => {
|
it('recovers 15 hp', () => {
|
||||||
it('recovers 15 hp', () => {
|
user.stats.hp = 30;
|
||||||
user.stats.hp = 30;
|
buy(user, {params: {key: 'potion'}});
|
||||||
buy(user, {params: {key: 'potion'}});
|
expect(user.stats.hp).to.eql(45);
|
||||||
expect(user.stats.hp).to.eql(45);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not increase hp above 50', () => {
|
|
||||||
user.stats.hp = 45;
|
|
||||||
buy(user, {params: {key: 'potion'}});
|
|
||||||
expect(user.stats.hp).to.eql(50);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('deducts 25 gp', () => {
|
|
||||||
user.stats.hp = 45;
|
|
||||||
buy(user, {params: {key: 'potion'}});
|
|
||||||
|
|
||||||
expect(user.stats.gp).to.eql(175);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not purchase if not enough gp', (done) => {
|
|
||||||
user.stats.hp = 45;
|
|
||||||
user.stats.gp = 5;
|
|
||||||
try {
|
|
||||||
buy(user, {params: {key: 'potion'}});
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
|
||||||
expect(user.stats.hp).to.eql(45);
|
|
||||||
expect(user.stats.gp).to.eql(5);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Gear', () => {
|
it('adds equipment to inventory', () => {
|
||||||
it('adds equipment to inventory', () => {
|
user.stats.gp = 31;
|
||||||
user.stats.gp = 31;
|
buy(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true, armor_warrior_1: true });
|
||||||
buy(user, {params: {key: 'armor_warrior_1'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true, armor_warrior_1: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('deducts gold from user', () => {
|
|
||||||
user.stats.gp = 31;
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armor_warrior_1'}});
|
|
||||||
|
|
||||||
expect(user.stats.gp).to.eql(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('auto equips equipment if user has auto-equip preference turned on', () => {
|
|
||||||
user.stats.gp = 31;
|
|
||||||
user.preferences.autoEquip = true;
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armor_warrior_1'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('armor', 'armor_warrior_1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('buys equipment but does not auto-equip', () => {
|
|
||||||
user.stats.gp = 31;
|
|
||||||
user.preferences.autoEquip = false;
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armor_warrior_1'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.equipped.property).to.not.equal('armor_warrior_1');
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO after user.ops.equip is done
|
|
||||||
xit('removes one-handed weapon and shield if auto-equip is on and a two-hander is bought', () => {
|
|
||||||
user.stats.gp = 100;
|
|
||||||
user.preferences.autoEquip = true;
|
|
||||||
buy(user, {params: {key: 'shield_warrior_1'}});
|
|
||||||
user.ops.equip({params: {key: 'shield_warrior_1'}});
|
|
||||||
buy(user, {params: {key: 'weapon_warrior_1'}});
|
|
||||||
user.ops.equip({params: {key: 'weapon_warrior_1'}});
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'weapon_wizard_1'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('shield', 'shield_base_0');
|
|
||||||
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_wizard_1');
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO after user.ops.equip is done
|
|
||||||
xit('buys two-handed equipment but does not automatically remove sword or shield', () => {
|
|
||||||
user.stats.gp = 100;
|
|
||||||
user.preferences.autoEquip = false;
|
|
||||||
buy(user, {params: {key: 'shield_warrior_1'}});
|
|
||||||
user.ops.equip({params: {key: 'shield_warrior_1'}});
|
|
||||||
buy(user, {params: {key: 'weapon_warrior_1'}});
|
|
||||||
user.ops.equip({params: {key: 'weapon_warrior_1'}});
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'weapon_wizard_1'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('shield', 'shield_warrior_1');
|
|
||||||
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_warrior_1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not buy equipment without enough Gold', (done) => {
|
|
||||||
user.stats.gp = 20;
|
|
||||||
|
|
||||||
try {
|
|
||||||
buy(user, {params: {key: 'armor_warrior_1'}});
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
|
||||||
expect(user.items.gear.owned).to.not.have.property('armor_warrior_1');
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Enchanted Armoire', () => {
|
|
||||||
let YIELD_EQUIPMENT = 0.5;
|
|
||||||
let YIELD_FOOD = 0.7;
|
|
||||||
let YIELD_EXP = 0.9;
|
|
||||||
|
|
||||||
let fullArmoire = {};
|
|
||||||
|
|
||||||
_(content.gearTypes).each((type) => {
|
|
||||||
_(content.gear.tree[type].armoire).each((gearObject) => {
|
|
||||||
let armoireKey = gearObject.key;
|
|
||||||
|
|
||||||
fullArmoire[armoireKey] = true;
|
|
||||||
}).value();
|
|
||||||
}).value();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
user.achievements.ultimateGearSets = { rogue: true };
|
|
||||||
user.flags.armoireOpened = true;
|
|
||||||
user.stats.exp = 0;
|
|
||||||
user.items.food = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
context('failure conditions', () => {
|
|
||||||
it('does not open if user does not have enough gold', (done) => {
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
|
||||||
user.stats.gp = 50;
|
|
||||||
|
|
||||||
try {
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
|
||||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
|
||||||
expect(user.items.food).to.be.empty;
|
|
||||||
expect(user.stats.exp).to.eql(0);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not open without Ultimate Gear achievement', (done) => {
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
|
||||||
user.achievements.ultimateGearSets = {healer: false, wizard: false, rogue: false, warrior: false};
|
|
||||||
|
|
||||||
try {
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
} catch (err) {
|
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
|
||||||
expect(err.message).to.equal(i18n.t('cannoyBuyItem'));
|
|
||||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
|
||||||
expect(user.items.food).to.be.empty;
|
|
||||||
expect(user.stats.exp).to.eql(0);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('non-gear awards', () => {
|
|
||||||
// Skipped because can't stub predictableRandom correctly
|
|
||||||
xit('gives Experience', () => {
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EXP);
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
|
||||||
expect(user.items.food).to.be.empty;
|
|
||||||
expect(user.stats.exp).to.eql(46);
|
|
||||||
expect(user.stats.gp).to.eql(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Skipped because can't stub predictableRandom correctly
|
|
||||||
xit('gives food', () => {
|
|
||||||
let honey = content.food.Honey;
|
|
||||||
|
|
||||||
shared.fns.randomVal.returns(honey);
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_FOOD);
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
|
||||||
expect(user.items.food).to.eql({Honey: 1});
|
|
||||||
expect(user.stats.exp).to.eql(0);
|
|
||||||
expect(user.stats.gp).to.eql(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Skipped because can't stub predictableRandom correctly
|
|
||||||
xit('does not give equipment if all equipment has been found', () => {
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
|
||||||
user.items.gear.owned = fullArmoire;
|
|
||||||
user.stats.gp = 150;
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql(fullArmoire);
|
|
||||||
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
|
||||||
|
|
||||||
expect(armoireCount).to.eql(0);
|
|
||||||
|
|
||||||
expect(user.stats.exp).to.eql(30);
|
|
||||||
expect(user.stats.gp).to.eql(50);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('gear awards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
let shield = content.gear.tree.shield.armoire.gladiatorShield;
|
|
||||||
|
|
||||||
shared.fns.randomVal.returns(shield);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Skipped because can't stub predictableRandom correctly
|
|
||||||
xit('always drops equipment the first time', () => {
|
|
||||||
delete user.flags.armoireOpened;
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EXP);
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
|
||||||
weapon_warrior_0: true,
|
|
||||||
shield_armoire_gladiatorShield: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
|
||||||
|
|
||||||
expect(armoireCount).to.eql(_.size(fullArmoire) - 1);
|
|
||||||
expect(user.items.food).to.be.empty;
|
|
||||||
expect(user.stats.exp).to.eql(0);
|
|
||||||
expect(user.stats.gp).to.eql(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Skipped because can't stub predictableRandom correctly
|
|
||||||
xit('gives more equipment', () => {
|
|
||||||
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
|
||||||
user.items.gear.owned = {
|
|
||||||
weapon_warrior_0: true,
|
|
||||||
head_armoire_hornedIronHelm: true,
|
|
||||||
};
|
|
||||||
user.stats.gp = 200;
|
|
||||||
|
|
||||||
buy(user, {params: {key: 'armoire'}});
|
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true, shield_armoire_gladiatorShield: true, head_armoire_hornedIronHelm: true});
|
|
||||||
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
|
||||||
|
|
||||||
expect(armoireCount).to.eql(_.size(fullArmoire) - 2);
|
|
||||||
expect(user.stats.gp).to.eql(100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
187
test/common/ops/buyArmoire.js
Normal file
187
test/common/ops/buyArmoire.js
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
|
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../helpers/common.helper';
|
||||||
|
import count from '../../../common/script/count';
|
||||||
|
import buyArmoire from '../../../common/script/ops/buyArmoire';
|
||||||
|
import shared from '../../../common/script';
|
||||||
|
import content from '../../../common/script/content/index';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
} from '../../../common/script/libs/errors';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
|
|
||||||
|
describe('shared.ops.buyArmoire', () => {
|
||||||
|
let user;
|
||||||
|
let YIELD_EQUIPMENT = 0.5;
|
||||||
|
let YIELD_FOOD = 0.7;
|
||||||
|
let YIELD_EXP = 0.9;
|
||||||
|
|
||||||
|
let fullArmoire = {};
|
||||||
|
|
||||||
|
_(content.gearTypes).each((type) => {
|
||||||
|
_(content.gear.tree[type].armoire).each((gearObject) => {
|
||||||
|
let armoireKey = gearObject.key;
|
||||||
|
|
||||||
|
fullArmoire[armoireKey] = true;
|
||||||
|
}).value();
|
||||||
|
}).value();
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = generateUser({
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
owned: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
equipped: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stats: { gp: 200 },
|
||||||
|
});
|
||||||
|
|
||||||
|
user.achievements.ultimateGearSets = { rogue: true };
|
||||||
|
user.flags.armoireOpened = true;
|
||||||
|
user.stats.exp = 0;
|
||||||
|
user.items.food = {};
|
||||||
|
|
||||||
|
sinon.stub(shared.fns, 'randomVal');
|
||||||
|
sinon.stub(shared.fns, 'predictableRandom');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
shared.fns.randomVal.restore();
|
||||||
|
shared.fns.predictableRandom.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('failure conditions', () => {
|
||||||
|
it('does not open if user does not have enough gold', (done) => {
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
||||||
|
user.stats.gp = 50;
|
||||||
|
|
||||||
|
try {
|
||||||
|
buyArmoire(user);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
|
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
||||||
|
expect(user.items.food).to.be.empty;
|
||||||
|
expect(user.stats.exp).to.eql(0);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not open without Ultimate Gear achievement', (done) => {
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
||||||
|
user.achievements.ultimateGearSets = {healer: false, wizard: false, rogue: false, warrior: false};
|
||||||
|
|
||||||
|
try {
|
||||||
|
buyArmoire(user);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('cannotBuyItem'));
|
||||||
|
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
||||||
|
expect(user.items.food).to.be.empty;
|
||||||
|
expect(user.stats.exp).to.eql(0);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('non-gear awards', () => {
|
||||||
|
// Skipped because can't stub predictableRandom correctly
|
||||||
|
xit('gives Experience', () => {
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EXP);
|
||||||
|
|
||||||
|
buyArmoire(user);
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
||||||
|
expect(user.items.food).to.be.empty;
|
||||||
|
expect(user.stats.exp).to.eql(46);
|
||||||
|
expect(user.stats.gp).to.eql(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipped because can't stub predictableRandom correctly
|
||||||
|
xit('gives food', () => {
|
||||||
|
let honey = content.food.Honey;
|
||||||
|
|
||||||
|
shared.fns.randomVal.returns(honey);
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_FOOD);
|
||||||
|
|
||||||
|
buyArmoire(user);
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true});
|
||||||
|
expect(user.items.food).to.eql({Honey: 1});
|
||||||
|
expect(user.stats.exp).to.eql(0);
|
||||||
|
expect(user.stats.gp).to.eql(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipped because can't stub predictableRandom correctly
|
||||||
|
xit('does not give equipment if all equipment has been found', () => {
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
||||||
|
user.items.gear.owned = fullArmoire;
|
||||||
|
user.stats.gp = 150;
|
||||||
|
|
||||||
|
buyArmoire(user);
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql(fullArmoire);
|
||||||
|
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||||
|
|
||||||
|
expect(armoireCount).to.eql(0);
|
||||||
|
|
||||||
|
expect(user.stats.exp).to.eql(30);
|
||||||
|
expect(user.stats.gp).to.eql(50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('gear awards', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
let shield = content.gear.tree.shield.armoire.gladiatorShield;
|
||||||
|
|
||||||
|
shared.fns.randomVal.returns(shield);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipped because can't stub predictableRandom correctly
|
||||||
|
xit('always drops equipment the first time', () => {
|
||||||
|
delete user.flags.armoireOpened;
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EXP);
|
||||||
|
|
||||||
|
buyArmoire(user);
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
shield_armoire_gladiatorShield: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||||
|
|
||||||
|
expect(armoireCount).to.eql(_.size(fullArmoire) - 1);
|
||||||
|
expect(user.items.food).to.be.empty;
|
||||||
|
expect(user.stats.exp).to.eql(0);
|
||||||
|
expect(user.stats.gp).to.eql(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipped because can't stub predictableRandom correctly
|
||||||
|
xit('gives more equipment', () => {
|
||||||
|
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
||||||
|
user.items.gear.owned = {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
head_armoire_hornedIronHelm: true,
|
||||||
|
};
|
||||||
|
user.stats.gp = 200;
|
||||||
|
|
||||||
|
buyArmoire(user);
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true, shield_armoire_gladiatorShield: true, head_armoire_hornedIronHelm: true});
|
||||||
|
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||||
|
|
||||||
|
expect(armoireCount).to.eql(_.size(fullArmoire) - 2);
|
||||||
|
expect(user.stats.gp).to.eql(100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
132
test/common/ops/buyGear.js
Normal file
132
test/common/ops/buyGear.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
|
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../helpers/common.helper';
|
||||||
|
import buyGear from '../../../common/script/ops/buyGear';
|
||||||
|
import shared from '../../../common/script';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
} from '../../../common/script/libs/errors';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
|
|
||||||
|
describe('shared.ops.buyGear', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = generateUser({
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
owned: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
equipped: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stats: { gp: 200 },
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.stub(shared.fns, 'randomVal');
|
||||||
|
sinon.stub(shared.fns, 'predictableRandom');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
shared.fns.randomVal.restore();
|
||||||
|
shared.fns.predictableRandom.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Gear', () => {
|
||||||
|
it('adds equipment to inventory', () => {
|
||||||
|
user.stats.gp = 31;
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true, armor_warrior_1: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deducts gold from user', () => {
|
||||||
|
user.stats.gp = 31;
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
|
||||||
|
expect(user.stats.gp).to.eql(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('auto equips equipment if user has auto-equip preference turned on', () => {
|
||||||
|
user.stats.gp = 31;
|
||||||
|
user.preferences.autoEquip = true;
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.equipped).to.have.property('armor', 'armor_warrior_1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('buyGears equipment but does not auto-equip', () => {
|
||||||
|
user.stats.gp = 31;
|
||||||
|
user.preferences.autoEquip = false;
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.equipped.property).to.not.equal('armor_warrior_1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not buyGear equipment twice', (done) => {
|
||||||
|
user.stats.gp = 62;
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
|
||||||
|
try {
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('equipmentAlreadyOwned'));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO after user.ops.equip is done
|
||||||
|
xit('removes one-handed weapon and shield if auto-equip is on and a two-hander is bought', () => {
|
||||||
|
user.stats.gp = 100;
|
||||||
|
user.preferences.autoEquip = true;
|
||||||
|
buyGear(user, {params: {key: 'shield_warrior_1'}});
|
||||||
|
user.ops.equip({params: {key: 'shield_warrior_1'}});
|
||||||
|
buyGear(user, {params: {key: 'weapon_warrior_1'}});
|
||||||
|
user.ops.equip({params: {key: 'weapon_warrior_1'}});
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'weapon_wizard_1'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.equipped).to.have.property('shield', 'shield_base_0');
|
||||||
|
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_wizard_1');
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO after user.ops.equip is done
|
||||||
|
xit('buyGears two-handed equipment but does not automatically remove sword or shield', () => {
|
||||||
|
user.stats.gp = 100;
|
||||||
|
user.preferences.autoEquip = false;
|
||||||
|
buyGear(user, {params: {key: 'shield_warrior_1'}});
|
||||||
|
user.ops.equip({params: {key: 'shield_warrior_1'}});
|
||||||
|
buyGear(user, {params: {key: 'weapon_warrior_1'}});
|
||||||
|
user.ops.equip({params: {key: 'weapon_warrior_1'}});
|
||||||
|
|
||||||
|
buyGear(user, {params: {key: 'weapon_wizard_1'}});
|
||||||
|
|
||||||
|
expect(user.items.gear.equipped).to.have.property('shield', 'shield_warrior_1');
|
||||||
|
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_warrior_1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not buyGear equipment without enough Gold', (done) => {
|
||||||
|
user.stats.gp = 20;
|
||||||
|
|
||||||
|
try {
|
||||||
|
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
|
expect(user.items.gear.owned).to.not.have.property('armor_warrior_1');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
65
test/common/ops/buyPotion.js
Normal file
65
test/common/ops/buyPotion.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../helpers/common.helper';
|
||||||
|
import buyPotion from '../../../common/script/ops/buyPotion';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
} from '../../../common/script/libs/errors';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
|
|
||||||
|
describe('shared.ops.buyPotion', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = generateUser({
|
||||||
|
items: {
|
||||||
|
gear: {
|
||||||
|
owned: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
equipped: {
|
||||||
|
weapon_warrior_0: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stats: { gp: 200 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Potion', () => {
|
||||||
|
it('recovers 15 hp', () => {
|
||||||
|
user.stats.hp = 30;
|
||||||
|
buyPotion(user);
|
||||||
|
expect(user.stats.hp).to.eql(45);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not increase hp above 50', () => {
|
||||||
|
user.stats.hp = 45;
|
||||||
|
buyPotion(user);
|
||||||
|
expect(user.stats.hp).to.eql(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deducts 25 gp', () => {
|
||||||
|
user.stats.hp = 45;
|
||||||
|
buyPotion(user);
|
||||||
|
|
||||||
|
expect(user.stats.gp).to.eql(175);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not purchase if not enough gp', (done) => {
|
||||||
|
user.stats.hp = 45;
|
||||||
|
user.stats.gp = 5;
|
||||||
|
try {
|
||||||
|
buyPotion(user);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
|
expect(user.stats.hp).to.eql(45);
|
||||||
|
expect(user.stats.gp).to.eql(5);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -477,15 +477,14 @@ api.allocateNow = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /api/v3/user/buy/:key Buy a content item.
|
* @api {post} /user/buy/:key Buy gear, armoire or potion
|
||||||
* @apiVersion 3.0.0
|
* @apiVersion 3.0.0
|
||||||
* @apiName UserBuy
|
* @apiName UserBuy
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
*
|
*
|
||||||
* @apiParam {string} key The item to buy.
|
* @apiParam {string} key The item to buy.
|
||||||
*
|
*
|
||||||
* @apiSuccess {Object} data `items, achievements, stats, flags`
|
* @apiSuccess {Object} data `items`
|
||||||
* @apiSuccess {object} armoireResp Optional extra item given by the armoire
|
|
||||||
* @apiSuccess {string} message
|
* @apiSuccess {string} message
|
||||||
*/
|
*/
|
||||||
api.buy = {
|
api.buy = {
|
||||||
@@ -501,7 +500,77 @@ api.buy = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /api/v3/user/buy-mystery-set/:key Buy a mystery set.
|
* @api {post} /user/buy-gear/:key Buy a piece of gear.
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName UserBuyGear
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
* @apiParam {string} key The item to buy.
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data `items`
|
||||||
|
* @apiSuccess {string} message
|
||||||
|
*/
|
||||||
|
api.buyGear = {
|
||||||
|
method: 'POST',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
url: '/user/buy-gear/:key',
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
let buyRes = common.ops.buyGear(user, req, res.analytics);
|
||||||
|
await user.save();
|
||||||
|
res.respond(200, buyRes);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} /user/buy-armoire Buy an armoire item.
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName UserBuyArmoire
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
* @apiParam {string} key The item to buy.
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data `items flags`
|
||||||
|
* @apiSuccess {object} armoireResp Optional extra item given by the armoire
|
||||||
|
* @apiSuccess {string} message
|
||||||
|
*/
|
||||||
|
api.buyArmoire = {
|
||||||
|
method: 'POST',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
url: '/user/buy-armoire',
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
let buyArmoireResponse = common.ops.buyArmoire(user, req, res.analytics);
|
||||||
|
await user.save();
|
||||||
|
res.respond(200, buyArmoireResponse);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} /user/buy-potion Buy a potion.
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName UserBuyPotion
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
* @apiParam {string} key The item to buy.
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data `stats`
|
||||||
|
* @apiSuccess {string} message
|
||||||
|
*/
|
||||||
|
api.buyPotion = {
|
||||||
|
method: 'POST',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
url: '/user/buy-potion',
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
let buyPotionResponse = common.ops.buyPotion(user, req, res.analytics);
|
||||||
|
await user.save();
|
||||||
|
res.respond(200, buyPotionResponse);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} /user/buy-mystery-set/:key Buy a mystery set.
|
||||||
* @apiVersion 3.0.0
|
* @apiVersion 3.0.0
|
||||||
* @apiName UserBuyMysterySet
|
* @apiName UserBuyMysterySet
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
|
|||||||
Reference in New Issue
Block a user