Merge branch 'api-v3-ops-revive' of https://github.com/TheHollidayInn/habitrpg into TheHollidayInn-api-v3-ops-revive

This commit is contained in:
Matteo Pagliazzi
2016-04-06 23:24:14 +02:00
7 changed files with 225 additions and 41 deletions

View File

@@ -157,5 +157,6 @@
"sold": "You sold a <%= key %> <%= type %>", "sold": "You sold a <%= key %> <%= type %>",
"pathRequired": "Path string is required", "pathRequired": "Path string is required",
"unlocked": "Items have been unlocked", "unlocked": "Items have been unlocked",
"alreadyUnlocked": "Item already unlocked" "alreadyUnlocked": "Item already unlocked",
"cannotRevive": "Cannot revive if not dead"
} }

View File

@@ -123,6 +123,7 @@ import releaseBoth from './ops/releaseBoth';
import releaseMounts from './ops/releaseMounts'; import releaseMounts from './ops/releaseMounts';
import sell from './ops/sell'; import sell from './ops/sell';
import unlock from './ops/unlock'; import unlock from './ops/unlock';
import revive from './ops/revive';
api.ops = { api.ops = {
scoreTask, scoreTask,
@@ -147,6 +148,7 @@ api.ops = {
releaseMounts, releaseMounts,
sell, sell,
unlock, unlock,
revive,
}; };
import handleTwoHanded from './fns/handleTwoHanded'; import handleTwoHanded from './fns/handleTwoHanded';

View File

@@ -1,72 +1,106 @@
import content from '../content/index'; import content from '../content/index';
import i18n from '../i18n'; import i18n from '../i18n';
import _ from 'lodash'; import _ from 'lodash';
import {
NotAuthorized,
} from '../libs/errors';
import splitWhitespace from '../libs/splitWhitespace';
import randomVal from '../fns/randomVal';
module.exports = function(user, req, cb, analytics) { module.exports = function revive (user, req = {}, analytics) {
var analyticsData, base, cl, gearOwned, item, losableItems, lostItem, lostStat; if (user.stats.hp > 0) {
if (!(user.stats.hp <= 0)) { throw new NotAuthorized(i18n.t('cannotRevive', req.language));
return typeof cb === "function" ? cb({
code: 400,
message: "Cannot revive if not dead"
}) : void 0;
} }
_.merge(user.stats, { _.merge(user.stats, {
hp: 50, hp: 50,
exp: 0, exp: 0,
gp: 0 gp: 0,
}); });
if (user.stats.lvl > 1) { if (user.stats.lvl > 1) {
user.stats.lvl--; user.stats.lvl--;
} }
lostStat = user.fns.randomVal(_.reduce(['str', 'con', 'per', 'int'], (function(m, k) {
let lostStat = randomVal(user, _.reduce(['str', 'con', 'per', 'int'], function findRandomStat (m, k) {
if (user.stats[k]) { if (user.stats[k]) {
m[k] = k; m[k] = k;
} }
return m; return m;
}), {})); }, {}));
if (lostStat) { if (lostStat) {
user.stats[lostStat]--; user.stats[lostStat]--;
} }
cl = user.stats["class"];
gearOwned = (typeof (base = user.items.gear.owned).toObject === "function" ? base.toObject() : void 0) || user.items.gear.owned; let base = user.items.gear.owned;
losableItems = {}; let gearOwned;
_.each(gearOwned, function(v, k) {
var itm; if (typeof base.toObject === 'function') {
if (v) { gearOwned = base.toObject();
itm = content.gear.flat['' + k]; } else {
gearOwned = user.items.gear.owned;
}
let losableItems = {};
let userClass = user.stats.class;
_.each(gearOwned, function findLosableItems (value, key) {
let itm;
if (value) {
itm = content.gear.flat[key];
if (itm) { if (itm) {
if ((itm.value > 0 || k === 'weapon_warrior_0') && (itm.klass === cl || (itm.klass === 'special' && (!itm.specialClass || itm.specialClass === cl)) || itm.klass === 'armoire')) { let itemHasValueOrWarrior0 = itm.value > 0 || key === 'weapon_warrior_0';
return losableItems['' + k] = '' + k;
let itemClassEqualsUserClass = itm.klass === userClass;
let itemClassSpecial = itm.klass === 'special';
let itemNotSpecialOrUserClassIsSpecial = !itm.specialClass || itm.specialClass === userClass;
let itemIsSpecial = itemNotSpecialOrUserClassIsSpecial && itemClassSpecial;
let itemIsArmoire = itm.klass === 'armoire';
if (itemHasValueOrWarrior0 && (itemClassEqualsUserClass || itemIsSpecial || itemIsArmoire)) {
losableItems[key] = key;
return losableItems[key];
} }
} }
} }
}); });
lostItem = user.fns.randomVal(losableItems);
if (item = content.gear.flat[lostItem]) { let lostItem = randomVal(user, losableItems);
let message = '';
let item = content.gear.flat[lostItem];
if (item) {
user.items.gear.owned[lostItem] = false; user.items.gear.owned[lostItem] = false;
if (user.items.gear.equipped[item.type] === lostItem) { if (user.items.gear.equipped[item.type] === lostItem) {
user.items.gear.equipped[item.type] = item.type + "_base_0"; user.items.gear.equipped[item.type] = `${item.type}_base_0`;
} }
if (user.items.gear.costume[item.type] === lostItem) { if (user.items.gear.costume[item.type] === lostItem) {
user.items.gear.costume[item.type] = item.type + "_base_0"; user.items.gear.costume[item.type] = `${item.type}_base_0`;
} }
message = i18n.t('messageLostItem', { itemText: item.text(req.language)}, req.language);
} }
if (typeof user.markModified === "function") {
user.markModified('items.gear'); if (analytics) {
analytics.track('Death', {
uuid: user._id,
lostItem,
gaLabel: lostItem,
category: 'behavior',
});
} }
analyticsData = {
uuid: user._id, let response = {
lostItem: lostItem, data: _.pick(user, splitWhitespace('user.items')),
gaLabel: lostItem, message,
category: 'behavior'
}; };
if (analytics != null) {
analytics.track('Death', analyticsData); return response;
}
return typeof cb === "function" ? cb((item ? {
code: 200,
message: i18n.t('messageLostItem', {
itemText: item.text(req.language)
}, req.language)
} : null), user) : void 0;
}; };

View File

@@ -29,7 +29,6 @@ const COMMON_FILES = [
'!./common/script/ops/releasePets.js', '!./common/script/ops/releasePets.js',
'!./common/script/ops/reroll.js', '!./common/script/ops/reroll.js',
'!./common/script/ops/reset.js', '!./common/script/ops/reset.js',
'!./common/script/ops/revive.js',
'!./common/script/ops/sortTag.js', '!./common/script/ops/sortTag.js',
'!./common/script/ops/sortTask.js', '!./common/script/ops/sortTask.js',
'!./common/script/ops/update.js', '!./common/script/ops/update.js',

View File

@@ -0,0 +1,37 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
describe('POST /user/revive', () => {
let user;
beforeEach(async () => {
user = await generateUser({
'user.items.gear.owned': {weaponKey: true},
});
});
it('returns an error when user is not dead', async () => {
await expect(user.post('/user/revive'))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('cannotRevive'),
});
});
// More tests in common code unit tests
it('decreases a stat', async () => {
await user.update({
'stats.str': 2,
'stats.hp': 0,
});
await user.post('/user/revive');
await user.sync();
expect(user.stats.str).to.equal(1);
});
});

