mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 21:57:22 +01:00
168 lines
4.3 KiB
JavaScript
168 lines
4.3 KiB
JavaScript
import filter from 'lodash/filter';
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import pick from 'lodash/pick';
|
|
import content from '../../content/index';
|
|
import splitWhitespace from '../../libs/splitWhitespace';
|
|
import {
|
|
NotAuthorized,
|
|
} from '../../libs/errors';
|
|
import randomVal, * as randomValFns from '../../libs/randomVal';
|
|
import { removeItemByPath } from '../pinnedGearUtils';
|
|
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
|
import updateStats from '../../fns/updateStats';
|
|
|
|
// TODO this is only used on the server
|
|
// move out of common?
|
|
|
|
const YIELD_EQUIPMENT_THRESHOLD = 0.6;
|
|
const YIELD_FOOD_THRESHOLD = 0.8;
|
|
|
|
export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
|
multiplePurchaseAllowed () { // eslint-disable-line class-methods-use-this
|
|
return false;
|
|
}
|
|
|
|
extractAndValidateParams (user) {
|
|
const item = content.armoire;
|
|
|
|
this.canUserPurchase(user, item);
|
|
}
|
|
|
|
executeChanges (user, item) {
|
|
let result = {};
|
|
|
|
const armoireResult = randomValFns.trueRandom();
|
|
const eligibleEquipment = filter(content.gear.flat, eligible => eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key]);
|
|
const armoireHasEquipment = !isEmpty(eligibleEquipment);
|
|
|
|
if (
|
|
armoireHasEquipment
|
|
&& (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)
|
|
) {
|
|
result = this._gearResult(user, eligibleEquipment);
|
|
} else if (
|
|
(armoireHasEquipment && armoireResult < YIELD_FOOD_THRESHOLD)
|
|
|| armoireResult < 0.5
|
|
) {
|
|
result = this._foodResult(user);
|
|
} else {
|
|
result = this._experienceResult(user);
|
|
}
|
|
|
|
this.subtractCurrency(user, item);
|
|
|
|
let { message } = result;
|
|
const { armoireResp } = result;
|
|
|
|
if (!message) {
|
|
message = this.i18n('messageBought', {
|
|
itemText: this.item.text(this.req.language),
|
|
});
|
|
}
|
|
|
|
const resData = pick(user, splitWhitespace('items flags'));
|
|
if (armoireResp) resData.armoire = armoireResp;
|
|
|
|
return [
|
|
resData,
|
|
message,
|
|
];
|
|
}
|
|
|
|
_trackDropAnalytics (user, key) {
|
|
this.analytics.track(
|
|
'Enchanted Armoire',
|
|
{
|
|
user,
|
|
uuid: user._id,
|
|
itemKey: key,
|
|
category: 'behavior',
|
|
headers: this.req.headers,
|
|
},
|
|
);
|
|
}
|
|
|
|
_gearResult (user, eligibleEquipment) {
|
|
const emptied = eligibleEquipment.length === 1;
|
|
eligibleEquipment.sort();
|
|
const drop = randomVal(eligibleEquipment);
|
|
|
|
if (user.items.gear.owned[drop.key]) {
|
|
throw new NotAuthorized(this.i18n('equipmentAlreadyOwned'));
|
|
}
|
|
|
|
user.items.gear.owned = {
|
|
...user.items.gear.owned,
|
|
[drop.key]: true,
|
|
};
|
|
if (user.markModified) user.markModified('items.gear.owned');
|
|
|
|
user.flags.armoireOpened = true;
|
|
const message = this.i18n('armoireEquipment', {
|
|
image: `<span class="shop_${drop.key} pull-left"></span>`,
|
|
dropText: drop.text(this.req.language),
|
|
});
|
|
|
|
removeItemByPath(user, `gear.flat.${drop.key}`);
|
|
|
|
if (this.analytics) {
|
|
this._trackDropAnalytics(user, drop.key);
|
|
}
|
|
|
|
const armoireResp = {
|
|
type: 'gear',
|
|
dropKey: drop.key,
|
|
dropText: drop.text(this.req.language),
|
|
emptied,
|
|
};
|
|
|
|
return {
|
|
message,
|
|
armoireResp,
|
|
};
|
|
}
|
|
|
|
_foodResult (user) {
|
|
const drop = randomVal(filter(content.food, {
|
|
canDrop: true,
|
|
}));
|
|
|
|
user.items.food = {
|
|
...user.items.food,
|
|
[drop.key]: user.items.food[drop.key] || 0,
|
|
};
|
|
user.items.food[drop.key] += 1;
|
|
if (user.markModified) user.markModified('items.food');
|
|
|
|
if (this.analytics) {
|
|
this._trackDropAnalytics(user, drop.key);
|
|
}
|
|
return {
|
|
message: this.i18n('armoireFood', {
|
|
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
|
dropText: drop.text(this.req.language),
|
|
}),
|
|
armoireResp: {
|
|
type: 'food',
|
|
dropKey: drop.key,
|
|
dropText: drop.textA(this.req.language),
|
|
},
|
|
};
|
|
}
|
|
|
|
_experienceResult (user) {
|
|
const armoireExp = Math.floor(randomValFns.trueRandom() * 40 + 10);
|
|
user.stats.exp += armoireExp;
|
|
updateStats(user, user.stats, this.req);
|
|
|
|
return {
|
|
message: this.i18n('armoireExp'),
|
|
armoireResp: {
|
|
type: 'experience',
|
|
value:
|
|
armoireExp,
|
|
},
|
|
};
|
|
}
|
|
}
|