diff --git a/migrations/20131109_refactor_pets.js b/migrations/20131109_refactor_pets.js new file mode 100644 index 0000000000..1e85ff8fb2 --- /dev/null +++ b/migrations/20131109_refactor_pets.js @@ -0,0 +1,34 @@ +db.users.find({},{backer: 1, items:1}).forEach(function(user){ + user.items = { + armor: +user.items.armor || 0, + weapon: +user.items.weapon || 0, + head: +user.items.head || 0, + shield: +user.items.shield || 0, + + pets: _.reduce(user.items.pets, function(m,v){ m[v] = 5; return m;}, {}), + currentPet: user.items.currentPet ? user.items.currentPet.str : '', + eggs: _.reduce(user.items.eggs, function(m,v){ + if (!m[v.name]) m[v.name] = 0; + m[v.name]++; + return m; + }, {}), + + hatchingPotions: _.reduce(user.items.hatchingPotions, function(m,v){ + if (!m[v]) m[v] = 0; + m[v]++; + return m; + }, {}), + + food: {}, + + mounts: {}, + currentMount: '', + + lastDrop: user.items.lastDrop || {date: new Date(), count: 0} + }; + if (user.backer && user.backer.tier && user.backer.tier >= 90) { + user.items.mounts['LionCub-Ethereal'] = true; + } + + db.users.update({_id:user._id}, {$set:{items:user.items}}); +}); diff --git a/package.json b/package.json index 8d7d1f36d8..9d1794abd6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0-152", "main": "./src/server.js", "dependencies": { - "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#master", + "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#mounts", "derby-auth": "git://github.com/lefnire/derby-auth#master", "connect-mongo": "*", "passport-facebook": "~1.0.0", diff --git a/public/css/inventory.styl b/public/css/inventory.styl index fe5f84fd22..f9ed52f076 100644 --- a/public/css/inventory.styl +++ b/public/css/inventory.styl @@ -52,23 +52,40 @@ menu.pets div padding-right:20px padding-bottom:20px -.Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Veteran, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base, .Pet_Food_SugarCube, .Pet_Food_Strawberry, .Pet_Food_Rotten, .Pet_Food_Licorice, .Pet_Food_Golden, .Pet_Food_Cream, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Chocolate, .Pet_Food_Base, .Pet_Food_Zombie, .Pet_Food_White, .Pet_Food_Skeleton, .Pet_Food_Shade, .Pet_Food_Red, .Pet_Food_Golden, .Pet_Food_Desert, .Pet_Food_CottonCandyPink, .Pet_Food_CottonCandyBlue, .Pet_Food_Base - background: url("/bower_components/habitrpg-shared/img/hatching_powder.png") no-repeat - width:34px - height:34px +// we'll remove this and place it into habitrpg-shared when we have art to work with +menu button[class*="Pet_Food_"] + width:48px + height:53px + margin-left: 1.25em .Pet_Currency_Gem, .Pet_Currency_Gem2x, .Pet_Currency_Gem1x background: url("/bower_components/habitrpg-shared/img/sprites/Egg_Sprite_Sheet.png") no-repeat display:block +.food-tray + position:fixed + right:0px + bottom:0px + width:30% + height: 50% + overflow-y: scroll + .Pet_Currency_Gem {background-position: 0px -510px; width: 51px; height: 45px} /* Not an egg or potion so has a different size */ .Pet_Currency_Gem2x {background-position: -55px -513px; width: 34px; height: 30px} .Pet_Currency_Gem1x {background-position: -63px -542px; width: 19px; height: 17px} -.inventory-list p +.inventory-list + p //display: none -.inventory-list li + li clear:both + button.customize-option + position:relative + .stack-count + position:absolute + bottom:-6px + right:-9px + .pets-menu > div display:inline-block vertical-align:top @@ -90,6 +107,17 @@ menu.pets div width:6em margin-top:-.5em +// This adds feeding progress bars to pets. If we have any issues with `menu.pets > button`, revisit +menu.pets .customize-menu + button + position: relative + .progress + width: 60% + position: absolute + bottom: -25px + left: 20% + height: 5px + .pet-button border: none background: none white @@ -106,6 +134,7 @@ menu.pets div .selectableInventory background-color: lightgreen !important + border-radius: 50% .sell-inventory width: 162px diff --git a/public/js/app.js b/public/js/app.js index bfb24d5175..359a76dfdc 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -30,6 +30,7 @@ window.habitrpg = angular.module('habitrpg', .when('/options/groups', '/options/groups/tavern') .when('/options/groups/guilds', '/options/groups/guilds/public') .when('/options/inventory', '/options/inventory/inventory') + .when('/options/inventory/stable', '/options/inventory/stable/pets') // redirect states that don't match .otherwise("/tasks"); @@ -134,7 +135,8 @@ window.habitrpg = angular.module('habitrpg', // Options > Inventory .state('options.inventory', { url: '/inventory', - templateUrl: "partials/options.inventory.html" + templateUrl: "partials/options.inventory.html", + controller: 'InventoryCtrl' }) .state('options.inventory.inventory', { url: '/inventory', @@ -144,6 +146,14 @@ window.habitrpg = angular.module('habitrpg', url: '/stable', templateUrl: "partials/options.inventory.stable.html" }) + .state('options.inventory.stable.pets', { + url: '/pets', + templateUrl: "partials/options.inventory.stable.pets.html" + }) + .state('options.inventory.stable.mounts', { + url: '/mounts', + templateUrl: "partials/options.inventory.stable.mounts.html" + }) // Options > Settings .state('options.settings', { diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 6dc8d16955..ce090d18ec 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -1,18 +1,31 @@ -habitrpg.controller("InventoryCtrl", ['$scope', 'User', - function($scope, User) { +habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', '$http', 'Notification', + function($rootScope, $scope, User, API_URL, $http, Notification) { + + var user = User.user; // convenience vars since these are accessed frequently - $scope.userEggs = User.user.items.eggs; - $scope.userHatchingPotions = User.user.items.hatchingPotions; $scope.selectedEgg = null; // {index: 1, name: "Tiger", value: 5} $scope.selectedPotion = null; // {index: 5, name: "Red", value: 3} + $scope.petCount = _.size(User.user.items.pets); + $scope.totalPets = _.size($scope.Items.eggs) * _.size($scope.Items.hatchingPotions); - $scope.chooseEgg = function(egg, $index){ - if ($scope.selectedEgg && $scope.selectedEgg.index == $index) { + // count egg, food, hatchingPotion stack totals + var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);} + // count pets, mounts collected totals + var countExists = function(items) { return _.reduce(items,function(m,v){return m+(v ? 1 : 0);},0);} + + $scope.$watch('user.items.pets', function(pets){ $scope.petCount = countExists(pets); }); + $scope.$watch('user.items.mounts', function(mounts){ $scope.mountCount = countExists(mounts); }); + $scope.$watch('user.items.eggs', function(eggs){ $scope.eggCount = countStacks(eggs); }); + $scope.$watch('user.items.hatchingPotions', function(pots){ $scope.potCount = countStacks(pots); }); + $scope.$watch('user.items.food', function(food){ $scope.foodCount = countStacks(food); }); + + $scope.chooseEgg = function(egg){ + if ($scope.selectedEgg && $scope.selectedEgg.name == egg) { return $scope.selectedEgg = null; // clicked same egg, unselect } - var eggData = _.defaults({index:$index}, egg); + var eggData = _.findWhere(window.habitrpgShared.items.items.eggs, {name:egg}); if (!$scope.selectedPotion) { $scope.selectedEgg = eggData; } else { @@ -20,13 +33,12 @@ habitrpg.controller("InventoryCtrl", ['$scope', 'User', } } - $scope.choosePotion = function(potion, $index){ - if ($scope.selectedPotion && $scope.selectedPotion.index == $index) { + $scope.choosePotion = function(potion){ + if ($scope.selectedPotion && $scope.selectedPotion.name == potion) { return $scope.selectedPotion = null; // clicked same egg, unselect } // we really didn't think through the way these things are stored and getting passed around... var potionData = _.findWhere(window.habitrpgShared.items.items.hatchingPotions, {name:potion}); - potionData = _.defaults({index:$index}, potionData); if (!$scope.selectedEgg) { $scope.selectedPotion = potionData; } else { @@ -34,54 +46,53 @@ habitrpg.controller("InventoryCtrl", ['$scope', 'User', } } + $scope.chooseFood = function(food){ + if ($scope.selectedFood && $scope.selectedFood.name == food) return $scope.selectedFood = null; + $scope.selectedFood = $scope.Items.food[food]; + } + $scope.sellInventory = function() { + // TODO DRY this if ($scope.selectedEgg) { - $scope.userEggs.splice($scope.selectedEgg.index, 1); + user.items.eggs[$scope.selectedEgg.name]--; User.setMultiple({ - 'items.eggs': $scope.userEggs, + 'items.eggs': user.items.eggs, 'stats.gp': User.user.stats.gp + $scope.selectedEgg.value }); $scope.selectedEgg = null; } else if ($scope.selectedPotion) { - $scope.userHatchingPotions.splice($scope.selectedPotion.index, 1); + user.items.hatchingPotions[$scope.selectedPotion.name]--; User.setMultiple({ - 'items.hatchingPotions': $scope.userHatchingPotions, + 'items.hatchingPotions': user.items.hatchingPotions, 'stats.gp': User.user.stats.gp + $scope.selectedPotion.value }); $scope.selectedPotion = null; + } else if ($scope.selectedFood) { + user.items.food[$scope.selectedFood.name]--; + User.setMultiple({ + 'items.food': user.items.food, + 'stats.gp': User.user.stats.gp + $scope.selectedFood.value + }); + $scope.selectedFood = null; } + } - $scope.ownsPet = function(egg, potion){ - if (!egg || !potion) return; - var pet = egg.name + '-' + potion; - return User.user.items.pets && ~User.user.items.pets.indexOf(pet) - } - - $scope.selectableInventory = function(egg, potion, $index) { - if (!egg || !potion) return; - // FIXME this isn't updating the view for some reason - //if ($scope.selectedEgg && $scope.selectedEgg.index == $index) return 'selectableInventory'; - //if ($scope.selectedPotion && $scope.selectedPotion.index == $index) return 'selectableInventory'; - if (!$scope.ownsPet(egg, potion)) return 'selectableInventory'; + $scope.ownedItems = function(inventory){ + return _.pick(inventory, function(v,k){return v>0;}); } $scope.hatch = function(egg, potion){ - if ($scope.ownsPet(egg, potion.name)){ - return alert("You already have that pet, hatch a different combo.") - } - var pet = egg.name + '-' + potion.name; - $scope.userEggs.splice(egg.index, 1); - $scope.userHatchingPotions.splice(potion.index, 1); + var pet = egg.name+"-"+potion.name; + if (user.items.pets[pet]) + return alert("You already have that pet, hatch a different combo."); - if(!User.user.items.pets) User.user.items.pets = []; - User.user.items.pets.push(pet); + var setObj = {}; + setObj['items.pets.' + pet] = 5; + setObj['items.eggs.' + egg.name] = user.items.eggs[egg.name] - 1; + setObj['items.hatchingPotions.' + potion.name] = user.items.hatchingPotions[potion.name] - 1; - User.log([ - { op: 'set', data: {'items.pets': User.user.items.pets} }, - { op: 'set', data: {'items.eggs': $scope.userEggs} }, - { op: 'set', data: {'items.hatchingPotions': $scope.userHatchingPotions} } - ]); + User.setMultiple(setObj); alert("Your egg hatched! Visit your stable to equip your pet."); @@ -89,4 +100,71 @@ habitrpg.controller("InventoryCtrl", ['$scope', 'User', $scope.selectedPotion = null; } - }]); \ No newline at end of file + $scope.buy = function(type, item){ + var gems = User.user.balance * 4; + if(gems < item.value) return $rootScope.modals.buyGems = true; + var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space + var message = "Buy this " + string + " with " + item.value + " of your " + gems + " Gems?" + if(confirm(message)){ + $http.post(API_URL + '/api/v1/market/buy?type=' + type, item) + .success(function(data){ + User.user.items = data.items; + }); + } + } + + $scope.choosePet = function(egg, potion){ + var pet = egg + '-' + potion; + + // Feeding Pet + if ($scope.selectedFood) { + var setObj = {}; + var userPets = user.items.pets; + if (user.items.mounts[pet] && (userPets[pet] >= 50 || $scope.selectedFood.name == 'Saddle')) + return Notification.text("You already have that mount"); + + var evolve = function(){ + userPets[pet] = 0; + setObj['items.mounts.' + pet] = true; + if (pet == user.items.currentPet) setObj['items.currentPet'] = ''; + Notification.text('You have tamed '+egg+", let's go for a ride!"); + } + // Saddling a pet + if ($scope.selectedFood.name == 'Saddle') { + if (!confirm('Saddle ' + pet + '?')) return; + evolve(); + } else { + if (!confirm('Feed ' + pet + ' a ' + $scope.selectedFood.name + '?')) return; + if ($scope.selectedFood.target == potion) { + userPets[pet] += 5; + Notification.text(egg+' really likes the '+$scope.selectedFood.name+'!'); + } else { + userPets[pet] += 2; + Notification.text(egg+' eats the '+$scope.selectedFood.name+" but doesn't seem to enjoy it."); + } + if (userPets[pet] >= 50 && !user.items.mounts[pet]) evolve(); + } + setObj['items.pets.' + pet] = userPets[pet]; + setObj['items.food.' + $scope.selectedFood.name] = user.items.food[$scope.selectedFood.name] - 1; + User.setMultiple(setObj); + $scope.selectedFood = null; + + // Selecting Pet + } else { + var userCurrentPet = User.user.items.currentPet; + if(userCurrentPet && userCurrentPet == pet){ + User.user.items.currentPet = ''; + }else{ + User.user.items.currentPet = pet; + } + User.set('items.currentPet', User.user.items.currentPet); + } + } + + $scope.chooseMount = function(egg, potion) { + var mount = egg + '-' + potion; + User.set('items.currentMount', (user.items.currentMount == mount) ? '' : mount); + } + + } +]); \ No newline at end of file diff --git a/public/js/controllers/marketCtrl.js b/public/js/controllers/marketCtrl.js deleted file mode 100644 index c0cf429539..0000000000 --- a/public/js/controllers/marketCtrl.js +++ /dev/null @@ -1,37 +0,0 @@ -habitrpg.controller("MarketCtrl", ['$rootScope', '$scope', 'User', 'API_URL', '$http', - function($rootScope, $scope, User, API_URL, $http) { - - $scope.eggs = window.habitrpgShared.items.items.pets; - $scope.hatchingPotions = window.habitrpgShared.items.items.hatchingPotions; - $scope.userEggs = User.user.items.eggs; - $scope.userHatchingPotions = User.user.items.hatchingPotions; - - $scope.buy = function(type, item){ - var gems = User.user.balance * 4, - store = type === 'egg' ? $scope.userEggs : $scope.userHatchingPotions, - storePath = type === 'egg' ? 'items.eggs' : 'items.hatchingPotions' - - if(gems < item.value){ - return $rootScope.modals.buyGems = true; - } - - var message = "Buy this " + (type == 'egg' ? 'egg' : 'hatching potion') + " with " + item.value + " of your " + gems + " Gems?" - - if(confirm(message)){ - $http.post(API_URL + '/api/v1/market/buy?type=' + type, item) - .success(function(data){ - // don't know what's going on, but trying to work with the returned data (a) isn't updating the ui, (b) isnt' - // stickign between refreshes until a force-refresh is called (user._v--). - User.user._v--; - User.log({}); - //User.user.balance = data.balance; - store.push(type === 'egg' ? item : item.name); - //$scope.items = data.items.eggs; // FIXME this isn't updating the UI - }).error(function(data){ - alert(data); - console.error(data); - }); - } - } - - }]); \ No newline at end of file diff --git a/public/js/controllers/notificationCtrl.js b/public/js/controllers/notificationCtrl.js index 618964f70f..4eb64caa9b 100644 --- a/public/js/controllers/notificationCtrl.js +++ b/public/js/controllers/notificationCtrl.js @@ -43,8 +43,8 @@ habitrpg.controller('NotificationCtrl', $rootScope.modals.achievements.ultimateGear = true; }); - $rootScope.$watch('user.items.pets.length', function(after, before){ - if(after === before || after < 90) return; + $rootScope.$watch('user.items.pets', function(after, before){ + if(_.size(after) === _.size(before) || _.size(after) < 90) return; User.user.achievements.beastMaster = true; $rootScope.modals.achievements.beastMaster = true; }) diff --git a/public/js/controllers/petsCtrl.js b/public/js/controllers/petsCtrl.js deleted file mode 100644 index 7d4e6ba5ed..0000000000 --- a/public/js/controllers/petsCtrl.js +++ /dev/null @@ -1,32 +0,0 @@ -habitrpg.controller("PetsCtrl", ['$scope', 'User', - function($scope, User) { - - $scope.userPets = User.user.items.pets; - $scope.userCurrentPet = User.user.items.currentPet; - $scope.pets = window.habitrpgShared.items.items.pets; - $scope.hatchingPotions = window.habitrpgShared.items.items.hatchingPotions; - $scope.totalPets = $scope.pets.length * $scope.hatchingPotions.length; - - $scope.hasPet = function(name, potion){ - if (!$scope.userPets) return false; - return _.contains($scope.userPets, name + '-' + potion) ? true : false; - } - - $scope.isCurrentPet = function(name, potion){ - if (!$scope.userCurrentPet || !$scope.userPets) return false; - return $scope.userCurrentPet.str === (name + '-' + potion); - } - - $scope.choosePet = function(name, potion){ - if($scope.userCurrentPet && $scope.userCurrentPet.str === (name + '-' + potion)){ - $scope.userCurrentPet = null; - }else{ - var pet = _.find($scope.pets, {name: name}); - pet.modifier = potion; - pet.str = name + '-' + potion; - $scope.userCurrentPet = pet; - } - User.set('items.currentPet', $scope.userCurrentPet); - } - - }]); \ No newline at end of file diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index eab96974a5..98b09b2401 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -10,6 +10,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $rootScope.User = User; $rootScope.user = User.user; $rootScope.settings = User.settings; + $rootScope.Items = window.habitrpgShared.items.items; // Angular UI Router $rootScope.$state = $state; diff --git a/public/js/filters/filters.js b/public/js/filters/filters.js index 65223c5957..9d2255d9e2 100644 --- a/public/js/filters/filters.js +++ b/public/js/filters/filters.js @@ -3,8 +3,9 @@ angular.module('habitrpg') return function (gp) { return Math.floor(gp); } - }).filter('silver', function () { + }) + .filter('silver', function () { return function (gp) { return Math.floor((gp - Math.floor(gp))*100); } - }); + }) \ No newline at end of file diff --git a/public/js/services/guideServices.js b/public/js/services/guideServices.js index 3e709ce4c5..ebb9cadb36 100644 --- a/public/js/services/guideServices.js +++ b/public/js/services/guideServices.js @@ -96,13 +96,6 @@ angular.module('guideServices', []). showPopover('div.rewards', 'Item Store Unlocked', html, 'left'); }); - $rootScope.$watch('user.flags.petsEnabled', function(after, before) { - //TODO is this ever used? I think dropsEnabled is the one that's used - if (alreadyShown(before, after)) return; - var html = "You have unlocked Pets! You can now buy pets with Gems (note, you replenish Gems with real-life money - so chose your pets wisely!)"; - showPopover('#rewardsTabs', 'Pets Unlocked', html, 'left'); - }); - $rootScope.$watch('user.flags.partyEnabled', function(after, before) { if (alreadyShown(before, after)) return; var html = "Be social, join a party and play Habit with your friends! You'll be better at your habits with accountability partners. Click User -> Options -> Party, and follow the instructions. LFG anyone?"; @@ -111,10 +104,8 @@ angular.module('guideServices', []). $rootScope.$watch('user.flags.dropsEnabled', function(after, before) { if (alreadyShown(before, after)) return; - var drop = Helpers.randomVal(Items.items.pets); - var eggs = User.user.items.eggs || []; - eggs.push(drop); // FIXME put this on server instead - User.set('items.eggs', eggs); + var eggs = User.user.items.eggs || {}; + eggs['Wolf'] = 1; // This is also set on the server $rootScope.modals.dropsEnabled = true; }); diff --git a/public/manifest.json b/public/manifest.json index a522b3f140..4cd6bdf6cd 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -50,9 +50,7 @@ "js/controllers/filtersCtrl.js", "js/controllers/userCtrl.js", "js/controllers/groupsCtrl.js", - "js/controllers/petsCtrl.js", "js/controllers/inventoryCtrl.js", - "js/controllers/marketCtrl.js", "js/controllers/footerCtrl.js", "js/controllers/challengesCtrl.js", "js/controllers/adminCtrl.js" diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 5a92b3d986..fea98e6750 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -60,9 +60,7 @@ api.updateMember = function(req, res) { member.balance += (req.body.contributor.level - (member.contributor.level || 0))*.5 // +2 gems per tier } _.merge(member, _.pick(req.body, 'contributor')); - if (!member.items.pets) member.items.pets = []; - var i = member.items.pets.indexOf('Dragon-Hydra'); - if (!~i && member.contributor.level >= 6) member.items.pets.push('Dragon-Hydra'); + if (member.contributor.level >= 6) member.items.pets['Dragon-Hydra'] = 5; member.save(cb); } ], function(err, saved){ diff --git a/src/controllers/user.js b/src/controllers/user.js index b67c61fe42..560b5a7776 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -22,17 +22,11 @@ api.marketBuy = function(req, res, next){ type = req.query.type, item = req.body; - if (!_.contains(['hatchingPotion', 'egg'], req.query.type)) - return res.json(400, {err: "Type must be in 'hatchingPotion' or 'egg'"}); - var item; - if (type == 'egg'){ - if (!user.items && !user.items.eggs) user.items.eggs = []; - user.items.eggs.push(item); - } else { - if (!user.items && !user.items.hatchingPotions) user.items.hatchingPotions = []; - user.items.hatchingPotions.push(item.name); - } - user.markModified('items'); // I still don't get when this is necessary and when not.. + if (!_.contains(['hatchingPotion', 'egg', 'food'], type)) + return res.json(400, {err: "Type must be 'hatchingPotion', 'egg', or 'food'"}); + type = (type == 'food' ? type : type + 's'); // I'm stupid, we're passing up 'hatchingPotion' but we need 'hatchingPotions' + if (!user.items[type][item.name]) user.items[type][item.name] = 0; + user.items[type][item.name]++; user.balance -= (item.value/4); user.save(function(err, saved){ if (err) return res.json(500, {err:err}); @@ -270,30 +264,21 @@ api.updateUser = function(req, res, next) { acceptableAttrs = 'tasks. achievements. filters. flags. invitations. items. lastCron party. preferences. profile. stats. tags custom.'.split(' '); _.each(req.body, function(v, k) { - if ((_.find(acceptableAttrs, function(attr) { - return k.indexOf(attr) === 0; - })) != null) { - if (_.isObject(v)) { - errors.push("Value for " + k + " was an object. Be careful here, you could clobber stuff."); - } + var found = _.find(acceptableAttrs, function(attr) { return k.indexOf(attr) == 0; }) + if (found) { +// if (_.isObject(v)) { +// errors.push("Value for " + k + " was an object. Be careful here, you could clobber stuff."); +// } helpers.dotSet(k, v, user); } else { errors.push("path `" + k + "` was not saved, as it's a protected path. Make sure to send `PUT /api/v1/user` request bodies as `{'set.this.path':value}` instead of `{set:{this:{path:value}}}`"); } return true; }); - return user.save(function(err) { - if (!_.isEmpty(errors)) { - return res.json(500, { - err: errors - }); - } - if (err) { - return res.json(500, { - err: err - }); - } - return res.json(200, user); + user.save(function(err) { + if (!_.isEmpty(errors)) return res.json(500, {err: errors}); + if (err) {return res.json(500, {err: err})} + res.json(200, user); }); }; diff --git a/src/models/user.js b/src/models/user.js index 7d7d766dca..5b22522725 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -7,6 +7,7 @@ var mongoose = require("mongoose"); var Schema = mongoose.Schema; var helpers = require('habitrpg-shared/script/helpers'); +var items = require('habitrpg-shared/script/items'); var _ = require('lodash'); var TaskSchema = require('./task').schema; var Challenge = require('./challenge').model; @@ -14,6 +15,12 @@ var Challenge = require('./challenge').model; // User Schema // ----------- +var eggPotionMapping = _.transform(items.items.eggs, function(m, egg){ + _.defaults(m, _.transform(items.items.hatchingPotions, function(m2, pot){ + m2[egg.name + '-' + pot.name] = true; + })); +}) + var UserSchema = new Schema({ // ### UUID and API Token _id: { @@ -83,7 +90,6 @@ var UserSchema = new Schema({ newStuff: {type: Boolean, 'default': false}, rewrite: {type: Boolean, 'default': true}, partyEnabled: Boolean, // FIXME do we need this? - petsEnabled: {type: Boolean, 'default': false}, rest: {type: Boolean, 'default': false}, // fixme - change to preferences.resting once we're off derby contributor: Boolean }, @@ -92,7 +98,7 @@ var UserSchema = new Schema({ todos: Array //[{data: Date, value: Number}] // big peformance issues if these are defined }, - /* FIXME remove?*/ + // FIXME remove? invitations: { guilds: {type: Array, 'default': []}, party: Schema.Types.Mixed @@ -103,52 +109,65 @@ var UserSchema = new Schema({ head: Number, shield: Number, - /*FIXME - tidy this up, not the best way to store current pet*/ + // -------------- Animals ------------------- - currentPet: { - /*Cactus*/ - - text: String, - /*Cactus*/ - - name: String, - /*3*/ - - value: Number, - /*"Find a hatching potion to pour on this egg, and one day it will hatch into a loyal pet.",*/ - - notes: String, - /*Skeleton*/ - - modifier: String, - /*Cactus-Skeleton*/ - - str: String - }, - - eggs: [ + // Complex bit here. The result looks like: + // pets: { + // 'Wolf-Desert': 0, // 0 means does not own + // 'PandaCub-Red': 10, // Number represents "Growth Points" + // etc... + // } + pets: + _.defaults( + // First transform to a 1D eggs/potions mapping + _.transform(eggPotionMapping, function(m,v,k){ m[k] = Number; }), + // Then add additional pets (backer, contributor) { - // example: You've found a Wolf Egg! Find a hatching potion to pour on this egg, and one day it will hatch into a loyal pet - dialog: String, - // example: Wolf - name: String, - // example: Find a hatching potion to pour on this egg, and one day it will hatch into a loyal pet. - notes: String, - // example: Wolf - text: String, - /* type: String, //Egg // this is forcing mongoose to return object as "[object Object]", but I don't think this is needed anyway? */ - // example: 3 - value: Number + 'Wolf-Veteran': Number, + 'Wolf-Cerberus': Number, + 'Dragon-Hydra': Number } - ], - hatchingPotions: Array, // ["Base", "Skeleton",...] + ), + currentPet: String, // Cactus-Desert + + // eggs: { + // 'PandaCub': 0, // 0 indicates "doesn't own" + // 'Wolf': 5 // Number indicates "stacking" + // } + eggs: _.transform(items.items.eggs, function(m,v,k){ m[k] = Number; }), + + // hatchingPotions: { + // 'Desert': 0, // 0 indicates "doesn't own" + // 'CottonCandyBlue': 5 // Number indicates "stacking" + // } + hatchingPotions: _.transform(items.items.hatchingPotions, function(m,v,k){ m[k] = Number; }), + + // Food: { + // 'Watermelon': 0, // 0 indicates "doesn't own" + // 'RottenMeat': 5 // Number indicates "stacking" + // } + food: _.transform(items.items.food, function(m,v,k){ m[k] = Number; }), + + // mounts: { + // 'Wolf-Desert': true, + // 'PandaCub-Red': false, + // etc... + // } + mounts: _.defaults( + // First transform to a 1D eggs/potions mapping + _.transform(eggPotionMapping, function(m,v,k){ m[k] = Boolean; }), + // Then add additional pets (backer, contributor) + { + 'LionCub-Ethereal': Boolean, + 'BearCub-Polar': Boolean + } + ), + currentMount: String, + lastDrop: { date: {type: Date, 'default': Date.now}, count: {type: Number, 'default': 0} - }, - // ["BearCub-Base", "Cactus-Base", ...] - - pets: Array + } }, lastCron: { diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 37be78f7b2..febf372a48 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -1,25 +1,38 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') - .row-fluid(ng-controller='InventoryCtrl') + .row-fluid .span6.border-right h2 Inventory p.well Click an egg to see usable potions highlighted in green and then click one of the highlighted potions to hatch your pet. If no potions are highlighted, click that egg again to deselect it, and instead click a potion first to have the usable eggs highlighted. You can also sell unwanted drops to Alexander the Merchant. menu.inventory-list(type='list') + li.customize-menu - menu.pets-menu(label='Eggs ({{userEggs.length}})') - p(ng-show='userEggs.length < 1') You don't have any eggs yet. - div(ng-repeat='egg in userEggs track by $index') - button.customize-option(tooltip='{{egg.text}}', ng-click='chooseEgg(egg, $index)', class='Pet_Egg_{{egg.name}}', ng-class='selectableInventory(egg, selectedPotion.name, $index)') - p {{egg.text}} + menu.pets-menu(label='Eggs ({{eggCount}})') + p(ng-show='user.items.eggs.length < 1') You don't have any eggs yet. + div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)') + //TODO move positioning this styling to css + button.customize-option(popover='{{Items.eggs[egg].notes}}', popover-title='{{Items.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !user.items.pets[egg+"-"+selectedPotion.name]}') + .badge.badge-info.stack-count {{points}} + //-p {{Items.eggs[egg].text}} + li.customize-menu - menu.hatchingPotion-menu(label='Hatching Potions ({{userHatchingPotions.length}})') - p(ng-show='userHatchingPotions.length < 1') You don't have any hatching potions yet. - div(ng-repeat='hatchingPotion in userHatchingPotions track by $index') - button.customize-option(tooltip='{{hatchingPotion}}', ng-click='choosePotion(hatchingPotion, $index)', class='Pet_HatchingPotion_{{hatchingPotion}}', ng-class='selectableInventory(selectedEgg, hatchingPotion, $index)') - p {{hatchingPotion}} + menu.hatchingPotion-menu(label='Hatching Potions ({{potCount}})') + p(ng-show='potCount < 1') You don't have any hatching potions yet. + div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)') + button.customize-option(popover='{{Items.hatchingPotions[pot].notes}}', popover-title='{{Items.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !user.items.pets[selectedEgg.name+"-"+pot]}') + .badge.badge-info.stack-count {{points}} + //-p {{pot}} + + //-li.customize-menu + menu.pets-menu(label='Food ({{foodCount}})') + p(ng-show='foodCount < 1') You don't have any food yet. + div(ng-repeat='(food,points) in ownedItems(user.items.food)') + button.customize-option(popover='{{Items.food[food].notes}}', popover-title='{{Items.food[food].text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + .badge.badge-info.stack-count {{points}} + //-p {{food}} .span6 h2 Market - .row-fluid(ng-controller='MarketCtrl') + .row-fluid table.NPC-Alex-container tr td @@ -37,19 +50,33 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') | Sell {{selectedEgg.name}} for {{selectedEgg.value}} GP button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()') | Sell {{selectedPotion.name}} for {{selectedPotion.value}} GP + button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()') + | Sell {{selectedFood.name}} for {{selectedFood.value}} GP menu.inventory-list(type='list') li.customize-menu menu.pets-menu(label='Eggs') - div(ng-repeat='egg in eggs track by $index') - button.customize-option(tooltip='{{egg.text}} - {{egg.value}} Gem(s)', ng-click='buy("egg", egg)', class='Pet_Egg_{{egg.name}}') - p {{egg.text}} + div(ng-repeat='egg in Items.eggs') + button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("egg", egg)', class='Pet_Egg_{{egg.name}}') + p + | {{egg.value}} + span.Pet_Currency_Gem1x.inline-gems li.customize-menu menu.pets-menu(label='Hatching Potions') - div(ng-repeat='hatchingPotion in hatchingPotions track by $index') - button.customize-option(tooltip='{{hatchingPotion.text}} - {{hatchingPotion.value}} Gem(s)', ng-click='buy("hatchingPotion", hatchingPotion)', class='Pet_HatchingPotion_{{hatchingPotion.name}}') - p {{hatchingPotion.text}} + div(ng-repeat='pot in Items.hatchingPotions') + button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("hatchingPotion", pot)', class='Pet_HatchingPotion_{{pot.name}}') + p + | {{pot.value}} + span.Pet_Currency_Gem1x.inline-gems + + //-li.customize-menu + menu.pets-menu(label='Food') + div(ng-repeat='food in Items.food') + button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("food", food)', class='Pet_Food_{{food.name}}') + p + | {{food.value}} + span.Pet_Currency_Gem1x.inline-gems diff --git a/views/options/inventory/stable.jade b/views/options/inventory/stable.jade index dae0906ea8..a261412c36 100644 --- a/views/options/inventory/stable.jade +++ b/views/options/inventory/stable.jade @@ -1,33 +1,84 @@ script(type='text/ng-template', id='partials/options.inventory.stable.html') - .stable(ng-controller='PetsCtrl') + div(ui-view) + //- + ul.nav.nav-tabs + li(ng-class="{ active: $state.includes('options.inventory.stable.pets') }") + a(ui-sref='options.inventory.stable.pets') + | Pets + li(ng-class="{ active: $state.includes('options.inventory.stable.mounts') }") + a(ui-sref='options.inventory.stable.mounts') + | Mounts + .tab-content + .tab-pane.active + div(ui-view) + +script(type='text/ng-template', id='partials/options.inventory.stable.mounts.html') + .stable .NPC-Matt .popover.static-popover.fade.right.in(style='max-width: 550px; margin-left: 10px;') .arrow h3.popover-title a(target='_blank', href='http://www.kickstarter.com/profile/mattboch') Matt Boch .popover-content - p - | Welcome to the Stable! I'm Matt, the beast master. Choose a pet here to venture at your side. They aren't much help yet, but I forsee a time when they're able to - a(href='https://trello.com/card/mounts/50e5d3684fe3a7266b0036d6/221') grow into powerful steeds - | ! Until that day, - a(target='_blank', href='https://f.cloud.github.com/assets/2374703/164631/3ed5fa6c-78cd-11e2-8743-f65ac477b55e.png') have a look-see - | at all the pets you can collect. - h4 {{userPets.length}} / {{totalPets}} Pets Found + p. + Shall I bring you your steed, {{user.profile.name}}? Click a mount to saddle up. + h4 {{mountCount}} / {{totalPets}} Mounts Tamed + menu.pets(type='list') + li.customize-menu(ng-repeat='egg in Items.eggs') + menu + div(ng-repeat='potion in Items.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', ng-init='mount = egg.name+"-"+potion.name') + button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.name, potion.name)') + //div(class='Mount_Head_{{mount}}') + button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]') + img(src='/bower_components/habitrpg-shared/img/PixelPaw.png') + + h4 Rare Mounts + menu + div + button(ng-if='user.items.mounts["BearCub-Polar"]', class="pet-button Mount_Head_BearCub-Polar", ng-class='{active: user.items.currentMount == "BearCub-Polar"}', ng-click='chooseMount("BearCub", "Polar")', tooltip='Polar Bear') + //.Mount_Head_BearCub-Polar + button(ng-if='user.items.mounts["LionCub-Ethereal"]', class="pet-button Mount_Head_LionCub-Ethereal", ng-class='{active: user.items.currentMount == "LionCub-Ethereal"}', ng-click='chooseMount("LionCub", "Ethereal")', tooltip='Ethereal Lion') + //.Mount_Head_LionCub-Ethereal + +script(type='text/ng-template', id='partials/options.inventory.stable.pets.html') + .stable + .NPC-Matt + .popover.static-popover.fade.right.in(style='max-width: 550px; margin-left: 10px;') + .arrow + h3.popover-title + a(target='_blank', href='http://www.kickstarter.com/profile/mattboch') Matt Boch + .popover-content + p. + Welcome to the Stable! I'm Matt, the beast master. Choose a pet here to venture at your side. Have a look-see at all the pets you can collect. + h4 {{petCount}} / {{totalPets}} Pets Found menu.pets(type='list') - li.customize-menu(ng-repeat='pet in pets') + li.customize-menu(ng-repeat='egg in Items.eggs') menu - div(ng-repeat='potion in hatchingPotions', tooltip='{{potion.name}} {{pet.name}}') - button(class="pet-button Pet-{{pet.name}}-{{potion.name}}", ng-show='hasPet(pet.name, potion.name)', ng-class="{active: isCurrentPet(pet.name, potion.name)}", ng-click='choosePet(pet.name, potion.name)') - button(class="pet-button pet-not-owned", ng-hide='hasPet(pet.name, potion.name)') + div(ng-repeat='potion in Items.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', ng-init='pet = egg.name+"-"+potion.name') + button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.name, potion.name)') + //-.progress(ng-class='{"progress-success": user.items.pets[pet]<50}') + .bar(style="width: {{user.items.pets[pet]/.5}}%;") + button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]') img(src='/bower_components/habitrpg-shared/img/PixelPaw.png') h4 Rare Pets menu div - button(ng-if='hasPet("Wolf", "Veteran")', class="pet-button Pet-Wolf-Veteran", ng-class='{active: isCurrentPet("Wolf", "Veteran")}', ng-click='choosePet("Wolf", "Veteran")', tooltip='Veteran Wolf') - button(ng-if='hasPet("Wolf", "Cerberus")', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: isCurrentPet("Wolf", "Cerberus")}', ng-click='choosePet("Wolf", "Cerberus")', tooltip='Cerberus Pup') - button(ng-if='hasPet("Dragon", "Hydra")', class="pet-button Pet-Dragon-Hydra", ng-class='{active: isCurrentPet("Dragon", "Hydra")}', ng-click='choosePet("Dragon", "Hydra")', tooltip='Hydra Pet') + button(ng-if='user.items.pets["Wolf-Veteran"]', class="pet-button Pet-Wolf-Veteran", ng-class='{active: user.items.currentPet == "Wolf-Veteran"}', ng-click='choosePet("Wolf", "Veteran")', tooltip='Veteran Wolf') + button(ng-if='user.items.pets["Wolf-Cerberus"]', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: user.items.currentPet == "Wolf-Cerberus"}', ng-click='choosePet("Wolf", "Cerberus")', tooltip='Cerberus Pup') + button(ng-if='user.items.pets["Dragon-Hydra"]', class="pet-button Pet-Dragon-Hydra", ng-class='{active: user.items.currentPet == "Dragon-Hydra"}', ng-click='choosePet("Dragon", "Hydra")', tooltip='Hydra Pet') a(target='_blank', href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG') - button(ng-if='!hasPet("Dragon", "Hydra")', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover="Click the gold paw to learn more about how you can obtain this rare pet through contributing to HabitRPG!", popover-title='How to Get this Pet!') - img(src='/bower_components/habitrpg-shared/img/PixelPaw-Gold.png') \ No newline at end of file + button(ng-if='!user.items.pets["Dragon-Hydra"]', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover="Click the gold paw to learn more about how you can obtain this rare pet through contributing to HabitRPG!", popover-title='How to Get this Pet!') + img(src='/bower_components/habitrpg-shared/img/PixelPaw-Gold.png') + + //-.well.food-tray + p(ng-show='foodCount < 1') You don't have any food yet. + menu.inventory-list(type='list', ng-if='foodCount > 0') + li.customize-menu + menu.pets-menu(label='Food') + div(ng-repeat='(food,points) in ownedItems(user.items.food)') + button.customize-option(popover-append-to-body='true', popover='{{Items.food[food].notes}}', popover-title='{{Items.food[food].text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + .badge.badge-info.stack-count {{points}} + // Remove this once we have images in + p {{Items.food[food].text}} \ No newline at end of file diff --git a/views/shared/header/avatar.jade b/views/shared/header/avatar.jade index 0fcb8096a4..3154f5f92d 100644 --- a/views/shared/header/avatar.jade +++ b/views/shared/header/avatar.jade @@ -1,6 +1,14 @@ -figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.pet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="
Level: {{profile.stats.lvl}}
GP: {{profile.stats.gp | number:0}}
{{count(profile.items.pets)}} / 90 Pets Found
") +//- FIXME the commented-out figure.herobox used to have a functioning popover, but angular-ui-bootstrap doesn't support + html in popovers. Figure out what to do here. Also, {isUser:..} class seems to bring the user too far down with mounts. + Removing it will remove the user's name, but will allow more room for mounts & helms. IMO this is the lesser of two evils, revisit +//-figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.currentPet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="
Level: {{profile.stats.lvl}}
GP: {{profile.stats.gp | number:0}}
{{count(profile.items.pets)}} / 90 Pets Found
") + +figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: user._id==profile._id && !(user.items.currentMount && user.items.currentPet), hasPet: profile.items.currentPet && profile.items.currentMount}') .character-sprites - span(ng-class='{zzz:profile.flags.rest}') + // Mount Body + span(ng-if='profile.items.currentMount', class='Mount_Body_{{profile.items.currentMount}}') + + // Avatar span(class='{{profile.preferences.gender}}_skin_{{profile.preferences.skin}}') span(class='{{profile.preferences.gender}}_hair_{{profile.preferences.hair}}') span(class='{{equipped("armor", profile.items.armor, profile.preferences, profile.backer, profile.contributor)}}') @@ -8,6 +16,14 @@ figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile span(class='{{equipped("head", profile.items.head, profile.preferences, profile.backer, profile.contributor)}}', ng-show='profile.preferences.showHelm') span(class='{{equipped("shield",profile.items.shield,profile.preferences, profile.backer, profile.contributor)}}') span(class='{{equipped("weapon",profile.items.weapon,profile.preferences, profile.backer, profile.contributor)}}') + + // Mount Head + span(ng-if='profile.items.currentMount', class='Mount_Head_{{profile.items.currentMount}}') + + // Resting + span(ng-class='{zzz:profile.flags.rest}') + + // Pet // FIXME handle @minimal, this might have to be a directive - span.current-pet(class='Pet-{{profile.items.currentPet.name}}-{{profile.items.currentPet.modifier}}', ng-show='profile.items.currentPet && !minimal') + span.current-pet(class='Pet-{{profile.items.currentPet}}', ng-show='profile.items.currentPet && !minimal') .avatar-level Lvl {{profile.stats.lvl}} diff --git a/views/shared/modals/pets.jade b/views/shared/modals/pets.jade index aa60e7610b..0a2f152d08 100644 --- a/views/shared/modals/pets.jade +++ b/views/shared/modals/pets.jade @@ -2,11 +2,15 @@ div(modal='modals.dropsEnabled') .modal-header h3 Drops Enabled! .modal-body - p + //-p // TODO how to handle random first drop? span.item-drop-icon(class='Pet_Egg_{{user.items.eggs.0.name}}', style='margin-left: 0px') | You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a strong {{user.items.eggs.0.text}} Egg | ! {{user.items.eggs.0.notes}}. + p + span.item-drop-icon(class='Pet_Egg_Wolf', style='margin-left: 0px') + span. + You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a {{Items.eggs.Wolf.text}} Egg! {{Items.eggs.Wolf.notes}} br p. If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in Options > Inventory to buy one!