91
test/common/ops/revive.js Normal file
View File

@@ -0,0 +1,91 @@
import revive from '../../../common/script/ops/revive';
import i18n from '../../../common/script/i18n';
import {
generateUser,
} from '../../helpers/common.helper';
import {
NotAuthorized,
} from '../../../common/script/libs/errors';
import content from '../../../common/script/content/index';
describe('shared.ops.revive', () => {
let user;
beforeEach(() => {
user = generateUser();
user.stats.hp = 0;
});
it('returns an error when user is not dead', (done) => {
user.stats.hp = 10;
try {
revive(user);
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('cannotRevive'));
done();
}
});
it('resets user\'s hp, exp and gp', () => {
user.stats.exp = 100;
user.stats.gp = 100;
revive(user);
expect(user.stats.hp).to.equal(50);
expect(user.stats.exp).to.equal(0);
expect(user.stats.gp).to.equal(0);
});
it('decreases user\'s level', () => {
user.stats.lvl = 2;
revive(user);
expect(user.stats.lvl).to.equal(1);
});
it('decreases a stat', () => {
user.stats.str = 2;
revive(user);
expect(user.stats.str).to.equal(1);
});
it('removes a random item from user gear owned', () => {
let weaponKey = 'weapon_warrior_0';
user.items.gear.owned[weaponKey] = true;
let reviveRequest = revive(user);
expect(reviveRequest.message).to.equal(i18n.t('messageLostItem', { itemText: content.gear.flat[weaponKey].text()}));
expect(user.items.gear.owned[weaponKey]).to.be.false;
});
it('removes a random item from user gear equipped', () => {
let weaponKey = 'weapon_warrior_0';
let itemToLose = content.gear.flat[weaponKey];
user.items.gear.owned[weaponKey] = true;
user.items.gear.equipped[itemToLose.type] = itemToLose.key;
let reviveRequest = revive(user);
expect(reviveRequest.message).to.equal(i18n.t('messageLostItem', { itemText: itemToLose.text()}));
expect(user.items.gear.equipped[itemToLose.type]).to.equal(`${itemToLose.type}_base_0`);
});
it('removes a random item from user gear costume', () => {
let weaponKey = 'weapon_warrior_0';
let itemToLose = content.gear.flat[weaponKey];
user.items.gear.owned[weaponKey] = true;
user.items.gear.costume[itemToLose.type] = itemToLose.key;
let reviveRequest = revive(user);
expect(reviveRequest.message).to.equal(i18n.t('messageLostItem', { itemText: itemToLose.text()}));
expect(user.items.gear.costume[itemToLose.type]).to.equal(`${itemToLose.type}_base_0`);
});
});

View File

@@ -868,4 +868,24 @@ api.userUnlock = {
}, },
}; };
/**
* @api {post} /user/revive Revives user from death.
* @apiVersion 3.0.0
* @apiName UserRevive
* @apiGroup User
*
* @apiSuccess {Object} data `user.items`
*/
api.userRevive = {
method: 'POST',
middlewares: [authWithHeaders(), cron],
url: '/user/revive',
async handler (req, res) {
let user = res.locals.user;
let reviveResponse = common.ops.revive(user, req, res.analytics);
await user.save();
res.respond(200, reviveResponse);
},
};
module.exports = api; module.exports = api;