diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js
index 7d120c055a..0a2b45b571 100644
--- a/test/api/unit/models/group.test.js
+++ b/test/api/unit/models/group.test.js
@@ -1734,7 +1734,7 @@ describe('Group Model', () => {
let quest;
beforeEach(() => {
- quest = questScrolls.whale;
+ quest = questScrolls.armadillo;
party.quest.key = quest.key;
party.quest.active = false;
party.quest.leader = questLeader._id;
@@ -1882,6 +1882,36 @@ describe('Group Model', () => {
expect(updatedSleepingParticipatingMember.achievements.lostMasterclasser).to.not.eql(true);
});
+ it('gives out other pet-related quest achievements', async () => {
+ quest = questScrolls.rock;
+ party.quest.key = quest.key;
+
+ questLeader.achievements.quests = {
+ mayhemMistiflying1: 1,
+ yarn: 1,
+ mayhemMistiflying2: 1,
+ egg: 1,
+ mayhemMistiflying3: 1,
+ slime: 2,
+ };
+ await questLeader.save();
+ await party.finishQuest(quest);
+
+ let [
+ updatedLeader,
+ updatedParticipatingMember,
+ updatedSleepingParticipatingMember,
+ ] = await Promise.all([
+ User.findById(questLeader._id).exec(),
+ User.findById(participatingMember._id).exec(),
+ User.findById(sleepingParticipatingMember._id).exec(),
+ ]);
+
+ expect(updatedLeader.achievements.mindOverMatter).to.eql(true);
+ expect(updatedParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
+ expect(updatedSleepingParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
+ });
+
it('gives xp and gold', async () => {
await party.finishQuest(quest);
@@ -2018,13 +2048,13 @@ describe('Group Model', () => {
questLeader = await User.findById(questLeader._id);
participatingMember = await User.findById(participatingMember._id);
- expect(questLeader.party.quest.completed).to.eql('whale');
+ expect(questLeader.party.quest.completed).to.eql('armadillo');
expect(questLeader.party.quest.progress.up).to.eql(10);
expect(questLeader.party.quest.progress.down).to.eql(8);
expect(questLeader.party.quest.progress.collectedItems).to.eql(5);
expect(questLeader.party.quest.RSVPNeeded).to.eql(false);
- expect(participatingMember.party.quest.completed).to.eql('whale');
+ expect(participatingMember.party.quest.completed).to.eql('armadillo');
expect(participatingMember.party.quest.progress.up).to.eql(10);
expect(participatingMember.party.quest.progress.down).to.eql(8);
expect(participatingMember.party.quest.progress.collectedItems).to.eql(5);
diff --git a/test/common/ops/feed.js b/test/common/ops/feed.js
index 4aabd119df..84c3713686 100644
--- a/test/common/ops/feed.js
+++ b/test/common/ops/feed.js
@@ -169,6 +169,24 @@ describe('shared.ops.feed', () => {
expect(user.items.pets['Wolf-Base']).to.equal(7);
});
+ it('awards All Your Base achievement', () => {
+ user.items.pets['Wolf-Spooky'] = 5;
+ user.items.food.Milk = 2;
+ user.items.mounts = {
+ 'Wolf-Base': true,
+ 'TigerCub-Base': true,
+ 'PandaCub-Base': true,
+ 'LionCub-Base': true,
+ 'Fox-Base': true,
+ 'FlyingPig-Base': true,
+ 'Dragon-Base': true,
+ 'Cactus-Base': true,
+ 'BearCub-Base': true,
+ };
+ feed(user, {params: {pet: 'Wolf-Spooky', food: 'Milk'}});
+ expect(user.achievements.allYourBase).to.eql(true);
+ });
+
it('evolves the pet into a mount when feeding user.items.pets[pet] >= 50', () => {
user.items.pets['Wolf-Base'] = 49;
user.items.food.Milk = 2;
diff --git a/test/common/ops/hatch.js b/test/common/ops/hatch.js
index 919829d6b8..0339a8fa8d 100644
--- a/test/common/ops/hatch.js
+++ b/test/common/ops/hatch.js
@@ -159,6 +159,24 @@ describe('shared.ops.hatch', () => {
expect(user.items.eggs).to.eql({Wolf: 0});
expect(user.items.hatchingPotions).to.eql({Base: 0});
});
+
+ it('awards Back to Basics achievement', () => {
+ user.items.pets = {
+ 'Wolf-Base': 5,
+ 'TigerCub-Base': 5,
+ 'PandaCub-Base': 10,
+ 'LionCub-Base': 5,
+ 'Fox-Base': 5,
+ 'FlyingPig-Base': 5,
+ 'Dragon-Base': 5,
+ 'Cactus-Base': 15,
+ 'BearCub-Base': 5,
+ };
+ user.items.eggs = {Wolf: 1};
+ user.items.hatchingPotions = {Spooky: 1};
+ hatch(user, {params: {egg: 'Wolf', hatchingPotion: 'Spooky'}});
+ expect(user.achievements.backToBasics).to.eql(true);
+ });
});
});
});
diff --git a/website/client/app.vue b/website/client/app.vue
index a7e3a424d5..bc0cf71deb 100644
--- a/website/client/app.vue
+++ b/website/client/app.vue
@@ -656,5 +656,6 @@ export default {
+
diff --git a/website/client/components/achievements/genericAchievement.vue b/website/client/components/achievements/genericAchievement.vue
new file mode 100644
index 0000000000..6f1b9042e2
--- /dev/null
+++ b/website/client/components/achievements/genericAchievement.vue
@@ -0,0 +1,42 @@
+
+ b-modal#generic-achievement(:title='data.message', size='md', :hide-footer='true')
+ .modal-body
+ .col-12
+ achievement-avatar.avatar
+ .col-6.offset-3.text-center
+ p(v-html='data.modalText')
+ button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
+ achievement-footer
+
+
+
+
+
diff --git a/website/client/components/achievements/justAddWater.vue b/website/client/components/achievements/justAddWater.vue
new file mode 100644
index 0000000000..c89d252b8b
--- /dev/null
+++ b/website/client/components/achievements/justAddWater.vue
@@ -0,0 +1,46 @@
+
+ b-modal#just-add-water(:title='title', size='md', :hide-footer='true')
+ .modal-body
+ .col-12
+ achievement-avatar.avatar
+ .col-6.offset-3.text-center
+ p {{ $t('achievementJustAddWaterModalText') }}
+ button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
+ achievement-footer
+
+
+
+
+
diff --git a/website/client/components/achievements/lostMasterclasser.vue b/website/client/components/achievements/lostMasterclasser.vue
new file mode 100644
index 0000000000..8ff6ef6c46
--- /dev/null
+++ b/website/client/components/achievements/lostMasterclasser.vue
@@ -0,0 +1,46 @@
+
+ b-modal#lost-masterclasser(:title='title', size='md', :hide-footer='true')
+ .modal-body
+ .col-12
+ achievement-avatar.avatar
+ .col-6.offset-3.text-center
+ p {{ $t('achievementLostMasterclasserModalText') }}
+ button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
+ achievement-footer
+
+
+
+
+
diff --git a/website/client/components/achievements/mindOverMatter.vue b/website/client/components/achievements/mindOverMatter.vue
new file mode 100644
index 0000000000..013891d5d2
--- /dev/null
+++ b/website/client/components/achievements/mindOverMatter.vue
@@ -0,0 +1,46 @@
+
+ b-modal#mind-over-matter(:title='title', size='md', :hide-footer='true')
+ .modal-body
+ .col-12
+ achievement-avatar.avatar
+ .col-6.offset-3.text-center
+ p {{ $t('achievementMindOverMatterModalText') }}
+ button.btn.btn-primary(@click='close()') {{ $t('huzzah') }}
+ achievement-footer
+
+
+
+
+
diff --git a/website/client/components/header/notifications/justAddWater.vue b/website/client/components/header/notifications/justAddWater.vue
new file mode 100644
index 0000000000..9889b28cc4
--- /dev/null
+++ b/website/client/components/header/notifications/justAddWater.vue
@@ -0,0 +1,30 @@
+
+ base-notification(
+ :can-remove="canRemove",
+ :notification="notification",
+ :read-after-click="true",
+ @click="action"
+ )
+ div(slot="content", v-html="achievementString")
+
+
+
diff --git a/website/client/components/header/notifications/lostMasterclasser.vue b/website/client/components/header/notifications/lostMasterclasser.vue
new file mode 100644
index 0000000000..c72aa02d3e
--- /dev/null
+++ b/website/client/components/header/notifications/lostMasterclasser.vue
@@ -0,0 +1,30 @@
+
+ base-notification(
+ :can-remove="canRemove",
+ :notification="notification",
+ :read-after-click="true",
+ @click="action"
+ )
+ div(slot="content", v-html="achievementString")
+
+
+
diff --git a/website/client/components/header/notifications/mindOverMatter.vue b/website/client/components/header/notifications/mindOverMatter.vue
new file mode 100644
index 0000000000..9044a25a91
--- /dev/null
+++ b/website/client/components/header/notifications/mindOverMatter.vue
@@ -0,0 +1,30 @@
+
+ base-notification(
+ :can-remove="canRemove",
+ :notification="notification",
+ :read-after-click="true",
+ @click="action"
+ )
+ div(slot="content", v-html="achievementString")
+
+
+
diff --git a/website/client/components/header/notificationsDropdown.vue b/website/client/components/header/notificationsDropdown.vue
index 6fce4cc0cf..526bbece0b 100644
--- a/website/client/components/header/notificationsDropdown.vue
+++ b/website/client/components/header/notificationsDropdown.vue
@@ -95,6 +95,9 @@ import NEW_INBOX_MESSAGE from './notifications/newInboxMessage';
import NEW_CHAT_MESSAGE from './notifications/newChatMessage';
import WORLD_BOSS from './notifications/worldBoss';
import VERIFY_USERNAME from './notifications/verifyUsername';
+import ACHIEVEMENT_JUST_ADD_WATER from './notifications/justAddWater';
+import ACHIEVEMENT_LOST_MASTERCLASSER from './notifications/lostMasterclasser';
+import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter';
export default {
components: {
@@ -106,6 +109,7 @@ export default {
QUEST_INVITATION, GROUP_TASK_APPROVAL, GROUP_TASK_APPROVED, GROUP_TASK_ASSIGNED,
UNALLOCATED_STATS_POINTS, NEW_MYSTERY_ITEMS, CARD_RECEIVED,
NEW_INBOX_MESSAGE, NEW_CHAT_MESSAGE,
+ ACHIEVEMENT_JUST_ADD_WATER, ACHIEVEMENT_LOST_MASTERCLASSER, ACHIEVEMENT_MIND_OVER_MATTER,
WorldBoss: WORLD_BOSS,
VERIFY_USERNAME,
},
@@ -130,6 +134,7 @@ export default {
'QUEST_INVITATION', 'GROUP_TASK_ASSIGNED', 'GROUP_TASK_APPROVAL', 'GROUP_TASK_APPROVED',
'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
+ 'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER',
'VERIFY_USERNAME',
],
};
diff --git a/website/client/components/notifications.vue b/website/client/components/notifications.vue
index 75f2cf0adb..00a8a6baa4 100644
--- a/website/client/components/notifications.vue
+++ b/website/client/components/notifications.vue
@@ -26,6 +26,10 @@ div
quest-completed
quest-invitation
verify-username
+ generic-achievement(:data='notificationData')
+ just-add-water
+ lost-masterclasser
+ mind-over-matter