Merge branch 'api-v3' of github.com:HabitRPG/habitrpg into api-v3

This commit is contained in:
Matteo Pagliazzi
2016-05-16 12:55:14 +02:00
13 changed files with 629 additions and 18 deletions

View File

@@ -594,6 +594,14 @@ api.questMounts = _.transform(api.questEggs, function(m, egg) {
})); }));
}); });
api.premiumMounts = _.transform(api.dropEggs, function(m, egg) {
return _.defaults(m, _.transform(api.hatchingPotions, function(m2, pot) {
if (pot.premium) {
return m2[egg.key + "-" + pot.key] = true;
}
}));
});
api.food = { api.food = {
Meat: { Meat: {
text: t('foodMeat'), text: t('foodMeat'),

View File

@@ -0,0 +1,160 @@
/* eslint-disable camelcase */
import nconf from 'nconf';
import {
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
describe('POST /debug/modify-inventory', () => {
let user, originalItems;
before(async () => {
originalItems = {
gear: { owned: { armor_base_0: true } },
special: {
snowball: 1,
},
pets: {
'Wolf-Desert': 5,
},
mounts: {
'Wolf-Desert': true,
},
eggs: {
Wolf: 5,
},
hatchingPotions: {
Desert: 5,
},
food: {
Watermelon: 5,
},
quests: {
gryphon: 5,
},
};
user = await generateUser({
items: originalItems,
});
});
afterEach(() => {
nconf.set('IS_PROD', false);
});
it('sets equipment', async () => {
let gear = {
weapon_healer_2: true,
weapon_wizard_1: true,
weapon_special_critical: true,
};
await user.post('/debug/modify-inventory', {
gear,
});
await user.sync();
expect(user.items.gear.owned).to.eql(gear);
});
it('sets special spells', async () => {
let special = {
shinySeed: 3,
};
await user.post('/debug/modify-inventory', {
special,
});
await user.sync();
expect(user.items.special).to.eql(special);
});
it('sets mounts', async () => {
let mounts = {
'Orca-Base': true,
'Mammoth-Base': true,
};
await user.post('/debug/modify-inventory', {
mounts,
});
await user.sync();
expect(user.items.mounts).to.eql(mounts);
});
it('sets eggs', async () => {
let eggs = {
Gryphon: 3,
Hedgehog: 7,
};
await user.post('/debug/modify-inventory', {
eggs,
});
await user.sync();
expect(user.items.eggs).to.eql(eggs);
});
it('sets hatching potions', async () => {
let hatchingPotions = {
White: 7,
Spooky: 2,
};
await user.post('/debug/modify-inventory', {
hatchingPotions,
});
await user.sync();
expect(user.items.hatchingPotions).to.eql(hatchingPotions);
});
it('sets food', async () => {
let food = {
Meat: 5,
Candy_Red: 7,
};
await user.post('/debug/modify-inventory', {
food,
});
await user.sync();
expect(user.items.food).to.eql(food);
});
it('sets quests', async () => {
let quests = {
whale: 5,
cheetah: 10,
};
await user.post('/debug/modify-inventory', {
quests,
});
await user.sync();
expect(user.items.quests).to.eql(quests);
});
it('returns error when not in production mode', async () => {
nconf.set('IS_PROD', true);
await expect(user.post('/debug/modify-inventory'))
.eventually.be.rejected.and.to.deep.equal({
code: 404,
error: 'NotFound',
message: 'Not found.',
});
});
});

View File

@@ -0,0 +1,63 @@
import nconf from 'nconf';
import {
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
describe('POST /debug/quest-progress', () => {
let user;
beforeEach(async () => {
user = await generateUser();
});
afterEach(() => {
nconf.set('IS_PROD', false);
});
it('errors if user is not on a quest', async () => {
await expect(user.post('/debug/quest-progress'))
.to.eventually.be.rejected.and.to.deep.equal({
code: 400,
error: 'BadRequest',
message: 'User is not on a valid quest.',
});
});
it('increases boss quest progress by 1000', async () => {
await user.update({
'party.quest.key': 'whale',
});
await user.post('/debug/quest-progress');
await user.sync();
expect(user.party.quest.progress.up).to.eql(1000);
});
it('increases collection quest progress by 300 items', async () => {
await user.update({
'party.quest.key': 'evilsanta2',
});
await user.post('/debug/quest-progress');
await user.sync();
expect(user.party.quest.progress.collect).to.eql({
tracks: 300,
branches: 300,
});
});
it('returns error when not in production mode', async () => {
nconf.set('IS_PROD', true);
await expect(user.post('/debug/quest-progress'))
.eventually.be.rejected.and.to.deep.equal({
code: 404,
error: 'NotFound',
message: 'Not found.',
});
});
});

View File

@@ -118,15 +118,63 @@ function($scope, $rootScope, User, $http, Notification, ApiUrl, Social) {
}); });
}; };
$scope.addBossQuestProgressUp = function(){ $scope.addQuestProgress = function(){
//@TODO: Route? $http({
User.set({ method: "POST",
'party.quest.progress.up': User.user.party.quest.progress.up + 1000 url: 'api/v3/debug/quest-progress'
}); })
.then(function (response) {
Notification.text('Quest progress increased');
User.sync();
})
}; };
$scope.makeAdmin = function () { $scope.makeAdmin = function () {
User.makeAdmin(); User.makeAdmin();
}; };
$scope.openModifyInventoryModal = function () {
$rootScope.openModal('modify-inventory', {controller: 'FooterCtrl', scope: $scope });
$scope.showInv = { };
$scope.inv = {
gear: {},
special: {},
pets: {},
mounts: {},
eggs: {},
hatchingPotions: {},
food: {},
quests: {},
};
$scope.setAllItems = function (type, value) {
var set = $scope.inv[type];
for (var item in set) {
if (set.hasOwnProperty(item)) {
set[item] = value;
}
}
};
};
$scope.modifyInventory = function () {
$http({
method: "POST",
url: 'api/v3/debug/modify-inventory',
data: {
gear: $scope.showInv.gear ? $scope.inv.gear : null,
special: $scope.showInv.special ? $scope.inv.special : null,
pets: $scope.showInv.pets ? $scope.inv.pets : null,
mounts: $scope.showInv.mounts ? $scope.inv.mounts : null,
eggs: $scope.showInv.eggs ? $scope.inv.eggs : null,
hatchingPotions: $scope.showInv.hatchingPotions ? $scope.inv.hatchingPotions : null,
food: $scope.showInv.food ? $scope.inv.food : null,
quests: $scope.showInv.quests ? $scope.inv.quests : null,
}
})
.then(function (response) {
Notification.text('Inventory updated. Refresh or sync.');
})
};
} }
}]) }])

