mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-07-13 15:38:59 +00:00
fix(api): Armoire actually works randomly
This commit is contained in:
parent
4d5b6992be
commit
4df1601718
2 changed files with 55 additions and 88 deletions
|
|
@ -1,24 +1,17 @@
|
|||
/* eslint-disable camelcase */
|
||||
|
||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
import count from '../../../website/common/script/count';
|
||||
import buyArmoire from '../../../website/common/script/ops/buyArmoire';
|
||||
import shared from '../../../website/common/script';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyArmoire', () => {
|
||||
let user;
|
||||
let YIELD_EQUIPMENT = 0.5;
|
||||
let YIELD_FOOD = 0.7;
|
||||
let YIELD_EXP = 0.9;
|
||||
|
||||
function getFullArmoire () {
|
||||
let fullArmoire = {};
|
||||
|
||||
_(content.gearTypes).each((type) => {
|
||||
|
|
@ -29,39 +22,36 @@ describe('shared.ops.buyArmoire', () => {
|
|||
}).value();
|
||||
}).value();
|
||||
|
||||
return fullArmoire;
|
||||
}
|
||||
|
||||
describe('shared.ops.buyArmoire', () => {
|
||||
let user;
|
||||
let YIELD_EQUIPMENT = 0.5;
|
||||
let YIELD_FOOD = 0.7;
|
||||
let YIELD_EXP = 0.9;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
items: {
|
||||
gear: {
|
||||
owned: {
|
||||
weapon_warrior_0: true,
|
||||
},
|
||||
equipped: {
|
||||
weapon_warrior_0: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
stats: { gp: 200 },
|
||||
});
|
||||
|
||||
user.items.gear.owned = {
|
||||
weapon_warrior_0: true,
|
||||
};
|
||||
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');
|
||||
sandbox.stub(Math, 'random');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.fns.randomVal.restore();
|
||||
shared.fns.predictableRandom.restore();
|
||||
Math.random.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 {
|
||||
|
|
@ -71,13 +61,6 @@ describe('shared.ops.buyArmoire', () => {
|
|||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
weapon_warrior_0: true,
|
||||
eyewear_special_blackTopFrame: true,
|
||||
eyewear_special_blueTopFrame: true,
|
||||
eyewear_special_greenTopFrame: true,
|
||||
eyewear_special_pinkTopFrame: true,
|
||||
eyewear_special_redTopFrame: true,
|
||||
eyewear_special_whiteTopFrame: true,
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
});
|
||||
expect(user.items.food).to.be.empty;
|
||||
expect(user.stats.exp).to.eql(0);
|
||||
|
|
@ -86,7 +69,6 @@ describe('shared.ops.buyArmoire', () => {
|
|||
});
|
||||
|
||||
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 {
|
||||
|
|
@ -96,13 +78,6 @@ describe('shared.ops.buyArmoire', () => {
|
|||
expect(err.message).to.equal(i18n.t('cannotBuyItem'));
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
weapon_warrior_0: true,
|
||||
eyewear_special_blackTopFrame: true,
|
||||
eyewear_special_blueTopFrame: true,
|
||||
eyewear_special_greenTopFrame: true,
|
||||
eyewear_special_pinkTopFrame: true,
|
||||
eyewear_special_redTopFrame: true,
|
||||
eyewear_special_whiteTopFrame: true,
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
});
|
||||
expect(user.items.food).to.be.empty;
|
||||
expect(user.stats.exp).to.eql(0);
|
||||
|
|
@ -112,93 +87,83 @@ describe('shared.ops.buyArmoire', () => {
|
|||
});
|
||||
|
||||
context('non-gear awards', () => {
|
||||
// Skipped because can't stub predictableRandom correctly
|
||||
xit('gives Experience', () => {
|
||||
shared.fns.predictableRandom.returns(YIELD_EXP);
|
||||
it('gives Experience', () => {
|
||||
let previousExp = user.stats.exp;
|
||||
Math.random.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);
|
||||
expect(user.stats.exp).to.be.greaterThan(previousExp);
|
||||
expect(user.stats.gp).to.equal(100);
|
||||
});
|
||||
|
||||
// Skipped because can't stub predictableRandom correctly
|
||||
xit('gives food', () => {
|
||||
let honey = content.food.Honey;
|
||||
it('gives food', () => {
|
||||
let previousExp = user.stats.exp;
|
||||
|
||||
shared.fns.randomVal.returns(honey);
|
||||
shared.fns.predictableRandom.returns(YIELD_FOOD);
|
||||
Math.random.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);
|
||||
expect(user.items.food).to.not.be.empty;
|
||||
expect(user.stats.exp).to.equal(previousExp);
|
||||
expect(user.stats.gp).to.equal(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;
|
||||
it('does not give equipment if all equipment has been found', () => {
|
||||
Math.random.returns(YIELD_EQUIPMENT);
|
||||
user.items.gear.owned = getFullArmoire();
|
||||
user.stats.gp = 150;
|
||||
|
||||
buyArmoire(user);
|
||||
|
||||
expect(user.items.gear.owned).to.eql(fullArmoire);
|
||||
expect(user.items.gear.owned).to.eql(getFullArmoire());
|
||||
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);
|
||||
expect(user.stats.gp).to.equal(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', () => {
|
||||
it('always drops equipment the first time', () => {
|
||||
delete user.flags.armoireOpened;
|
||||
shared.fns.predictableRandom.returns(YIELD_EXP);
|
||||
Math.random.returns(YIELD_EXP);
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(1);
|
||||
|
||||
buyArmoire(user);
|
||||
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
weapon_warrior_0: true,
|
||||
shield_armoire_gladiatorShield: true,
|
||||
});
|
||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||
|
||||
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||
|
||||
expect(armoireCount).to.eql(_.size(fullArmoire) - 1);
|
||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 1);
|
||||
expect(user.items.food).to.be.empty;
|
||||
expect(user.stats.exp).to.eql(0);
|
||||
expect(user.stats.gp).to.eql(100);
|
||||
expect(user.stats.exp).to.equal(0);
|
||||
expect(user.stats.gp).to.equal(100);
|
||||
});
|
||||
|
||||
// Skipped because can't stub predictableRandom correctly
|
||||
xit('gives more equipment', () => {
|
||||
shared.fns.predictableRandom.returns(YIELD_EQUIPMENT);
|
||||
it('gives more equipment', () => {
|
||||
Math.random.returns(YIELD_EQUIPMENT);
|
||||
user.items.gear.owned = {
|
||||
weapon_warrior_0: true,
|
||||
head_armoire_hornedIronHelm: true,
|
||||
};
|
||||
user.stats.gp = 200;
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||
|
||||
buyArmoire(user);
|
||||
|
||||
expect(user.items.gear.owned).to.eql({weapon_warrior_0: true, shield_armoire_gladiatorShield: true, head_armoire_hornedIronHelm: true});
|
||||
expect(_.size(user.items.gear.owned)).to.equal(3);
|
||||
|
||||
let armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||
|
||||
expect(armoireCount).to.eql(_.size(fullArmoire) - 2);
|
||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
||||
expect(user.stats.gp).to.eql(100);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ import splitWhitespace from '../libs/splitWhitespace';
|
|||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import predictableRandom from '../fns/predictableRandom';
|
||||
import randomVal from '../fns/randomVal';
|
||||
|
||||
const YIELD_EQUIPMENT_THRESHOLD = 0.6;
|
||||
const YIELD_FOOD_THRESHOLD = 0.8;
|
||||
|
||||
module.exports = function buyArmoire (user, req = {}, analytics) {
|
||||
let item = content.armoire;
|
||||
|
||||
|
|
@ -21,17 +23,16 @@ module.exports = function buyArmoire (user, req = {}, analytics) {
|
|||
}
|
||||
|
||||
let armoireResp;
|
||||
let armoireResult;
|
||||
let eligibleEquipment;
|
||||
let drop;
|
||||
let message;
|
||||
|
||||
armoireResult = predictableRandom(user, user.stats.gp);
|
||||
eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
||||
let armoireResult = Math.random();
|
||||
let eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
||||
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
|
||||
});
|
||||
let armoireHasEquipment = !_.isEmpty(eligibleEquipment);
|
||||
|
||||
if (!_.isEmpty(eligibleEquipment) && (armoireResult < 0.6 || !user.flags.armoireOpened)) {
|
||||
if (armoireHasEquipment && (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)) {
|
||||
eligibleEquipment.sort();
|
||||
drop = randomVal(user, eligibleEquipment);
|
||||
|
||||
|
|
@ -55,10 +56,11 @@ module.exports = function buyArmoire (user, req = {}, analytics) {
|
|||
dropKey: drop.key,
|
||||
dropText: drop.text(req.language),
|
||||
};
|
||||
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < 0.8) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
||||
} else if ((armoireHasEquipment && armoireResult < YIELD_FOOD_THRESHOLD) || 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;
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ module.exports = function buyArmoire (user, req = {}, analytics) {
|
|||
dropText: drop.text(req.language),
|
||||
};
|
||||
} else {
|
||||
let armoireExp = Math.floor(predictableRandom(user, user.stats.exp) * 40 + 10);
|
||||
let armoireExp = Math.floor(Math.random() * 40 + 10);
|
||||
user.stats.exp += armoireExp;
|
||||
message = i18n.t('armoireExp', req.language);
|
||||
armoireResp = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue