Merge branch 'develop' into release

This commit is contained in:
SabreCat
2018-04-05 21:04:11 +00:00
71 changed files with 2244 additions and 709 deletions
@@ -1,3 +1,6 @@
// Magic Hatching Potions are configured like this:
// type: 'premiumHatchingPotion', // note no "s" at the end
// path: 'premiumHatchingPotions.Rainbow',
const featuredItems = {
market: [
{
@@ -5,12 +8,12 @@ const featuredItems = {
path: 'armoire',
},
{
type: 'hatchingPotions',
path: 'hatchingPotions.Shimmer',
type: 'premiumHatchingPotion',
path: 'premiumHatchingPotions.Shimmer',
},
{
type: 'hatchingPotions',
path: 'hatchingPotions.Rainbow',
type: 'premiumHatchingPotion',
path: 'premiumHatchingPotions.Rainbow',
},
{
type: 'card',
+1 -1
View File
@@ -62,7 +62,7 @@ spells.wizard = {
cast (user, target) {
each(target, (member) => {
let bonus = statsComputed(user).int;
if (user._id !== member._id) {
if (user._id !== member._id && member.stats.class !== 'wizard') {
member.stats.mp += Math.ceil(diminishingReturns(bonus, 25, 125));
}
});
+12 -6
View File
@@ -3,8 +3,8 @@ import get from 'lodash/get';
import {
BadRequest,
} from '../../libs/errors';
import buyHealthPotion from './buyHealthPotion';
import buyArmoire from './buyArmoire';
import {BuyArmoireOperation} from './buyArmoire';
import {BuyHealthPotionOperation} from './buyHealthPotion';
import {BuyMarketGearOperation} from './buyMarketGear';
import buyMysterySet from './buyMysterySet';
import buyQuest from './buyQuest';
@@ -30,15 +30,21 @@ module.exports = function buy (user, req = {}, analytics) {
let buyRes;
switch (type) {
case 'armoire':
buyRes = buyArmoire(user, req, analytics);
case 'armoire': {
const buyOp = new BuyArmoireOperation(user, req, analytics);
buyRes = buyOp.purchase();
break;
}
case 'mystery':
buyRes = buyMysterySet(user, req, analytics);
break;
case 'potion':
buyRes = buyHealthPotion(user, req, analytics);
case 'potion': {
const buyOp = new BuyHealthPotionOperation(user, req, analytics);
buyRes = buyOp.purchase();
break;
}
case 'eggs':
case 'hatchingPotions':
case 'food':
+90 -66
View File
@@ -1,5 +1,4 @@
import content from '../../content/index';
import i18n from '../../i18n';
import filter from 'lodash/filter';
import isEmpty from 'lodash/isEmpty';
import pick from 'lodash/pick';
@@ -9,7 +8,8 @@ import {
NotAuthorized,
} from '../../libs/errors';
import randomVal from '../../libs/randomVal';
import { removeItemByPath } from '../pinnedGearUtils';
import {removeItemByPath} from '../pinnedGearUtils';
import {AbstractGoldItemOperation} from './abstractBuyOperation';
// TODO this is only used on the server
// move out of common?
@@ -17,41 +17,71 @@ import { removeItemByPath } from '../pinnedGearUtils';
const YIELD_EQUIPMENT_THRESHOLD = 0.6;
const YIELD_FOOD_THRESHOLD = 0.8;
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));
export class BuyArmoireOperation extends AbstractGoldItemOperation {
constructor (user, req, analytics) {
super(user, req, analytics);
}
if (item.canOwn && !item.canOwn(user)) {
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
multiplePurchaseAllowed () {
return false;
}
let armoireResp;
let drop;
let message;
extractAndValidateParams (user) {
let item = content.armoire;
let armoireResult = randomVal.trueRandom();
let eligibleEquipment = filter(content.gear.flat, (eligible) => {
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
});
let armoireHasEquipment = !isEmpty(eligibleEquipment);
this.canUserPurchase(user, item);
}
if (armoireHasEquipment && (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)) {
executeChanges (user, item) {
let result = {};
let armoireResult = randomVal.trueRandom();
let eligibleEquipment = filter(content.gear.flat, (eligible) => {
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
});
let 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) { // eslint-disable-line no-extra-parens
result = this._foodResult(user);
} else {
result = this._experienceResult(user);
}
this.subtractCurrency(user, item);
let {message, armoireResp} = result;
if (!message) {
message = this.i18n('messageBought', {
itemText: this.item.text(this.req.language),
});
}
let resData = pick(user, splitWhitespace('items flags'));
if (armoireResp) resData.armoire = armoireResp;
return [
resData,
message,
];
}
_gearResult (user, eligibleEquipment) {
eligibleEquipment.sort();
drop = randomVal(eligibleEquipment);
let drop = randomVal(eligibleEquipment);
if (user.items.gear.owned[drop.key]) {
throw new NotAuthorized(i18n.t('equipmentAlreadyOwned', req.language));
throw new NotAuthorized(this.i18n('equipmentAlreadyOwned'));
}
user.items.gear.owned[drop.key] = true;
user.flags.armoireOpened = true;
message = i18n.t('armoireEquipment', {
let message = this.i18n('armoireEquipment', {
image: `<span class="shop_${drop.key} pull-left"></span>`,
dropText: drop.text(req.language),
}, req.language);
dropText: drop.text(this.req.language),
});
if (count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
user.flags.armoireEmpty = true;
@@ -59,62 +89,56 @@ module.exports = function buyArmoire (user, req = {}, analytics) {
removeItemByPath(user, `gear.flat.${drop.key}`);
armoireResp = {
let armoireResp = {
type: 'gear',
dropKey: drop.key,
dropText: drop.text(req.language),
dropText: drop.text(this.req.language),
};
} else if ((armoireHasEquipment && armoireResult < YIELD_FOOD_THRESHOLD) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
drop = randomVal(filter(content.food, {
return {
message,
armoireResp,
};
}
_foodResult (user) {
let drop = randomVal(filter(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>`,
dropText: drop.text(req.language),
}, req.language);
armoireResp = {
type: 'food',
dropKey: drop.key,
dropText: drop.textA(req.language),
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),
},
};
} else {
}
_experienceResult (user) {
let armoireExp = Math.floor(randomVal.trueRandom() * 40 + 10);
user.stats.exp += armoireExp;
message = i18n.t('armoireExp', req.language);
armoireResp = {
type: 'experience',
value: armoireExp,
return {
message: this.i18n('armoireExp'),
armoireResp: {
type: 'experience',
value:
armoireExp,
},
};
}
user.stats.gp -= item.value;
if (!message) {
message = i18n.t('messageBought', {
itemText: item.text(req.language),
}, req.language);
analyticsData () {
let data = super.analyticsData();
data.itemKey = 'Armoire';
return data;
}
if (analytics) {
analytics.track('acquire item', {
uuid: user._id,
itemKey: 'Armoire',
acquireMethod: 'Gold',
goldCost: item.value,
category: 'behavior',
headers: req.headers,
});
}
let resData = pick(user, splitWhitespace('items flags'));
if (armoireResp) resData.armoire = armoireResp;
return [
resData,
message,
];
};
}
@@ -1,55 +1,55 @@
import content from '../../content/index';
import i18n from '../../i18n';
import {
NotAuthorized,
} from '../../libs/errors';
module.exports = function buyHealthPotion (user, req = {}, analytics) {
let item = content.potion;
let quantity = req.quantity || 1;
import { AbstractGoldItemOperation} from './abstractBuyOperation';
if (user.stats.gp < item.value * quantity) {
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
export class BuyHealthPotionOperation extends AbstractGoldItemOperation {
constructor (user, req, analytics) {
super(user, req, analytics);
}
if (item.canOwn && !item.canOwn(user)) {
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
multiplePurchaseAllowed () {
return true;
}
if (user.stats.hp >= 50) {
throw new NotAuthorized(i18n.t('messageHealthAlreadyMax', req.language));
extractAndValidateParams (user) {
let item = content.potion;
let userHp = user.stats.hp;
super.canUserPurchase(user, item);
if (userHp >= 50) {
throw new NotAuthorized(this.i18n('messageHealthAlreadyMax'));
}
if (userHp <= 0) {
throw new NotAuthorized(this.i18n('messageHealthAlreadyMin'));
}
}
if (user.stats.hp <= 0) {
throw new NotAuthorized(i18n.t('messageHealthAlreadyMin', req.language));
}
executeChanges (user, item) {
user.stats.hp += 15 * this.quantity;
if (user.stats.hp > 50) {
user.stats.hp = 50;
}
user.stats.hp += 15 * quantity;
if (user.stats.hp > 50) {
user.stats.hp = 50;
}
this.subtractCurrency(user, item, this.quantity);
user.stats.gp -= item.value * quantity;
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',
headers: req.headers,
quantityPurchased: quantity,
let message = this.i18n('messageBought', {
itemText: this.item.text(this.req.language),
});
return [
this.user.stats,
message,
];
}
return [
user.stats,
message,
];
};
analyticsData () {
let data = super.analyticsData();
data.itemKey = 'Potion';
return data;
}
}