From 2a1f52a35908f3e1a1d799c6472f17b83ecc855e Mon Sep 17 00:00:00 2001 From: Travis Date: Fri, 30 Dec 2016 13:29:20 -0600 Subject: [PATCH] feature: adding hp notification for boss damage (#8249) * feature: adding hp notification for boss damage. fixes #7749 * Updating boss damage text to 'Damage from Boss' to make it more clear --- test/api/v3/unit/models/group.test.js | 31 +++++++++++++++++++ .../js/controllers/notificationCtrl.js | 3 ++ .../js/services/notificationServices.js | 8 +++++ website/common/locales/en/quests.json | 1 + website/server/models/group.js | 29 ++++++++++++----- website/server/models/userNotification.js | 1 + 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/test/api/v3/unit/models/group.test.js b/test/api/v3/unit/models/group.test.js index 04b545d45e..fa87d2e6fe 100644 --- a/test/api/v3/unit/models/group.test.js +++ b/test/api/v3/unit/models/group.test.js @@ -219,6 +219,37 @@ describe('Group Model', () => { expect(updatedUndecidedMember.stats.hp).to.eql(50); }); + it('doesn\'t notify the user when the boss damage is 0', async () => { + progress.down = 0; + await Group.processQuestProgress(participatingMember, progress); + + let updatedLeader = await User.findById(questLeader._id); + + expect(updatedLeader.stats.hp).to.eql(50); + expect(updatedLeader.notifications.length).to.eql(0); + }); + + it('notifies user of boss damage', async () => { + await Group.processQuestProgress(participatingMember, progress); + + let updatedLeader = await User.findById(questLeader._id); + + expect(updatedLeader.stats.hp).to.eql(42.5); + expect(updatedLeader.notifications[0].type).to.eql('BOSS_DAMAGE'); + expect(updatedLeader.notifications[0].data).to.eql({damageTaken: -7.5}); + }); + + it('combines boss damage notifications across multiple updates', async () => { + await Group.processQuestProgress(participatingMember, progress); + await Group.processQuestProgress(participatingMember, progress); + + let updatedLeader = await User.findById(participatingMember._id); + + expect(updatedLeader.stats.hp).to.eql(35); + expect(updatedLeader.notifications[0].type).to.eql('BOSS_DAMAGE'); + expect(updatedLeader.notifications[0].data).to.eql({damageTaken: -15}); + }); + it('applies damage only to participating members of party even under buggy conditions', async () => { // stops unfair damage from mbugs like https://github.com/HabitRPG/habitrpg/issues/7653 party.quest.members = { diff --git a/website/client-old/js/controllers/notificationCtrl.js b/website/client-old/js/controllers/notificationCtrl.js index 9f1679150f..4f9f83edec 100644 --- a/website/client-old/js/controllers/notificationCtrl.js +++ b/website/client-old/js/controllers/notificationCtrl.js @@ -149,6 +149,9 @@ habitrpg.controller('NotificationCtrl', case 'LOGIN_INCENTIVE': Notification.showLoginIncentive(User.user, notification.data, Social.loadWidgets); break; + case 'BOSS_DAMAGE': + Notification.bossDamage(notification.data.damageTaken); + break; default: if (notification.data.headerText && notification.data.bodyText) { var modalScope = $rootScope.$new(); diff --git a/website/client-old/js/services/notificationServices.js b/website/client-old/js/services/notificationServices.js index edb08cd4cc..9e86e6ec8e 100644 --- a/website/client-old/js/services/notificationServices.js +++ b/website/client-old/js/services/notificationServices.js @@ -78,6 +78,13 @@ angular.module("habitrpg").factory("Notification", _notify(_sign(val) + " " + _round(val) + " " + window.env.t('health'), 'hp', 'glyphicon glyphicon-heart'); } + function bossDamage(val) { + var data = { + val: _round(val), + }; + _notify(window.env.t('bossDamageDone', data), 'hp', 'glyphicon glyphicon-heart'); + } + function lvl(){ _notify(window.env.t('levelUp'), 'lvl', 'glyphicon glyphicon-chevron-up'); } @@ -176,5 +183,6 @@ angular.module("habitrpg").factory("Notification", text: text, quest: quest, showLoginIncentive: showLoginIncentive, + bossDamage: bossDamage, }; }]); diff --git a/website/common/locales/en/quests.json b/website/common/locales/en/quests.json index 366451d5d9..0eda3fc50a 100644 --- a/website/common/locales/en/quests.json +++ b/website/common/locales/en/quests.json @@ -41,6 +41,7 @@ "collected": "Collected", "collectionItems": "<%= number %> <%= items %>", "itemsToCollect": "Items to Collect", + "bossDamageDone": "Damage from Boss: - <%= val %> Health", "bossDmg1": "Each completed Daily and To-Do and each positive Habit hurts the boss. Hurt it more with redder tasks or Brutal Smash and Burst of Flames. The boss will deal damage to every quest participant for every Daily you've missed (multiplied by the boss's Strength) in addition to your regular damage, so keep your party healthy by completing your Dailies! All damage to and from a boss is tallied on cron (your day roll-over).", "bossDmg2": "Only participants will fight the boss and share in the quest loot.", "bossDmg1Broken": "Each completed Daily and To-Do and each positive Habit hurts the boss... Hurt it more with redder tasks or Brutal Smash and Burst of Flames... The boss will deal damage to every quest participant for every Daily you've missed (multiplied by the boss's Strength) in addition to your regular damage, so keep your party healthy by completing your Dailies... All damage to and from a boss is tallied on cron (your day roll-over)...", diff --git a/website/server/models/group.js b/website/server/models/group.js index 88fe3a598f..5e1277cc97 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -709,12 +709,26 @@ schema.methods._processBossQuest = async function processBossQuest (options) { } } + let promises = []; + // Everyone takes damage - await User.update({ - _id: {$in: this.getParticipatingQuestMembers()}, - }, { - $inc: {'stats.hp': down}, - }, {multi: true}).exec(); + if (down !== 0) { + let members = await User.find({ + _id: {$in: this.getParticipatingQuestMembers()}, + }).select('notifications stats.hp'); + + _.each(members, (member) => { + member.stats.hp += down; + let bossNotification = _.find(member.notifications, {type: 'BOSS_DAMAGE'}); + if (bossNotification) { + bossNotification.data.damageTaken += down; + bossNotification.markModified('data.damageTaken'); + } else { + member.addNotification('BOSS_DAMAGE', {damageTaken: down}); + } + promises.push(member.save()); + }); + } // Apply changes the currently cronning user locally so we don't have to reload it to get the updated state // TODO how to mark not modified? https://github.com/Automattic/mongoose/pull/1167 // must be notModified or otherwise could overwrite future changes: if the user is saved it'll save @@ -726,10 +740,11 @@ schema.methods._processBossQuest = async function processBossQuest (options) { group.sendChat(`\`You defeated ${quest.boss.name('en')}! Questing party members receive the rewards of victory.\``); // Participants: Grant rewards & achievements, finish quest - await group.finishQuest(shared.content.quests[group.quest.key]); + promises.push(group.finishQuest(shared.content.quests[group.quest.key])); } - return await group.save(); + promises.push(group.save()); + return await Bluebird.all(promises); }; schema.methods._processCollectionQuest = async function processCollectionQuest (options) { diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index 1fec849c35..38b0bee5b4 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -16,6 +16,7 @@ const NOTIFICATION_TYPES = [ 'GROUP_TASK_APPROVED', 'LOGIN_INCENTIVE', 'GROUP_INVITE_ACCEPTED', + 'BOSS_DAMAGE', 'SCORED_TASK', ];