View File

@@ -373,7 +373,7 @@ api.updatePassword = {
}; };
/** /**
* @api {post} /api/v3/user/reset-password Reser password * @api {post} /api/v3/user/reset-password Reset password
* @apiDescription Reset the user password * @apiDescription Reset the user password
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName ResetPassword * @apiName ResetPassword
@@ -427,7 +427,7 @@ api.resetPassword = {
/** /**
* @api {put} /api/v3/user/auth/update-email Update email * @api {put} /api/v3/user/auth/update-email Update email
* @apiDescription Che the user email * @apiDescription Change the user email address
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UpdateEmail * @apiName UpdateEmail
* @apiGroup User * @apiGroup User

View File

@@ -1,5 +1,8 @@
import { authWithHeaders } from '../../middlewares/api-v3/auth'; import { authWithHeaders } from '../../middlewares/api-v3/auth';
import ensureDevelpmentMode from '../../middlewares/api-v3/ensureDevelpmentMode'; import ensureDevelpmentMode from '../../middlewares/api-v3/ensureDevelpmentMode';
import { BadRequest } from '../../libs/api-v3/errors';
import { content } from '../../../../common';
import _ from 'lodash';
let api = {}; let api = {};
@@ -101,4 +104,87 @@ api.setCron = {
// }, // },
// }; // };
/**
* @api {post} /api/v3/debug/modify-inventory Manipulate user's inventory
* @apiDescription Only available in development mode.
* @apiVersion 3.0.0
* @apiName modifyInventory
* @apiGroup Development
*
* @apiSuccess {Object} data An empty Object
*/
api.modifyInventory = {
method: 'POST',
url: '/debug/modify-inventory',
middlewares: [ensureDevelpmentMode, authWithHeaders()],
async handler (req, res) {
let user = res.locals.user;
let { gear } = req.body;
if (gear) {
user.items.gear.owned = gear;
}
[
'special',
'pets',
'mounts',
'eggs',
'hatchingPotions',
'food',
'quests',
].forEach((type) => {
if (req.body[type]) {
user.items[type] = req.body[type];
}
});
await user.save();
res.respond(200, {});
},
};
/**
* @api {post} /api/v3/debug/quest-progress Artificially accelerate quest progress
* @apiDescription Only available in development mode.
* @apiVersion 3.0.0
* @apiName questProgress
* @apiGroup Development
*
* @apiSuccess {Object} data An empty Object
*/
api.questProgress = {
method: 'POST',
url: '/debug/quest-progress',
middlewares: [ensureDevelpmentMode, authWithHeaders()],
async handler (req, res) {
let user = res.locals.user;
let key = _.get(user, 'party.quest.key');
let quest = content.quests[key];
if (!quest) {
throw new BadRequest('User is not on a valid quest.');
}
if (quest.boss) {
user.party.quest.progress.up += 1000;
}
if (quest.collect) {
let collect = user.party.quest.progress.collect;
_.each(quest.collect, (details, item) => {
collect[item] = collect[item] || 0;
collect[item] += 300;
});
}
user.markModified('party.quest.progress');
await user.save();
res.respond(200, {});
},
};
module.exports = api; module.exports = api;

