diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index bde42cbc34..376c25d810 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -116,7 +116,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' // We watch Members.selectedMember because it's asynchronously set, so would be a hassle to handle updates here $scope.$watch( function() { return Members.selectedMember; }, function (member) { if(member) - member.petCount = Shared.countPets(null, member.items.pets); + member.petCount = Shared.countPets($rootScope.countExists(member.items.pets), member.items.pets); + member.mountCount = Shared.countMounts($rootScope.countExists(member.items.mounts), member.items.mounts); $scope.profile = member; }); $scope.sendPrivateMessage = function(uuid, message){ diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 325584ca26..02572e7f7f 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -1,5 +1,6 @@ -habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', 'Content', - function($rootScope, $scope, $window, User, Content) { +habitrpg.controller("InventoryCtrl", + ['$rootScope', '$scope', 'Shared', '$window', 'User', 'Content', + function($rootScope, $scope, Shared, $window, User, Content) { var user = User.user; @@ -8,12 +9,11 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', $scope.selectedEgg = null; // {index: 1, name: "Tiger", value: 5} $scope.selectedPotion = null; // {index: 5, name: "Red", value: 3} $scope.totalPets = _.size(Content.dropEggs) * _.size(Content.hatchingPotions); - $scope.totalMounts = _.size(_.reject(Content.eggs,function(egg){return egg.noMount})) * _.size(Content.hatchingPotions); + $scope.totalMounts = _.size(Content.dropEggs) * _.size(Content.hatchingPotions); // count egg, food, hatchingPotion stack totals var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);} - $scope.$watch('user.items.mounts', function(mounts){ $scope.mountCount = $rootScope.countExists(mounts); }, true); $scope.$watch('user.items.eggs', function(eggs){ $scope.eggCount = countStacks(eggs); }, true); $scope.$watch('user.items.hatchingPotions', function(pots){ $scope.potCount = countStacks(pots); }, true); $scope.$watch('user.items.food', function(food){ $scope.foodCount = countStacks(food); }, true); @@ -77,6 +77,18 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', return _.pick(inventory, function(v,k){return v>0;}); } + $scope.calcTriadBingo = function() { + var hasPets = Shared.countPets(null, User.user.items.pets) >= 90 ; + var hasMounts = Shared.countMounts(null, User.user.items.mounts) >= 90; + + if(!(hasPets && hasMounts)) return false; + + for (var p in User.user.items.pets) { + if(User.user.items.pets[p] < 0) return false; + } + return true; + } + $scope.hatch = function(egg, potion){ var eggName = Content.eggs[egg.key].text(); var potName = Content.hatchingPotions[potion.key].text(); @@ -84,6 +96,20 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', user.ops.hatch({params:{egg:egg.key, hatchingPotion:potion.key}}); $scope.selectedEgg = null; $scope.selectedPotion = null; + + // Checks if beastmaster has been reached for the first time + if(!User.user.achievements.beastMaster + && Shared.countPets(null, User.user.items.pets) >= 90) { + User.user.achievements.beastMaster = true; + $rootScope.openModal('achievements/beastMaster'); + } + + // Checks if Triad Bingo has been reached for the first time + if(!User.user.achievements.triadBingo + && $scope.calcTriadBingo()) { + User.user.achievements.triadBingo = true; + $rootScope.openModal('achievements/triadBingo'); + } } $scope.purchase = function(type, item){ @@ -123,6 +149,14 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', } User.user.ops.feed({params:{pet: pet, food: food.key}}); $scope.selectedFood = null; + $rootScope.mountCount = Shared.countMounts(null, User.user.items.mounts); + + // Checks if mountmaster has been reached for the first time + if(!User.user.achievements.mountMaster + && Shared.countMounts(null, User.user.items.mounts) >= 90) { + User.user.achievements.mountMaster = true; + $rootScope.openModal('achievements/mountMaster'); + } // Selecting Pet } else { diff --git a/public/js/controllers/notificationCtrl.js b/public/js/controllers/notificationCtrl.js index 5ccc09ea86..dae22b9d0f 100644 --- a/public/js/controllers/notificationCtrl.js +++ b/public/js/controllers/notificationCtrl.js @@ -104,15 +104,6 @@ habitrpg.controller('NotificationCtrl', $rootScope.openModal('achievements/ultimateGear'); }); - $rootScope.$watch('user.items.pets', function(after, before){ - if(_.size(after) === _.size(before) || - Shared.countPets(null, after) < 90) return; - if (User.user.achievements.beastMaster == false) { - User.user.achievements.beastMaster = true; - $rootScope.openModal('achievements/beastMaster'); - } - }, true); - $rootScope.$watch('user.achievements.rebirths', function(after, before){ if(after === before) return; $rootScope.openModal('achievements/rebirth'); diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index 3533d28a6b..e46ffdbc1f 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -88,7 +88,8 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ // count pets, mounts collected totals, etc $rootScope.countExists = function(items) {return _.reduce(items,function(m,v){return m+(v?1:0)},0)} - $rootScope.petCount = Shared.countPets(null, User.user.items.pets); + $rootScope.petCount = Shared.countPets($rootScope.countExists(User.user.items.pets), User.user.items.pets); + $rootScope.mountCount = Shared.countMounts($rootScope.countExists(User.user.items.mounts), User.user.items.mounts); $rootScope.$watch('user.items.pets', function(pets){ $rootScope.petCount = Shared.countPets($rootScope.countExists(pets), User.user.items.pets); diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 599e29664c..d033749620 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -156,13 +156,20 @@ habitrpg.controller('SettingsCtrl', window.location.href = '/api/v2/coupons?limit='+codes.count+'&_id='+User.user._id+'&apiToken='+User.user.apiToken; }) } - $scope.release = function() { - User.user.ops.release({}); + $scope.releasePets = function() { + User.user.ops.releasePets({}); $rootScope.$state.go('tasks'); } - $scope.release2 = function() { - User.user.ops.release2({}); + $scope.releaseMounts = function() { + User.user.ops.releaseMounts({}); + $rootScope.mountCount = 0; + $rootScope.$state.go('tasks'); + } + + $scope.releaseBoth = function() { + User.user.ops.releaseBoth({}); + $rootScope.mountCount = 0; $rootScope.$state.go('tasks'); } diff --git a/public/js/controllers/userCtrl.js b/public/js/controllers/userCtrl.js index 71f185c6ff..cf5336f4c7 100644 --- a/public/js/controllers/userCtrl.js +++ b/public/js/controllers/userCtrl.js @@ -3,7 +3,8 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http', '$state', 'Guide', 'Shared', function($rootScope, $scope, $location, User, $http, $state, Guide, Shared) { $scope.profile = User.user; - $scope.profile.petCount = Shared.countPets(null, $scope.profile.items.pets); + $scope.profile.petCount = Shared.countPets($rootScope.countExists($scope.profile.items.pets), $scope.profile.items.pets); + $scope.profile.mountCount = Shared.countMounts($rootScope.countExists($scope.profile.items.mounts), $scope.profile.items.mounts); $scope.hideUserAvatar = function() { $(".userAvatar").hide(); }; diff --git a/src/models/user.js b/src/models/user.js index 48eb31ecbc..7cfc710474 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -36,6 +36,10 @@ var UserSchema = new Schema({ ultimateGear: Boolean, beastMaster: Boolean, beastMasterCount: Number, + mountMaster: Boolean, + mountMasterCount: Number, + triadBingo: Boolean, + triadBingoCount: Number, veteran: Boolean, snowball: Number, spookDust: Number, @@ -429,6 +433,7 @@ UserSchema.pre('save', function(next) { 'Anonymous'; } + // Determines if Beast Master should be awarded var petCount = shared.countPets(_.reduce(this.items.pets,function(m,v){ //HOTFIX - Remove when solution is found, the first argument passed to reduce is a function if(_.isFunction(v)) return m; @@ -438,6 +443,29 @@ UserSchema.pre('save', function(next) { this.achievements.beastMaster = true } + // Determines if Mount Master should be awarded + var mountCount = shared.countMounts(_.reduce(this.items.mounts,function(m,v){ + //HOTFIX - Remove when solution is found, the first argument passed to reduce is a function + if(_.isFunction(v)) return m; + return m+(v?1:0)},0), this.items.mounts); + + if (mountCount >= 90 || this.achievements.mountMasterCount > 0) { + this.achievements.mountMaster = true + } + + // Determines if Triad Bingo should be awarded + + var giveTriadBingo = function(pets) { + for(var p in pets) { + if(pets[p] == -1) return false; + } + return true; + }; + + if ((mountCount >= 90 && giveTriadBingo(this.items.pets)) || this.achievements.triadBingoCount > 0) { + this.achievements.triadBingo = true; + } + // EXAMPLE CODE for allowing all existing and new players to be // automatically granted an item during a certain time period: // if (!this.items.pets['JackOLantern-Base'] && moment().isBefore('2014-11-01')) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index f5e760f30e..50ca1259d8 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -226,8 +226,8 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') p(ng-show='user.stats.lvl < 100') | 8  span.Pet_Currency_Gem1x.inline-gems - div(ng-show='petCount >= 90') - button.customize-option(popover=env.t('petKeyPop'), popover-title=env.t('petKeyName'), popover-trigger='mouseenter', popover-placement='top', ng-click='openModal("pet-key")', class='pet_key') + div(ng-show='petCount >= 90 || mountCount >= 90') + button.customize-option(popover=env.t('petKeyPop'), popover-title=env.t('petKeyName'), popover-trigger='mouseenter', popover-placement='top', ng-click='openModal("pet-key", {size:"lg"})', class='pet_key') p | 4  span.Pet_Currency_Gem1x.inline-gems diff --git a/views/options/inventory/stable.jade b/views/options/inventory/stable.jade index 6fa5b75aa8..2cfb1c6d51 100644 --- a/views/options/inventory/stable.jade +++ b/views/options/inventory/stable.jade @@ -41,7 +41,7 @@ script(type='text/ng-template', id='partials/options.inventory.mounts.html') a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') .popover-content p=env.t('mattShall', {name: "{{user.profile.name}}"}) - h4= '{{Shared.countMounts(null,User.user.items.mounts) || 0}} / {{totalMounts}} ' + env.t('mountsTamed') + h4= env.t('mountMasterProgress') + ': {{mountCount}} / {{totalMounts}} ' + env.t('mountsTamed') .col-md-12 +mountList(env.Content.dropEggs) .col-md-12 @@ -66,13 +66,8 @@ script(type='text/ng-template', id='partials/options.inventory.pets.html') h3.popover-title a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') .popover-content - p - =env.t('mattBochText1') + ' ' - | - =env.t('mattBochText2') - | - = ' ' + env.t('mattBochText3') - h4= env.t('beastmasterProgress') + ': {{petCount}} / {{totalPets}} ' + env.t('petsFound') + p=env.t('mattBochText1') + h4= env.t('beastMasterProgress') + ': {{petCount}} / {{totalPets}} ' + env.t('petsFound') .col-md-12 +petList(env.Content.dropEggs) diff --git a/views/shared/modals/achievements.jade b/views/shared/modals/achievements.jade index 86e2b2a8b3..9d1decd66c 100644 --- a/views/shared/modals/achievements.jade +++ b/views/shared/modals/achievements.jade @@ -29,6 +29,29 @@ script(id='modals/achievements/beastMaster.html', type='text/ng-template') .modal-footer button.btn.btn-default(ng-click='$close()')=env.t('ok') + +// Mount Master +script(id='modals/achievements/mountMaster.html', type='text/ng-template') + .modal-header + h4=env.t('modalAchievement') + .modal-body + p + .achievement.achievement-wolf + =env.t('mountAchievement') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('ok') + +// Triad Bingo +script(id='modals/achievements/triadBingo.html', type='text/ng-template') + .modal-header + h4=env.t('modalAchievement') + .modal-body + p + .achievement.achievement-triadbingo + =env.t('triadBingoAchievement') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('ok') + // Contributor // activated by user.flags.contributor script(id='modals/achievements/contributor.html', type='text/ng-template') diff --git a/views/shared/modals/drops.jade b/views/shared/modals/drops.jade index 12338eaac8..ffba52e088 100644 --- a/views/shared/modals/drops.jade +++ b/views/shared/modals/drops.jade @@ -16,11 +16,14 @@ script(type='text/ng-template', id='modals/dropsEnabled.html') script(type='text/ng-template', id='modals/pet-key.html') .modal-header - h4=env.t('petKeyBegin') + // Checks if user has 90 pets and 90 mounts. If so, displays "Beast Master/Mount Master". + // Else if user has 90 pets, displays "Beast Master". Else, displays "Mount Master" + - var masterTitle='{{petCount >= 90 && mountCount >= 90 ? env.t("beastMountMasterName") : petCount >= 90 ? env.t("beastMasterName") : env.t("mountMasterName")}}' + h4=env.t('petKeyBegin', {title: masterTitle}) .modal-body +gemButton figure - .npc_alex + .npc_matt p=env.t('petKeyInfo') br p=env.t('petKeyInfo2') @@ -31,10 +34,18 @@ script(type='text/ng-template', id='modals/pet-key.html') span(ng-if='user.balance < 1') a.btn.btn-success(ng-click='openModal("buyGems")')=env.t('buyMoreGems') span.gem-cost=env.t('notEnoughGems') - span(ng-if='user.balance >= 1', ng-controller='SettingsCtrl') - a.btn.btn-danger(ng-click='$close(); release()')=env.t('petKeyPets') - span(ng-if='user.balance >= 1', ng-controller='SettingsCtrl') - a.btn.btn-danger(ng-click='$close(); release2()')=env.t('petKeyMounts') - span.gem-cost - | 4  - =env.t('gemsEach') \ No newline at end of file + span(ng-if='user.balance >= 1 && petCount >= 90', ng-controller='SettingsCtrl') + a.btn.btn-danger(ng-click='$close(); releasePets()') + =env.t('petKeyPets') + | : 4  + span.Pet_Currency_Gem1x.inline-gems + span(ng-if='user.balance >= 1 && mountCount >= 90', ng-controller='SettingsCtrl') + a.btn.btn-danger(ng-click='$close(); releaseMounts()') + =env.t('petKeyMounts') + | : 4  + span.Pet_Currency_Gem1x.inline-gems + span(ng-if='user.balance >= 1.5 && petCount >= 90 && mountCount >= 90', ng-controller='SettingsCtrl') + a.btn.btn-danger(ng-click='$close(); releaseBoth()') + =env.t('petKeyBoth') + | : 6  + span.Pet_Currency_Gem1x.inline-gems diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 3b22a93917..724e1f8dd7 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -1,14 +1,47 @@ -h5 +h5 MOUNT MASTER AND TRIAD BINGO ACHIEVEMENTS, PARTY SORTING OPTIONS, DATED TO-DOs, AND STRESSBEAST DESPERATION TRIGGERED! + hr tr td - h5 1/19/2015 - WORLD BOSS: SECOND STRESS STRIKE! - p AHHHHHHHH!!!!! IT'S GOT ME!!!!! Oh, Habiticans, why didn't you do your Dailies?! - p The World Boss in the Tavern has used another Stress Strike, and this time it's attacked me, Bailey the Town Crier! To save me and the other NPCs, complete Dailies and To-Dos to damage the World Boss! Incomplete Dailies fill the Stress Strike Bar. When the Stress Strike bar is full, the World Boss will attack an NPC and regain some health. A World Boss will never damage individual players or accounts in any way. Only active accounts who are not resting in the inn will have their incomplete Dailies tallied. - p by Lemoness, Kiwibot, and SabreCat + h5 Mount Master and Triad Bingo Achievements + .achievement.achievement-wolf + .achievement.achievement-triadbingo + p There are two new achievements you can earn: Mount Master and Triad Bingo! Mount Master is awarded to users who have collected all 90 standard mounts, and Triad Bingo is for those who have collected all 90 standard pets, grown all 90 into mounts, and then rehatched 90 more standard pets. Wow! + + P Note that Quest Pets and Quest Mounts do not count towards Mount Master or Triad Bingo. If you currently meet the criteria, you will be awarded the badge, but unfortunately, if you already released your Mounts, you will not receive the badge until you collect them again. + p.small.muted by Taldin, Blade, Lorian, Aiseant, and Hanztan + tr + td + h5 Party Sorting Options + p In the Party Page you can now sort your friends' avatars in ascending or descending order! To make the change take effect, you'll have to refresh the page. + + p.small.muted by Blade and Viirus + tr + td + h5 Dated To-Dos + p Now you can use To-Do tabs to sort and see your dated To-Dos! Simply click the "Dated" tab and only To-Dos with a due date will be displayed. They are not currently sorted by date, but we will be implementing that feature in the future. + + p.small.muted by Alys + tr + td + h5 Stressbeast Desperation Triggered + + p We're almost there, Habiticans! With diligence and Dailies, we've whittled the Stressbeast's health down to only 500K! The creature roars and flails in desperation, rage building faster than ever. The monster is --- AHHH! --- swinging me and Matt around at a terrifying pace, raising a blinding snowstorm that makes it harder to hit. + p We'll have to redouble our efforts, but take heart - this is a sign that the Stressbeast knows it is about to be defeated. Don't give up now! Please? + + p The Stressbeast raises its Rage and Defense! Complete Dailies and To-Dos to damage the World Boss. A World Boss will never damage individual players or accounts in any way. Only active accounts who are not resting in the inn will have their incomplete Dailies tallied. + p.small.muted by Lemoness, Kiwibot, and SabreCat hr a(href='/static/old-news', target='_blank') Read older news mixin oldNews + h5 1/19/2015 + tr + td + h5 WORLD BOSS: SECOND STRESS STRIKE! + p AHHHHHHHH!!!!! IT'S GOT ME!!!!! Oh, Habiticans, why didn't you do your Dailies?! + p The World Boss in the Tavern has used another Stress Strike, and this time it's attacked me, Bailey the Town Crier! To save me and the other NPCs, complete Dailies and To-Dos to damage the World Boss! Incomplete Dailies fill the Stress Strike Bar. When the Stress Strike bar is full, the World Boss will attack an NPC and regain some health. A World Boss will never damage individual players or accounts in any way. Only active accounts who are not resting in the inn will have their incomplete Dailies tallied. + p.small.muted by Lemoness, Kiwibot, and SabreCat + h5 1/15/2015 tr td diff --git a/views/shared/profiles/achievements.jade b/views/shared/profiles/achievements.jade index 4ff1777d49..af414f15b5 100644 --- a/views/shared/profiles/achievements.jade +++ b/views/shared/profiles/achievements.jade @@ -81,10 +81,28 @@ div(ng-if='::user._id == profile._id') div(ng-if='profile.achievements.beastMaster || user._id == profile._id') .achievement.achievement-rat(ng-show='profile.achievements.beastMaster') div(ng-class='{muted: !profile.achievements.beastMaster}') - h5=env.t('beastMastName') - small=env.t('beastMastText') + h5=env.t('beastMasterName') + small=env.t('beastMasterText') small(ng-if='profile.achievements.beastMasterCount') - =env.t('beastMastText2', {count: "{{profile.achievements.beastMasterCount}}"}) + =env.t('beastMasterText2', {count: "{{profile.achievements.beastMasterCount}}"}) + hr + +div(ng-if='profile.achievements.mountMaster || user._id == profile._id') + .achievement.achievement-wolf(ng-show='profile.achievements.mountMaster') + div(ng-class='{muted: !profile.achievements.mountMaster}') + h5=env.t('mountMasterName') + small=env.t('mountMasterText') + small(ng-if='profile.achievements.mountMasterCount') + =env.t('mountMasterText2', {count: "{{profile.achievements.mountMasterCount}}"}) + hr + +div(ng-if='profile.achievements.triadBingo || user._id == profile._id') + .achievement.achievement-triadbingo(ng-show='profile.achievements.triadBingo') + div(ng-class='{muted: !profile.achievements.triadBingo}') + h5=env.t('triadBingoName') + small=env.t('triadBingoText') + small(ng-if='profile.achievements.triadBingoCount') + =env.t('triadBingoText2', {count: "{{profile.achievements.triadBingoCount}}"}) hr div(ng-if='profile.achievements.rebirths') @@ -190,4 +208,4 @@ div(ng-if='::profile.achievements.quests.stressbeast') h5=env.t('achievementStressbeast') small =env.t('achievementStressbeastText') - hr \ No newline at end of file + hr diff --git a/views/shared/profiles/stats.jade b/views/shared/profiles/stats.jade index 3a3e512c66..f644a7f6f7 100644 --- a/views/shared/profiles/stats.jade +++ b/views/shared/profiles/stats.jade @@ -21,7 +21,6 @@ table.table.table-striped unless mobile h4.stats-equipment(class=mobile?'item item-divider':'',ng-show='user.flags.itemsEnabled')=env.t('equipment') table.table.table-striped(ng-show='user.flags.itemsEnabled') - // FIXME this isn't refreshing properly. On first party modal, correct. Second party modal click gives the previous gear. It's always one behind. tr(ng-repeat='(k,v) in profile.items.gear.equipped', ng-init='piece=Content.gear.flat[v]', ng-show='piece') td strong {{piece.text()}}:  @@ -65,14 +64,24 @@ table.table.table-striped strong.hint(popover-title=env.t('streaksFrozen'), popover-trigger='mouseenter', popover-placement='right', popover=env.t('streaksFrozenText'))=env.t('streaksFrozen') td -h4(class=mobile?'item item-divider':'',ng-show='user.flags.dropsEnabled')=env.t('pets') -table.table.table-striped(ng-show='user.flags.dropsEnabled') +h4(class=mobile?'item item-divider':'',ng-if='user.flags.dropsEnabled')=env.t('pets') +table.table.table-striped(ng-if='user.flags.dropsEnabled') tr td strong=env.t('petsFound') - | : {{profile.petCount}} -// Dunno why this doesn't work // -// tr + | : {{_.size(profile.items.pets)}} + tr + td + strong=env.t('beastMasterProgress') + | : {{profile.petCount}}/90 + +h4(class=mobile?'item item-divider':'', ng-if='user.flags.dropsEnabled')=env.t('mounts') +table.table.table-striped(ng-if='user.flags.dropsEnabled') + tr td strong=env.t('mountsTamed') - | : {{profile.mountCount}} // + | : {{_.size(profile.items.mounts)}} + tr + td + strong=env.t('mountMasterProgress') + | : {{profile.mountCount}}/90