port allocateNow and autoAllocate

This commit is contained in:
Matteo Pagliazzi
2016-03-20 20:21:15 +01:00
parent 480194f53c
commit bd3c162b97
8 changed files with 237 additions and 58 deletions

View File

@@ -7,50 +7,59 @@ import splitWhitespace from '../libs/splitWhitespace';
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately {update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
*/ */
module.exports = function(user) { function getStatToAllocate (user) {
return user.stats[(function() { let suggested;
var diff, ideal, lvlDiv7, preference, stats, suggested;
switch (user.preferences.allocationMode) { switch (user.preferences.allocationMode) {
case "flat": case 'flat':
stats = _.pick(user.stats, splitWhitespace('con str per int')); let stats = _.pick(user.stats, splitWhitespace('con str per int'));
return _.invert(stats)[_.min(stats)]; return _.invert(stats)[_.min(stats)];
case "classbased": case 'classbased':
lvlDiv7 = user.stats.lvl / 7; let lvlDiv7 = user.stats.lvl / 7;
ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; let ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7];
preference = (function() {
switch (user.stats["class"]) { let preference;
case "wizard": switch (user.stats.class) {
return ["int", "per", "con", "str"]; case 'wizard':
case "rogue": preference = ['int', 'per', 'con', 'str'];
return ["per", "str", "int", "con"]; break;
case "healer": case 'rogue':
return ["con", "int", "str", "per"]; preference = ['per', 'str', 'int', 'con'];
default: break;
return ["str", "con", "per", "int"]; case 'healer':
} preference = ['con', 'int', 'str', 'per'];
})(); break;
diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]]; default:
suggested = _.findIndex(diff, (function(val) { preference = ['str', 'con', 'per', 'int'];
if (val === _.min(diff)) { }
return true;
} let diff = [
})); user.stats[preference[0]] - ideal[0],
if (~suggested) { user.stats[preference[1]] - ideal[1],
return preference[suggested]; user.stats[preference[2]] - ideal[2],
} else { user.stats[preference[3]] - ideal[3],
return "str"; ];
}
case "taskbased": suggested = _.findIndex(diff, (val) => {
suggested = _.invert(user.stats.training)[_.max(user.stats.training)]; if (val === _.min(diff)) return true;
_.merge(user.stats.training, { });
str: 0,
int: 0, return suggested !== -1 ? preference[suggested] : 'str';
con: 0, case 'taskbased':
per: 0 suggested = _.invert(user.stats.training)[_.max(user.stats.training)];
});
return suggested || "str"; let training = user.stats.training;
default: training.str = 0;
return "str"; training.int = 0;
} training.con = 0;
})()]++; training.per = 0;
return suggested || 'str';
default:
return 'str';
}
}
module.exports = function autoAllocate (user) {
return user.stats[getStatToAllocate(user)]++;
}; };

View File