View File

@@ -6,7 +6,7 @@ let tasksModels = ['habit', 'daily', 'todo', 'reward'];
let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels); let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels);
/** /**
* @api {get} /api/v3s/models/:model/paths Get all paths for the specified model. * @api {get} /api/v3/models/:model/paths Get all paths for the specified model.
* @apiDescription Doesn't require authentication * @apiDescription Doesn't require authentication
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName GetUserModelPaths * @apiName GetUserModelPaths

View File

@@ -249,7 +249,7 @@ api.rejectQuest = {
/** /**
* @api {post} /api/v3/groups/:groupId/quests/force-start Accept a pending quest * @api {post} /api/v3/groups/:groupId/quests/force-start Force-start a pending quest
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName ForceQuestStart * @apiName ForceQuestStart
* @apiGroup Group * @apiGroup Group

View File

@@ -54,7 +54,7 @@ async function _createTasks (req, res, user, challenge) {
} }
/** /**
* @api {post} /api/v3/tasks/user Create a new task the user. * @api {post} /api/v3/tasks/user Create a new task belonging to the user.
* @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks.
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName CreateUserTasks * @apiName CreateUserTasks

View File

@@ -177,7 +177,7 @@ api.updateUser = {
}; };
/** /**
* @api {delete} /api/v3/user DELETE an authenticated user's account * @api {delete} /api/v3/user Delete an authenticated user's account
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UserDelete * @apiName UserDelete
* @apiGroup User * @apiGroup User
@@ -240,7 +240,7 @@ function _cleanChecklist (task) {
} }
/** /**
* @api {get} /api/v3/user/anonymized * @api {get} /api/v3/user/anonymized Get anonymized user data
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UserGetAnonymized * @apiName UserGetAnonymized
* @apiGroup User * @apiGroup User
@@ -886,12 +886,12 @@ api.userOpenMysteryItem = {
}; };
/* /*
* @api {post} /api/v3/user/webhook * @api {post} /api/v3/user/webhook Create a new webhook
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UserAddWebhook * @apiName UserAddWebhook
* @apiGroup User * @apiGroup User
* *
* @apiParam {string} url Body parameter - The webhook's urò * @apiParam {string} url Body parameter - The webhook's URL
* @apiParam {boolean} enabled Body parameter - If the webhook should be enabled * @apiParam {boolean} enabled Body parameter - If the webhook should be enabled
* *
* @apiSuccess {Object} data The created webhook * @apiSuccess {Object} data The created webhook
@@ -909,13 +909,13 @@ api.addWebhook = {
}; };
/* /*
* @api {put} /api/v3/user/webhook/:id * @api {put} /api/v3/user/webhook/:id Edit a webhook
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UserUpdateWebhook * @apiName UserUpdateWebhook
* @apiGroup User * @apiGroup User
* *
* @apiParam {UUID} id The id of the webhook to update * @apiParam {UUID} id The id of the webhook to update
* @apiParam {string} url Body parameter - The webhook's urò * @apiParam {string} url Body parameter - The webhook's URL
* @apiParam {boolean} enabled Body parameter - If the webhook should be enabled * @apiParam {boolean} enabled Body parameter - If the webhook should be enabled
* *
* @apiSuccess {Object} data The updated webhook * @apiSuccess {Object} data The updated webhook
@@ -933,7 +933,7 @@ api.updateWebhook = {
}; };
/* /*
* @api {delete} /api/v3/user/webhook/:id * @api {delete} /api/v3/user/webhook/:id Delete a webhook
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UserDeleteWebhook * @apiName UserDeleteWebhook
* @apiGroup User * @apiGroup User

View File

@@ -91,9 +91,10 @@ footer.footer(ng-controller='FooterCtrl')
a.btn.btn-default(ng-click='addMana()') +MP a.btn.btn-default(ng-click='addMana()') +MP
a.btn.btn-default(ng-click='addLevelsAndGold()') +Exp +GP +MP a.btn.btn-default(ng-click='addLevelsAndGold()') +Exp +GP +MP
a.btn.btn-default(ng-click='addOneLevel()') +1 Level a.btn.btn-default(ng-click='addOneLevel()') +1 Level
a.btn.btn-default(ng-click='addBossQuestProgressUp()') +1000 Boss Quest Progress Up a.btn.btn-default(ng-click='addQuestProgress()' tooltip="+1000 to boss quests. 300 items to collection quests") Quest Progress Up
// TODO Re-enable after v3 prod testing // TODO Re-enable after v3 prod testing
// a.btn.btn-default(ng-click='makeAdmin()') Make Admin // a.btn.btn-default(ng-click='makeAdmin()') Make Admin
a.btn.btn-default(ng-click='openModifyInventoryModal()') Modify Inventory
div(ng-init='deferredScripts()') div(ng-init='deferredScripts()')

View File

@@ -19,6 +19,7 @@ include ./level-up.jade
include ./hatch-pet.jade include ./hatch-pet.jade
include ./raise-pet.jade include ./raise-pet.jade
include ./won-challenge.jade include ./won-challenge.jade
include ./modify-inventory.jade
//- Settings //- Settings
script(type='text/ng-template', id='modals/change-day-start.html') script(type='text/ng-template', id='modals/change-day-start.html')

View File

@@ -0,0 +1,244 @@
script(type='text/ng-template', id='modals/modify-inventory.html')
.modal-header
h4 Modify Inventory for {{::user.profile.name}}
.modal-body
.container-fluid
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.gear", ng-click="showInv.gear = true") Show Gear
h4 Gear
div(ng-if="showInv.gear")
button.btn.btn-default(ng-click="setAllItems('gear', true)") Own All
button.btn.btn-default(ng-click="setAllItems('gear', false)") Previously Own All
button.btn.btn-default(ng-click="setAllItems('gear', undefined)") Never Own All
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.gear.flat" ng-init="inv.gear[item.key] = user.items.gear.owned[item.key]")
.pull-left(class="shop_{{::item.key}}" style="margin-right: 10px")
| {{::item.text()}}
.clearfix
label.radio-inline
input(type="radio" name="gear-{{::item.key}}" ng-model="inv.gear[item.key]" ng-value="true")
| Owned
label.radio-inline
input(type="radio" name="gear-{{::item.key}}" ng-model="inv.gear[item.key]" ng-value="false")
| Previously Owned
label.radio-inline
input(type="radio" name="gear-{{::item.key}}" ng-model="inv.gear[item.key]" ng-value="undefined")
| Never Owned
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.special", ng-click="showInv.special = true") Show Special Items
h4 Special Items
div(ng-if="showInv.special")
button.btn.btn-default(ng-click="setAllItems('special', 999)") Set All to 999
button.btn.btn-default(ng-click="setAllItems('special', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('special', undefined)") Set All to undefined
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.special" ng-init="inv.special[item.key] = user.items.special[item.key]")
.form-inline.clearfix
.pull-left(class="inventory_special_{{::item.key}}" style="margin-right: 10px")
p {{::item.text()}}
input.form-control(type="number" ng-model="inv.special[item.key]")
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.pets", ng-click="showInv.pets = true") Show Pets
h4 Pets
div(ng-if="showInv.pets")
button.btn.btn-default(ng-click="setAllItems('pets', 99)") Set All to 99
button.btn.btn-default(ng-click="setAllItems('pets', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('pets', -1)") Set All to -1
button.btn.btn-default(ng-click="setAllItems('pets', undefined)") Set All to undefined
hr
h5 Drop Pets
ul.list-group
li.list-group-item(ng-repeat="(pet, value) in Content.pets" ng-init="inv.pets[pet] = user.items.pets[pet]")
.form-inline.clearfix
.pull-left(class="Pet-{{::pet}}" style="margin-right: 10px")
p {{::pet}}
input.form-control(type="number" ng-model="inv.pets[pet]")
h5 Quest Pets
ul.list-group
li.list-group-item(ng-repeat="(pet, value) in Content.questPets" ng-init="inv.pets[pet] = user.items.pets[pet]")
.form-inline.clearfix
.pull-left(class="Pet-{{::pet}}" style="margin-right: 10px")
p {{::pet}}
input.form-control(type="number" ng-model="inv.pets[pet]")
h5 Special Pets
ul.list-group
li.list-group-item(ng-repeat="(pet, value) in Content.specialPets" ng-init="inv.pets[pet] = user.items.pets[pet]")
.form-inline.clearfix
.pull-left(class="Pet-{{::pet}}" style="margin-right: 10px")
p {{::pet}}
input.form-control(type="number" ng-model="inv.pets[pet]")
h5 Premium Pets
ul.list-group
li.list-group-item(ng-repeat="(pet, value) in Content.premiumPets" ng-init="inv.pets[pet] = user.items.pets[pet]")
.form-inline.clearfix
.pull-left(class="Pet-{{::pet}}" style="margin-right: 10px")
p {{::pet}}
input.form-control(type="number" ng-model="inv.pets[pet]")
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.mounts", ng-click="showInv.mounts = true") Show Mounts
h4 Mounts
div(ng-if="showInv.mounts")
button.btn.btn-default(ng-click="setAllItems('mounts', true)") Set all to Owned
button.btn.btn-default(ng-click="setAllItems('mounts', undefined)") Set all to Not Owned
hr
h5 Drop Mounts
ul.list-group
li.list-group-item(ng-repeat="(mount, value) in Content.mounts" ng-init="inv.mounts[mount] = user.items.mounts[mount]")
.pull-left(class="Mount_Icon_{{::mount}}" style="margin-right: 10px")
| {{::mount}}
.clearfix
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="true")
| Owned
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="undefined")
| Not Owned
h5 Quest Mounts
ul.list-group
li.list-group-item(ng-repeat="(mount, value) in Content.questMounts" ng-init="inv.mounts[mount] = user.items.mounts[mount]")
.pull-left(class="Mount_Icon_{{::mount}}" style="margin-right: 10px")
| {{::mount}}
.clearfix
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="true")
| Owned
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="undefined")
| Not Owned
h5 Special Mounts
ul.list-group
li.list-group-item(ng-repeat="(mount, value) in Content.specialMounts" ng-init="inv.mounts[mount] = user.items.mounts[mount]")
.pull-left(class="Mount_Icon_{{::mount}}" style="margin-right: 10px")
| {{::mount}}
.clearfix
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="true")
| Owned
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="undefined")
| Not Owned
h5 Premium Mounts
ul.list-group
li.list-group-item(ng-repeat="(mount, value) in Content.premiumMounts" ng-init="inv.mounts[mount] = user.items.mounts[mount]")
.pull-left(class="Mount_Icon_{{::mount}}" style="margin-right: 10px")
| {{::mount}}
.clearfix
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="true")
| Owned
label.radio-inline
input(type="radio" name="mounts-{{::mount}}" ng-model="inv.mounts[mount]" ng-value="undefined")
| Not Owned
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.hatchingPotions", ng-click="showInv.hatchingPotions = true") Show Hatching Potions
h4 Hatching Potions
div(ng-if="showInv.hatchingPotions")
button.btn.btn-default(ng-click="setAllItems('hatchingPotions', 999)") Set All to 999
button.btn.btn-default(ng-click="setAllItems('hatchingPotions', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('hatchingPotions', undefined)") Set All to undefined
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.hatchingPotions" ng-init="inv.hatchingPotions[item.key] = user.items.hatchingPotions[item.key]")
.form-inline.clearfix
.pull-left(class="Pet_HatchingPotion_{{::item.key}}" style="margin-right: 10px")
p {{::item.text()}}
input.form-control(type="number" ng-model="inv.hatchingPotions[item.key]")
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.eggs", ng-click="showInv.eggs = true") Show Eggs
h4 Eggs
div(ng-if="showInv.eggs")
button.btn.btn-default(ng-click="setAllItems('eggs', 999)") Set All to 999
button.btn.btn-default(ng-click="setAllItems('eggs', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('eggs', undefined)") Set All to undefined
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.eggs" ng-init="inv.eggs[item.key] = user.items.eggs[item.key]")
.form-inline.clearfix
.pull-left(class="Pet_Egg_{{::item.key}}" style="margin-right: 10px")
p {{::item.text()}}
input.form-control(type="number" ng-model="inv.eggs[item.key]")
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.food", ng-click="showInv.food = true") Show Food
h4 Food
div(ng-if="showInv.food")
button.btn.btn-default(ng-click="setAllItems('food', 999)") Set All to 999
button.btn.btn-default(ng-click="setAllItems('food', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('food', undefined)") Set All to undefined
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.food" ng-init="inv.food[item.key] = user.items.food[item.key]")
.form-inline.clearfix
.pull-left(class="Pet_Food_{{::item.key}}" style="margin-right: 10px")
p {{::item.text()}}
input.form-control(type="number" ng-model="inv.food[item.key]")
hr
.row
.col-xs-12
button.btn.btn-default.pull-right(ng-if="!showInv.quests", ng-click="showInv.quests = true") Show Quests
h4 Quests
div(ng-if="showInv.quests")
button.btn.btn-default(ng-click="setAllItems('quests', 999)") Set All to 999
button.btn.btn-default(ng-click="setAllItems('quests', 0)") Set All to 0
button.btn.btn-default(ng-click="setAllItems('quests', undefined)") Set All to undefined
hr
ul.list-group
li.list-group-item(ng-repeat="item in Content.quests" ng-init="inv.quests[item.key] = user.items.quests[item.key]" ng-if="item.category !== 'world'")
.form-inline.clearfix
.pull-left(class="inventory_quest_scroll_{{::item.key}}" style="margin-right: 10px")
p {{::item.text()}}
input.form-control(type="number" ng-model="inv.quests[item.key]")
.modal-footer
button.btn.btn-default(ng-click="$close()")=env.t('close')
button.btn.btn-primary(ng-click="$close();modifyInventory()") Apply Changes