mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
wip(shared): port buy ops and linked fns
This commit is contained in:
@@ -77,6 +77,7 @@
|
|||||||
"guildQuestsNotSupported": "Guilds cannot be invited on quests.",
|
"guildQuestsNotSupported": "Guilds cannot be invited on quests.",
|
||||||
"questNotFound": "Quest \"<%= key %>\" not found.",
|
"questNotFound": "Quest \"<%= key %>\" not found.",
|
||||||
"questNotOwned": "You don't own that quest scroll.",
|
"questNotOwned": "You don't own that quest scroll.",
|
||||||
|
"questNotGoldPurchasable": "Quest \"<%= key %>\" is not a Gold-purchasable quest.",
|
||||||
"questLevelTooHigh": "You must be level <%= level %> to begin this quest.",
|
"questLevelTooHigh": "You must be level <%= level %> to begin this quest.",
|
||||||
"questAlreadyUnderway": "Your party is already on a quest. Try again when the current quest has ended.",
|
"questAlreadyUnderway": "Your party is already on a quest. Try again when the current quest has ended.",
|
||||||
"questAlreadyAccepted": "You already accepted the quest invitation.",
|
"questAlreadyAccepted": "You already accepted the quest invitation.",
|
||||||
@@ -102,5 +103,9 @@
|
|||||||
"spellNotOwned": "You don't own this spell.",
|
"spellNotOwned": "You don't own this spell.",
|
||||||
"spellLevelTooHigh": "You must be level <%= level %> to use this spell.",
|
"spellLevelTooHigh": "You must be level <%= level %> to use this spell.",
|
||||||
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
|
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
|
||||||
"notEnoughAttrPoints": "You don't have enough attribute points."
|
"notEnoughAttrPoints": "You don't have enough attribute points.",
|
||||||
|
"missingKeyParam": "\"req.params.key\" is required.",
|
||||||
|
"mysterySetNotFound": "Mystery set not found, or set already owned",
|
||||||
|
"itemNotFound": "Item \"<%= key %>\" not found.",
|
||||||
|
"cannoyBuyItem": "You can't buy this item"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
|
|
||||||
module.exports = function(user, item, type, req) {
|
module.exports = function handleTwoHanded (user, item, type = 'equipped', req) {
|
||||||
var message, currentWeapon, currentShield;
|
let currentShield = content.gear.flat[user.items.gear[type].shield];
|
||||||
if (type == null) {
|
let currentWeapon = content.gear.flat[user.items.gear[type].weapon];
|
||||||
type = 'equipped';
|
|
||||||
}
|
|
||||||
currentShield = content.gear.flat[user.items.gear[type].shield];
|
|
||||||
currentWeapon = content.gear.flat[user.items.gear[type].weapon];
|
|
||||||
|
|
||||||
if (item.type === "shield" && (currentWeapon ? currentWeapon.twoHanded : false)) {
|
let message;
|
||||||
|
|
||||||
|
if (item.type === 'shield' && (currentWeapon ? currentWeapon.twoHanded : false)) {
|
||||||
user.items.gear[type].weapon = 'weapon_base_0';
|
user.items.gear[type].weapon = 'weapon_base_0';
|
||||||
message = i18n.t('messageTwoHandedUnequip', {
|
message = i18n.t('messageTwoHandedUnequip', {
|
||||||
twoHandedText: currentWeapon.text(req.language), offHandedText: item.text(req.language),
|
twoHandedText: currentWeapon.text(req.language), offHandedText: item.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
} else if (item.twoHanded && (currentShield && user.items.gear[type].shield != "shield_base_0")) {
|
} else if (item.twoHanded && (currentShield && user.items.gear[type].shield !== 'shield_base_0')) {
|
||||||
user.items.gear[type].shield = "shield_base_0";
|
user.items.gear[type].shield = 'shield_base_0';
|
||||||
message = i18n.t('messageTwoHandedEquip', {
|
message = i18n.t('messageTwoHandedEquip', {
|
||||||
twoHandedText: item.text(req.language), offHandedText: currentShield.text(req.language),
|
twoHandedText: item.text(req.language), offHandedText: currentShield.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
/*
|
|
||||||
Because the same op needs to be performed on the client and the server (critical hits, item drops, etc),
|
|
||||||
we need things to be "random", but technically predictable so that they don't go out-of-sync
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = function(user, seed) {
|
// Because the same op needs to be performed on the client and the server (critical hits, item drops, etc),
|
||||||
var x;
|
// we need things to be "random", but technically predictable so that they don't go out-of-sync
|
||||||
|
|
||||||
|
module.exports = function predictableRandom (user, seed) {
|
||||||
if (!seed || seed === Math.PI) {
|
if (!seed || seed === Math.PI) {
|
||||||
seed = _.reduce(user.stats, (function(m, v) {
|
seed = _.reduce(user.stats, (accumulator, val) => {
|
||||||
if (_.isNumber(v)) {
|
if (_.isNumber(val)) {
|
||||||
return m + v;
|
return accumulator + val;
|
||||||
} else {
|
} else {
|
||||||
return m;
|
return accumulator;
|
||||||
}
|
}
|
||||||
}), 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
x = Math.sin(seed++) * 10000;
|
|
||||||
|
let x = Math.sin(seed++) * 10000;
|
||||||
return x - Math.floor(x);
|
return x - Math.floor(x);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import predictableRandom from './predictableRandom';
|
||||||
|
|
||||||
/*
|
// Get a random property from an object
|
||||||
Get a random property from an object
|
// returns random property (the value)
|
||||||
returns random property (the value)
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = function(user, obj, options) {
|
module.exports = function randomVal (user, obj, options = {}) {
|
||||||
var array, rand;
|
let array = options.key ? _.keys(obj) : _.values(obj);
|
||||||
array = (options != null ? options.key : void 0) ? _.keys(obj) : _.values(obj);
|
let rand = predictableRandom(user, options.seed);
|
||||||
rand = user.fns.predictableRandom(options != null ? options.seed : void 0);
|
|
||||||
array.sort();
|
array.sort();
|
||||||
return array[Math.floor(rand * array.length)];
|
return array[Math.floor(rand * array.length)];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,33 +1,35 @@
|
|||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
module.exports = function(user) {
|
module.exports = function ultimateGear (user) {
|
||||||
var base, owned;
|
let owned = window ? user.items.gear.owned : user.items.gear.owned.toObject();
|
||||||
owned = typeof window !== "undefined" && window !== null ? user.items.gear.owned : user.items.gear.owned.toObject();
|
|
||||||
if ((base = user.achievements).ultimateGearSets == null) {
|
if (!user.achievements.ultimateGearSets) {
|
||||||
base.ultimateGearSets = {
|
user.achievements.ultimateGearSets = {
|
||||||
healer: false,
|
healer: false,
|
||||||
wizard: false,
|
wizard: false,
|
||||||
rogue: false,
|
rogue: false,
|
||||||
warrior: false
|
warrior: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
content.classes.forEach(function(klass) {
|
|
||||||
|
content.classes.forEach((klass) => {
|
||||||
if (user.achievements.ultimateGearSets[klass] !== true) {
|
if (user.achievements.ultimateGearSets[klass] !== true) {
|
||||||
return user.achievements.ultimateGearSets[klass] = _.reduce(['armor', 'shield', 'head', 'weapon'], function(soFarGood, type) {
|
user.achievements.ultimateGearSets[klass] = _.reduce(['armor', 'shield', 'head', 'weapon'], (soFarGood, type) => {
|
||||||
var found;
|
let found = _.find(content.gear.tree[type][klass], {
|
||||||
found = _.find(content.gear.tree[type][klass], {
|
last: true,
|
||||||
last: true
|
|
||||||
});
|
});
|
||||||
return soFarGood && (!found || owned[found.key] === true);
|
return soFarGood && (!found || owned[found.key] === true);
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (typeof user.markModified === "function") {
|
|
||||||
user.markModified('achievements.ultimateGearSets');
|
// TODO
|
||||||
}
|
if (user.markModified) user.markModified('achievements.ultimateGearSets');
|
||||||
|
|
||||||
if (_.contains(user.achievements.ultimateGearSets, true) && user.flags.armoireEnabled !== true) {
|
if (_.contains(user.achievements.ultimateGearSets, true) && user.flags.armoireEnabled !== true) {
|
||||||
user.flags.armoireEnabled = true;
|
user.flags.armoireEnabled = true;
|
||||||
return typeof user.markModified === "function" ? user.markModified('flags') : void 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,117 +3,133 @@ import i18n from '../i18n';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import count from '../count';
|
import count from '../count';
|
||||||
import splitWhitespace from '../libs/splitWhitespace';
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
BadRequest,
|
||||||
|
NotAuthorized,
|
||||||
|
NotFound,
|
||||||
|
} from '../libs/errors';
|
||||||
|
import predictableRandom from '../fns/predictableRandom';
|
||||||
|
import randomVal from '../fns/randomVal';
|
||||||
|
import handleTwoHanded from '../fns/handleTwoHanded';
|
||||||
|
import ultimateGear from '../fns/ultimateGear';
|
||||||
|
|
||||||
module.exports = function(user, req, cb, analytics) {
|
module.exports = function buy (user, req = {}, analytics) {
|
||||||
var analyticsData, armoireExp, armoireResp, armoireResult, base, buyResp, drop, eligibleEquipment, item, key, message, name;
|
let key = _.get(req, 'params.key');
|
||||||
key = req.params.key;
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
item = key === 'potion' ? content.potion : key === 'armoire' ? content.armoire : content.gear.flat[key];
|
|
||||||
if (!item) {
|
let item;
|
||||||
return typeof cb === "function" ? cb({
|
if (key === 'potion') {
|
||||||
code: 404,
|
item = content.potion;
|
||||||
message: "Item '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
|
} else if (key === 'armoire') {
|
||||||
}) : void 0;
|
item = content.armoire;
|
||||||
|
} else {
|
||||||
|
item = content.gear.flat[key];
|
||||||
}
|
}
|
||||||
|
if (!item) throw new NotFound(i18n.t('itemNotFound', {key}, req.language));
|
||||||
|
|
||||||
if (user.stats.gp < item.value) {
|
if (user.stats.gp < item.value) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
code: 401,
|
|
||||||
message: i18n.t('messageNotEnoughGold', req.language)
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
if ((item.canOwn != null) && !item.canOwn(user)) {
|
|
||||||
return typeof cb === "function" ? cb({
|
if (item.canOwn && !item.canOwn(user)) {
|
||||||
code: 401,
|
throw new NotAuthorized(i18n.t('cannoyBuyItem', req.language));
|
||||||
message: "You can't buy this item"
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
armoireResp = void 0;
|
|
||||||
|
let armoireResp;
|
||||||
|
let armoireResult;
|
||||||
|
let eligibleEquipment;
|
||||||
|
let drop;
|
||||||
|
let message;
|
||||||
|
|
||||||
if (item.key === 'potion') {
|
if (item.key === 'potion') {
|
||||||
user.stats.hp += 15;
|
user.stats.hp += 15;
|
||||||
if (user.stats.hp > 50) {
|
if (user.stats.hp > 50) {
|
||||||
user.stats.hp = 50;
|
user.stats.hp = 50;
|
||||||
}
|
}
|
||||||
} else if (item.key === 'armoire') {
|
} else if (item.key === 'armoire') {
|
||||||
armoireResult = user.fns.predictableRandom(user.stats.gp);
|
armoireResult = predictableRandom(user, user.stats.gp);
|
||||||
eligibleEquipment = _.filter(content.gear.flat, (function(i) {
|
eligibleEquipment = _.filter(content.gear.flat, (eligible) => {
|
||||||
return i.klass === 'armoire' && !user.items.gear.owned[i.key];
|
return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key];
|
||||||
}));
|
});
|
||||||
if (!_.isEmpty(eligibleEquipment) && (armoireResult < .6 || !user.flags.armoireOpened)) {
|
|
||||||
|
if (!_.isEmpty(eligibleEquipment) && (armoireResult < 0.6 || !user.flags.armoireOpened)) {
|
||||||
eligibleEquipment.sort();
|
eligibleEquipment.sort();
|
||||||
drop = user.fns.randomVal(eligibleEquipment);
|
drop = randomVal(user, eligibleEquipment);
|
||||||
|
|
||||||
user.items.gear.owned[drop.key] = true;
|
user.items.gear.owned[drop.key] = true;
|
||||||
user.flags.armoireOpened = true;
|
user.flags.armoireOpened = true;
|
||||||
message = i18n.t('armoireEquipment', {
|
message = i18n.t('armoireEquipment', {
|
||||||
image: '<span class="shop_' + drop.key + ' pull-left"></span>',
|
image: `<span class="shop_${drop.key} pull-left"></span>`,
|
||||||
dropText: drop.text(req.language)
|
dropText: drop.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
|
|
||||||
if (count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
|
if (count.remainingGearInSet(user.items.gear.owned, 'armoire') === 0) {
|
||||||
user.flags.armoireEmpty = true;
|
user.flags.armoireEmpty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
armoireResp = {
|
armoireResp = {
|
||||||
type: "gear",
|
type: 'gear',
|
||||||
dropKey: drop.key,
|
dropKey: drop.key,
|
||||||
dropText: drop.text(req.language)
|
dropText: drop.text(req.language),
|
||||||
};
|
};
|
||||||
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < .8) || armoireResult < .5) {
|
} else if ((!_.isEmpty(eligibleEquipment) && armoireResult < 0.8) || armoireResult < 0.5) { // eslint-disable-line no-extra-parens
|
||||||
drop = user.fns.randomVal(_.where(content.food, {
|
drop = randomVal(_.where(content.food, {
|
||||||
canDrop: true
|
canDrop: true,
|
||||||
}));
|
}));
|
||||||
if ((base = user.items.food)[name = drop.key] == null) {
|
user.items.food[drop.key] = user.items.food[drop.key] || 0;
|
||||||
base[name] = 0;
|
|
||||||
}
|
|
||||||
user.items.food[drop.key] += 1;
|
user.items.food[drop.key] += 1;
|
||||||
|
|
||||||
message = i18n.t('armoireFood', {
|
message = i18n.t('armoireFood', {
|
||||||
image: '<span class="Pet_Food_' + drop.key + ' pull-left"></span>',
|
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
||||||
dropArticle: drop.article,
|
dropArticle: drop.article,
|
||||||
dropText: drop.text(req.language)
|
dropText: drop.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
armoireResp = {
|
armoireResp = {
|
||||||
type: "food",
|
type: 'food',
|
||||||
dropKey: drop.key,
|
dropKey: drop.key,
|
||||||
dropArticle: drop.article,
|
dropArticle: drop.article,
|
||||||
dropText: drop.text(req.language)
|
dropText: drop.text(req.language),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
armoireExp = Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10);
|
let armoireExp = Math.floor(predictableRandom(user, user.stats.exp) * 40 + 10);
|
||||||
user.stats.exp += armoireExp;
|
user.stats.exp += armoireExp;
|
||||||
message = i18n.t('armoireExp', req.language);
|
message = i18n.t('armoireExp', req.language);
|
||||||
armoireResp = {
|
armoireResp = {
|
||||||
"type": "experience",
|
type: 'experience',
|
||||||
"value": armoireExp
|
value: armoireExp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (user.preferences.autoEquip) {
|
if (user.preferences.autoEquip) {
|
||||||
user.items.gear.equipped[item.type] = item.key;
|
user.items.gear.equipped[item.type] = item.key;
|
||||||
message = user.fns.handleTwoHanded(item, null, req);
|
message = handleTwoHanded(user, item, null, req);
|
||||||
}
|
}
|
||||||
user.items.gear.owned[item.key] = true;
|
user.items.gear.owned[item.key] = true;
|
||||||
if (message == null) {
|
|
||||||
|
if (!message) {
|
||||||
message = i18n.t('messageBought', {
|
message = i18n.t('messageBought', {
|
||||||
itemText: item.text(req.language)
|
itemText: item.text(req.language),
|
||||||
}, req.language);
|
}, req.language);
|
||||||
}
|
}
|
||||||
if (item.last) {
|
if (item.last) ultimateGear(user);
|
||||||
user.fns.ultimateGear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.stats.gp -= item.value;
|
user.stats.gp -= item.value;
|
||||||
analyticsData = {
|
if (analytics) {
|
||||||
uuid: user._id,
|
analytics.track('acquire item', {
|
||||||
itemKey: key,
|
uuid: user._id,
|
||||||
acquireMethod: 'Gold',
|
itemKey: key,
|
||||||
goldCost: item.value,
|
acquireMethod: 'Gold',
|
||||||
category: 'behavior'
|
goldCost: item.value,
|
||||||
|
category: 'behavior',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let buyResp = _.pick(user, splitWhitespace('items achievements stats flags'));
|
||||||
|
if (armoireResp) buyResp.armoire = armoireResp;
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: buyResp,
|
||||||
|
message,
|
||||||
};
|
};
|
||||||
if (analytics != null) {
|
|
||||||
analytics.track('acquire item', analyticsData);
|
|
||||||
}
|
|
||||||
buyResp = _.pick(user, splitWhitespace('items achievements stats flags'));
|
|
||||||
if (armoireResp) {
|
|
||||||
buyResp["armoire"] = armoireResp;
|
|
||||||
}
|
|
||||||
return typeof cb === "function" ? cb({
|
|
||||||
code: 200,
|
|
||||||
message: message
|
|
||||||
}, buyResp) : void 0;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,42 +2,48 @@ import i18n from '../i18n';
|
|||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import splitWhitespace from '../libs/splitWhitespace';
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
BadRequest,
|
||||||
|
NotAuthorized,
|
||||||
|
NotFound,
|
||||||
|
} from '../libs/errors';
|
||||||
|
|
||||||
|
module.exports = function buyMysterySet (user, req = {}, analytics) {
|
||||||
|
let key = _.get(req, 'params.key');
|
||||||
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
|
|
||||||
module.exports = function(user, req, cb, analytics) {
|
|
||||||
var mysterySet, ref;
|
|
||||||
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
|
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||||
code: 401,
|
|
||||||
message: i18n.t('notEnoughHourglasses', req.language)
|
|
||||||
}) : void 0;
|
|
||||||
}
|
|
||||||
mysterySet = (ref = content.timeTravelerStore(user.items.gear.owned)) != null ? ref[req.params.key] : void 0;
|
|
||||||
if ((typeof window !== "undefined" && window !== null ? window.confirm : void 0) != null) {
|
|
||||||
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ref = content.timeTravelerStore(user.items.gear.owned);
|
||||||
|
let mysterySet = ref ? ref[key] : undefined;
|
||||||
|
|
||||||
if (!mysterySet) {
|
if (!mysterySet) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotFound(i18n.t('mysterySetNotFound', req.language));
|
||||||
code: 404,
|
|
||||||
message: "Mystery set not found, or set already owned"
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
_.each(mysterySet.items, function(i) {
|
|
||||||
var analyticsData;
|
if (window && window.confirm) { // TODO move to client
|
||||||
user.items.gear.owned[i.key] = true;
|
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) return;
|
||||||
analyticsData = {
|
}
|
||||||
uuid: user._id,
|
|
||||||
itemKey: i.key,
|
_.each(mysterySet.items, item => {
|
||||||
itemType: 'Subscriber Gear',
|
user.items.gear.owned[item.key] = true;
|
||||||
acquireMethod: 'Hourglass',
|
if (analytics) {
|
||||||
category: 'behavior'
|
analytics.track('acquire item', {
|
||||||
};
|
uuid: user._id,
|
||||||
return analytics != null ? analytics.track('acquire item', analyticsData) : void 0;
|
itemKey: item.key,
|
||||||
|
itemType: 'Subscriber Gear',
|
||||||
|
acquireMethod: 'Hourglass',
|
||||||
|
category: 'behavior',
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
user.purchased.plan.consecutive.trinkets--;
|
user.purchased.plan.consecutive.trinkets--;
|
||||||
return typeof cb === "function" ? cb({
|
|
||||||
code: 200,
|
return {
|
||||||
message: i18n.t('hourglassPurchaseSet', req.language)
|
data: _.pick(user, splitWhitespace('items purchased.plan.consecutive')),
|
||||||
}, _.pick(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0;
|
message: i18n.t('hourglassPurchaseSet', req.language),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,49 +1,44 @@
|
|||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
|
import {
|
||||||
|
BadRequest,
|
||||||
|
NotAuthorized,
|
||||||
|
NotFound,
|
||||||
|
} from '../libs/errors';
|
||||||
|
import _ from 'lodash';
|
||||||
|
module.exports = function buyQuest (user, req = {}, analytics) {
|
||||||
|
let key = _.get(req, 'params.key');
|
||||||
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
|
|
||||||
|
let item = content.quests[key];
|
||||||
|
if (!item) throw new NotFound(i18n.t('questNotFound', req.language));
|
||||||
|
|
||||||
module.exports = function(user, req, cb, analytics) {
|
|
||||||
var analyticsData, base, item, key, message, name;
|
|
||||||
key = req.params.key;
|
|
||||||
item = content.quests[key];
|
|
||||||
if (!item) {
|
|
||||||
return typeof cb === "function" ? cb({
|
|
||||||
code: 404,
|
|
||||||
message: "Quest '" + key + " not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
|
|
||||||
}) : void 0;
|
|
||||||
}
|
|
||||||
if (!(item.category === 'gold' && item.goldValue)) {
|
if (!(item.category === 'gold' && item.goldValue)) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotAuthorized(i18n.t('questNotGoldPurchasable', {key}, req.language));
|
||||||
code: 404,
|
|
||||||
message: "Quest '" + key + " is not a Gold-purchasable quest (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content/index.js)"
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
if (user.stats.gp < item.goldValue) {
|
if (user.stats.gp < item.goldValue) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
code: 401,
|
|
||||||
message: i18n.t('messageNotEnoughGold', req.language)
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
message = i18n.t('messageBought', {
|
|
||||||
itemText: item.text(req.language)
|
user.items.quests[item.key] = user.items.quests[item.key] || 0;
|
||||||
}, req.language);
|
user.items.quests[item.key]++;
|
||||||
if ((base = user.items.quests)[name = item.key] == null) {
|
|
||||||
base[name] = 0;
|
|
||||||
}
|
|
||||||
user.items.quests[item.key] += 1;
|
|
||||||
user.stats.gp -= item.goldValue;
|
user.stats.gp -= item.goldValue;
|
||||||
analyticsData = {
|
|
||||||
uuid: user._id,
|
if (analytics) {
|
||||||
itemKey: item.key,
|
analytics.track('acquire item', {
|
||||||
itemType: 'Market',
|
uuid: user._id,
|
||||||
goldCost: item.goldValue,
|
itemKey: item.key,
|
||||||
acquireMethod: 'Gold',
|
itemType: 'Market',
|
||||||
category: 'behavior'
|
goldCost: item.goldValue,
|
||||||
};
|
acquireMethod: 'Gold',
|
||||||
if (analytics != null) {
|
category: 'behavior',
|
||||||
analytics.track('acquire item', analyticsData);
|
});
|
||||||
}
|
}
|
||||||
return typeof cb === "function" ? cb({
|
|
||||||
code: 200,
|
return {
|
||||||
message: message
|
data: user.items.quests,
|
||||||
}, user.items.quests) : void 0;
|
message: i18n.t('messageBought', {
|
||||||
|
itemText: item.text(req.language),
|
||||||
|
}, req.language),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,30 +2,30 @@ import i18n from '../i18n';
|
|||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import splitWhitespace from '../libs/splitWhitespace';
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
BadRequest,
|
||||||
|
NotAuthorized,
|
||||||
|
NotFound,
|
||||||
|
} from '../libs/errors';
|
||||||
|
|
||||||
|
module.exports = function buySpecialSpell (user, req = {}) {
|
||||||
|
let key = _.get(req, 'params.key');
|
||||||
|
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||||
|
|
||||||
|
let item = content.special[key];
|
||||||
|
if (!item) throw new NotFound(i18n.t('spellNotFound', {spellId: key}, req.language));
|
||||||
|
|
||||||
module.exports = function(user, req, cb) {
|
|
||||||
var base, item, key, message;
|
|
||||||
key = req.params.key;
|
|
||||||
item = content.special[key];
|
|
||||||
if (user.stats.gp < item.value) {
|
if (user.stats.gp < item.value) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||||
code: 401,
|
|
||||||
message: i18n.t('messageNotEnoughGold', req.language)
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
user.stats.gp -= item.value;
|
user.stats.gp -= item.value;
|
||||||
if ((base = user.items.special)[key] == null) {
|
|
||||||
base[key] = 0;
|
|
||||||
}
|
|
||||||
user.items.special[key]++;
|
user.items.special[key]++;
|
||||||
if (typeof user.markModified === "function") {
|
|
||||||
user.markModified('items.special');
|
return {
|
||||||
}
|
data: _.pick(user, splitWhitespace('items stats')),
|
||||||
message = i18n.t('messageBought', {
|
message: i18n.t('messageBought', {
|
||||||
itemText: item.text(req.language)
|
itemText: item.text(req.language),
|
||||||
}, req.language);
|
}, req.language),
|
||||||
return typeof cb === "function" ? cb({
|
};
|
||||||
code: 200,
|
|
||||||
message: message
|
|
||||||
}, _.pick(user, splitWhitespace('items stats'))) : void 0;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ const COMMON_FILES = [
|
|||||||
'!./common/script/ops/addWebhook.js',
|
'!./common/script/ops/addWebhook.js',
|
||||||
'!./common/script/ops/allocateNow.js',
|
'!./common/script/ops/allocateNow.js',
|
||||||
'!./common/script/ops/blockUser.js',
|
'!./common/script/ops/blockUser.js',
|
||||||
'!./common/script/ops/buy.js',
|
|
||||||
'!./common/script/ops/buyMysterySet.js',
|
|
||||||
'!./common/script/ops/buyQuest.js',
|
|
||||||
'!./common/script/ops/buySpecialSpell.js',
|
|
||||||
'!./common/script/ops/changeClass.js',
|
'!./common/script/ops/changeClass.js',
|
||||||
'!./common/script/ops/clearCompleted.js',
|
'!./common/script/ops/clearCompleted.js',
|
||||||
'!./common/script/ops/clearPMs.js',
|
'!./common/script/ops/clearPMs.js',
|
||||||
@@ -63,13 +59,9 @@ const COMMON_FILES = [
|
|||||||
'!./common/script/fns/dotGet.js',
|
'!./common/script/fns/dotGet.js',
|
||||||
'!./common/script/fns/dotSet.js',
|
'!./common/script/fns/dotSet.js',
|
||||||
'!./common/script/fns/getItem.js',
|
'!./common/script/fns/getItem.js',
|
||||||
'!./common/script/fns/handleTwoHanded.js',
|
|
||||||
'!./common/script/fns/nullify.js',
|
'!./common/script/fns/nullify.js',
|
||||||
'!./common/script/fns/predictableRandom.js',
|
|
||||||
'!./common/script/fns/preenUserHistory.js',
|
'!./common/script/fns/preenUserHistory.js',
|
||||||
'!./common/script/fns/randomDrop.js',
|
'!./common/script/fns/randomDrop.js',
|
||||||
'!./common/script/fns/randomVal.js',
|
|
||||||
'!./common/script/fns/ultimateGear.js',
|
|
||||||
'!./common/script/fns/updateStats.js',
|
'!./common/script/fns/updateStats.js',
|
||||||
'!./common/script/libs/appliedTags.js',
|
'!./common/script/libs/appliedTags.js',
|
||||||
'!./common/script/libs/countExists.js',
|
'!./common/script/libs/countExists.js',
|
||||||
|
|||||||
@@ -263,15 +263,15 @@ export let schema = new Schema({
|
|||||||
spookDust: {type: Number, default: 0},
|
spookDust: {type: Number, default: 0},
|
||||||
shinySeed: {type: Number, default: 0},
|
shinySeed: {type: Number, default: 0},
|
||||||
seafoam: {type: Number, default: 0},
|
seafoam: {type: Number, default: 0},
|
||||||
valentine: Number,
|
valentine: {type: Number, default: 0},
|
||||||
valentineReceived: Array, // array of strings, by sender name
|
valentineReceived: Array, // array of strings, by sender name
|
||||||
nye: Number,
|
nye: {type: Number, default: 0},
|
||||||
nyeReceived: Array,
|
nyeReceived: Array,
|
||||||
greeting: Number,
|
greeting: {type: Number, default: 0},
|
||||||
greetingReceived: Array,
|
greetingReceived: Array,
|
||||||
thankyou: Number,
|
thankyou: {type: Number, default: 0},
|
||||||
thankyouReceived: Array,
|
thankyouReceived: Array,
|
||||||
birthday: Number,
|
birthday: {type: Number, default: 0},
|
||||||
birthdayReceived: Array,
|
birthdayReceived: Array,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user