@@ -108,6 +108,7 @@ import buy from './ops/buy';
import buyMysterySet from './ops/buyMysterySet'; import buyMysterySet from './ops/buyMysterySet';
import buyQuest from './ops/buyQuest'; import buyQuest from './ops/buyQuest';
import buySpecialSpell from './ops/buySpecialSpell'; import buySpecialSpell from './ops/buySpecialSpell';
import allocateNow from './ops/allocateNow';
api.ops = { api.ops = {
scoreTask, scoreTask,
@@ -117,18 +118,21 @@ api.ops = {
buyMysterySet, buyMysterySet,
buySpecialSpell, buySpecialSpell,
buyQuest, buyQuest,
allocateNow,
}; };
import handleTwoHanded from './fns/handleTwoHanded'; import handleTwoHanded from './fns/handleTwoHanded';
import predictableRandom from './fns/predictableRandom'; import predictableRandom from './fns/predictableRandom';
import randomVal from './fns/randomVal'; import randomVal from './fns/randomVal';
import ultimateGear from './fns/ultimateGear'; import ultimateGear from './fns/ultimateGear';
import autoAllocate from './fns/autoAllocate';
api.fns = { api.fns = {
handleTwoHanded, handleTwoHanded,
predictableRandom, predictableRandom,
randomVal, randomVal,
ultimateGear, ultimateGear,
autoAllocate,
}; };
@@ -191,7 +195,6 @@ api.wrap = function wrapUser (user, main = true) {
reset: _.partial(importedOps.reset, user), reset: _.partial(importedOps.reset, user),
reroll: _.partial(importedOps.reroll, user), reroll: _.partial(importedOps.reroll, user),
rebirth: _.partial(importedOps.rebirth, user), rebirth: _.partial(importedOps.rebirth, user),
allocateNow: _.partial(importedOps.allocateNow, user),
clearCompleted: _.partial(importedOps.clearCompleted, user), clearCompleted: _.partial(importedOps.clearCompleted, user),
sortTask: _.partial(importedOps.sortTask, user), sortTask: _.partial(importedOps.sortTask, user),
updateTask: _.partial(importedOps.updateTask, user), updateTask: _.partial(importedOps.updateTask, user),
@@ -222,7 +225,6 @@ api.wrap = function wrapUser (user, main = true) {
unlock: _.partial(importedOps.unlock, user), unlock: _.partial(importedOps.unlock, user),
changeClass: _.partial(importedOps.changeClass, user), changeClass: _.partial(importedOps.changeClass, user),
disableClasses: _.partial(importedOps.disableClasses, user), disableClasses: _.partial(importedOps.disableClasses, user),
allocate: _.partial(importedOps.allocate, user),
readCard: _.partial(importedOps.readCard, user), readCard: _.partial(importedOps.readCard, user),
openMysteryItem: _.partial(importedOps.openMysteryItem, user), openMysteryItem: _.partial(importedOps.openMysteryItem, user),
scoreTask: _.partial(importedOps.scoreTask, user), scoreTask: _.partial(importedOps.scoreTask, user),
@@ -235,7 +237,6 @@ api.wrap = function wrapUser (user, main = true) {
dotSet: _.partial(importedFns.dotSet, user), dotSet: _.partial(importedFns.dotSet, user),
dotGet: _.partial(importedFns.dotGet, user), dotGet: _.partial(importedFns.dotGet, user),
randomDrop: _.partial(importedFns.randomDrop, user), randomDrop: _.partial(importedFns.randomDrop, user),
autoAllocate: _.partial(importedFns.autoAllocate, user),
updateStats: _.partial(importedFns.updateStats, user), updateStats: _.partial(importedFns.updateStats, user),
cron: _.partial(importedFns.cron, user), cron: _.partial(importedFns.cron, user),
preenUserHistory: _.partial(importedFns.preenUserHistory, user), preenUserHistory: _.partial(importedFns.preenUserHistory, user),

View File

@@ -1,10 +1,10 @@
import _ from 'lodash'; import _ from 'lodash';
import autoAllocate from '../fns/autoAllocate';
module.exports = function(user, req, cb) { module.exports = function allocateNow (user) {
_.times(user.stats.points, user.fns.autoAllocate); _.times(user.stats.points, () => autoAllocate(user));
user.stats.points = 0; user.stats.points = 0;
if (typeof user.markModified === "function") { return {
user.markModified('stats'); data: _.pick(user, 'stats'),
} };
return typeof cb === "function" ? cb(null, user.stats) : void 0;
}; };

View File

@@ -19,7 +19,6 @@ const COMMON_FILES = [
'!./common/script/ops/addTag.js', '!./common/script/ops/addTag.js',
'!./common/script/ops/addTask.js', '!./common/script/ops/addTask.js',
'!./common/script/ops/addWebhook.js', '!./common/script/ops/addWebhook.js',
'!./common/script/ops/allocateNow.js',
'!./common/script/ops/blockUser.js', '!./common/script/ops/blockUser.js',
'!./common/script/ops/changeClass.js', '!./common/script/ops/changeClass.js',
'!./common/script/ops/clearCompleted.js', '!./common/script/ops/clearCompleted.js',
@@ -53,7 +52,6 @@ const COMMON_FILES = [
'!./common/script/ops/updateTag.js', '!./common/script/ops/updateTag.js',
'!./common/script/ops/updateTask.js', '!./common/script/ops/updateTask.js',
'!./common/script/ops/updateWebhook.js', '!./common/script/ops/updateWebhook.js',
'!./common/script/fns/autoAllocate.js',
'!./common/script/fns/crit.js', '!./common/script/fns/crit.js',
'!./common/script/fns/cron.js', '!./common/script/fns/cron.js',
'!./common/script/fns/dotGet.js', '!./common/script/fns/dotGet.js',
@@ -68,7 +66,6 @@ const COMMON_FILES = [
'!./common/script/libs/dotGet.js', '!./common/script/libs/dotGet.js',
'!./common/script/libs/dotSet.js', '!./common/script/libs/dotSet.js',
'!./common/script/libs/encodeiCalLink.js', '!./common/script/libs/encodeiCalLink.js',
'!./common/script/libs/extendableBuiltin.js',
'!./common/script/libs/friendlyTimestamp.js', '!./common/script/libs/friendlyTimestamp.js',
'!./common/script/libs/gold.js', '!./common/script/libs/gold.js',
'!./common/script/libs/newChatMessages.js', '!./common/script/libs/newChatMessages.js',

View File

@@ -0,0 +1,32 @@
import {
generateUser,
} from '../../../../helpers/api-integration/v3';
describe('POST /user/allocate-now', () => {
// More tests in common code unit tests
it('auto allocates all points', async () => {
let user = await generateUser({
'stats.points': 5,
'stats.int': 3,
'stats.con': 9,
'stats.per': 9,
'stats.str': 9,
'preferences.allocationMode': 'flat',
});
let res = await user.post(`/user/allocate-now`);
await user.sync();
expect(res).to.eql({
data: {
stats: user.stats,
},
});
expect(user.stats.points).to.equal(0);
expect(user.stats.con).to.equal(9);
expect(user.stats.int).to.equal(8);
expect(user.stats.per).to.equal(9);
expect(user.stats.str).to.equal(9);
});
});

View File

@@ -0,0 +1,86 @@
import autoAllocate from '../../../common/script/fns/autoAllocate';
import {
generateUser,
} from '../../helpers/common.helper';
describe('shared.fns.autoAllocate', () => {
let user;
beforeEach(() => {
user = generateUser();
});
it('user.preferences.allocationMode === flat', () => {
user.stats.con = 5;
user.stats.int = 5;
user.stats.per = 3;
user.stats.str = 8;
user.preferences.allocationMode = 'flat';
autoAllocate(user);
expect(user.stats.con).to.equal(5);
expect(user.stats.int).to.equal(5);
expect(user.stats.per).to.equal(4);
expect(user.stats.str).to.equal(8);
});
it('user.preferences.allocationMode === taskbased', () => {
user.stats.con = 5;
user.stats.int = 5;
user.stats.per = 3;
user.stats.str = 8;
user.stats.training.con = 2;
user.stats.training.int = 5;
user.stats.training.per = 7;
user.stats.training.str = 4;
user.preferences.allocationMode = 'taskbased';
autoAllocate(user);
expect(user.stats.con).to.equal(5);
expect(user.stats.int).to.equal(5);
expect(user.stats.per).to.equal(4);
expect(user.stats.str).to.equal(8);
expect(user.stats.training.con).to.equal(0);
expect(user.stats.training.int).to.equal(0);
expect(user.stats.training.per).to.equal(0);
expect(user.stats.training.str).to.equal(0);
});
it('user.preferences.allocationMode === classbased', () => {
user.stats.lvl = 35;
user.stats.class = 'healer';
user.stats.con = 5;
user.stats.int = 5;
user.stats.per = 3;
user.stats.str = 8;
user.preferences.allocationMode = 'classbased';
autoAllocate(user);
expect(user.stats.con).to.equal(6);
expect(user.stats.int).to.equal(5);
expect(user.stats.per).to.equal(3);
expect(user.stats.str).to.equal(8);
});
it('user.preferences.allocationMode === anything', () => {
user.stats.con = 5;
user.stats.int = 5;
user.stats.per = 3;
user.stats.str = 8;
user.preferences.allocationMode = 'wrong';
autoAllocate(user);
expect(user.stats.con).to.equal(5);
expect(user.stats.int).to.equal(5);
expect(user.stats.per).to.equal(3);
expect(user.stats.str).to.equal(9);
});
});

View File

@@ -0,0 +1,34 @@
import allocateNow from '../../../common/script/ops/allocateNow';
import {
generateUser,
} from '../../helpers/common.helper';
describe('shared.ops.allocateNow', () => {
let user;
beforeEach(() => {
user = generateUser();
});
it('auto allocates all points', () => {
user.stats.points = 5;
user.stats.int = 3;
user.stats.con = 9;
user.stats.per = 9;
user.stats.str = 9;
user.preferences.allocationMode = 'flat';
let res = allocateNow(user);
expect(user.stats.points).to.equal(0);
expect(user.stats.con).to.equal(9);
expect(user.stats.int).to.equal(8);
expect(user.stats.per).to.equal(9);
expect(user.stats.str).to.equal(9);
expect(res).to.eql({
data: {
stats: user.stats,
},
});
});
});

View File

@@ -196,6 +196,26 @@ api.allocate = {
}, },
}; };
/**
* @api {post} /user/allocate-now Allocate all attribute points.
* @apiVersion 3.0.0
* @apiName UserAllocateNow
* @apiGroup User
*
* @apiSuccess {Object} data `stats`
*/
api.allocateNow = {
method: 'POST',
middlewares: [authWithHeaders(), cron],
url: '/user/allocate-now',
async handler (req, res) {
let user = res.locals.user;
let allocateNowRes = common.ops.allocateNow(user, req);
await user.save();
res.respond(200, allocateNowRes);
},
};
/** /**
* @api {post} /user/buy/:key Buy a content item. * @api {post} /user/buy/:key Buy a content item.
* @apiVersion 3.0.0 * @apiVersion 3.0.0