diff --git a/migrations/archive/2021/20210525_pet_color_achievements.js b/migrations/archive/2021/20210525_pet_color_achievements.js new file mode 100644 index 0000000000..66cea73444 --- /dev/null +++ b/migrations/archive/2021/20210525_pet_color_achievements.js @@ -0,0 +1,82 @@ +/* eslint-disable no-console */ +const MIGRATION_NAME = '20210525_pet_color_achievements'; +import { model as User } from '../../../website/server/models/user'; + +const progressCount = 1000; +let count = 0; + +async function updateUser (user) { + count++; + + const set = { + migration: MIGRATION_NAME, + }; + + if (user && user.items && user.items.pets) { + const pets = user.items.pets; + if (pets['Wolf-CottonCandyBlue'] > 0 + && pets['TigerCub-CottonCandyBlue'] > 0 + && pets['PandaCub-CottonCandyBlue'] > 0 + && pets['LionCub-CottonCandyBlue'] > 0 + && pets['Fox-CottonCandyBlue'] > 0 + && pets['FlyingPig-CottonCandyBlue'] > 0 + && pets['Dragon-CottonCandyBlue'] > 0 + && pets['Cactus-CottonCandyBlue'] > 0 + && pets['BearCub-CottonCandyBlue'] > 0) { + set['achievements.violetsAreBlue'] = true; + } + } + + if (user && user.items && user.items.mounts) { + const mounts = user.items.mounts; + if (mounts['Wolf-CottonCandyBlue'] + && mounts['TigerCub-CottonCandyBlue'] + && mounts['PandaCub-CottonCandyBlue'] + && mounts['LionCub-CottonCandyBlue'] + && mounts['Fox-CottonCandyBlue'] + && mounts['FlyingPig-CottonCandyBlue'] + && mounts['Dragon-CottonCandyBlue'] + && mounts['Cactus-CottonCandyBlue'] + && mounts['BearCub-CottonCandyBlue'] ) { + set['achievements.wildBlueYonder'] = true; + } + } + + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + + return await User.update({ _id: user._id }, { $set: set }).exec(); +} + +module.exports = async function processUsers () { + let query = { + migration: { $ne: MIGRATION_NAME }, + 'auth.timestamps.loggedin': { $gt: new Date('2021-05-01') }, + }; + + const fields = { + _id: 1, + items: 1, + }; + + while (true) { // eslint-disable-line no-constant-condition + const users = await User // eslint-disable-line no-await-in-loop + .find(query) + .limit(250) + .sort({_id: 1}) + .select(fields) + .lean() + .exec(); + + if (users.length === 0) { + console.warn('All appropriate users found and modified.'); + console.warn(`\n${count} users processed\n`); + break; + } else { + query._id = { + $gt: users[users.length - 1]._id, + }; + } + + await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop + } +}; diff --git a/website/client/src/components/notifications.vue b/website/client/src/components/notifications.vue index d4ebf738d3..770b8bdeb4 100644 --- a/website/client/src/components/notifications.vue +++ b/website/client/src/components/notifications.vue @@ -403,6 +403,22 @@ const NOTIFICATIONS = { achievement: 'seasonalSpecialist', }, }, + ACHIEVEMENT_VIOLETS_ARE_BLUE: { + achievement: true, + label: $t => `${$t('achievement')}: ${$t('achievementVioletsAreBlue')}`, + modalId: 'generic-achievement', + data: { + achievement: 'violetsAreBlue', + }, + }, + ACHIEVEMENT_WILD_BLUE_YONDER: { + achievement: true, + label: $t => `${$t('achievement')}: ${$t('achievementWildBlueYonder')}`, + modalId: 'generic-achievement', + data: { + achievement: 'wildBlueYonder', + }, + }, }; export default { @@ -466,6 +482,7 @@ export default { 'ACHIEVEMENT_FRESHWATER_FRIENDS', 'ACHIEVEMENT_GOOD_AS_GOLD', 'ACHIEVEMENT_ALL_THAT_GLITTERS', 'ACHIEVEMENT_BONE_COLLECTOR', 'ACHIEVEMENT_SKELETON_CREW', 'ACHIEVEMENT_SEEING_RED', 'ACHIEVEMENT_RED_LETTER_DAY', 'ACHIEVEMENT_LEGENDARY_BESTIARY', 'ACHIEVEMENT_SEASONAL_SPECIALIST', + 'ACHIEVEMENT_VIOLETS_ARE_BLUE', 'ACHIEVEMENT_WILD_BLUE_YONDER', ].forEach(type => { handledNotifications[type] = true; }); @@ -890,6 +907,8 @@ export default { case 'ACHIEVEMENT_RED_LETTER_DAY': case 'ACHIEVEMENT_LEGENDARY_BESTIARY': case 'ACHIEVEMENT_SEASONAL_SPECIALIST': + case 'ACHIEVEMENT_VIOLETS_ARE_BLUE': + case 'ACHIEVEMENT_WILD_BLUE_YONDER': case 'GENERIC_ACHIEVEMENT': this.showNotificationWithModal(notification); break; diff --git a/website/common/locales/en/achievements.json b/website/common/locales/en/achievements.json index 85a64c9870..799274037b 100644 --- a/website/common/locales/en/achievements.json +++ b/website/common/locales/en/achievements.json @@ -108,5 +108,11 @@ "achievementLegendaryBestiaryModalText": "You collected all the mythical pets!", "achievementSeasonalSpecialist": "Seasonal Specialist", "achievementSeasonalSpecialistText": "Has completed all the Spring and Winter seasonal quests: Egg Hunt, Trapper Santa, and Find the Cub!", - "achievementSeasonalSpecialistModalText": "You completed all the seasonal quests!" + "achievementSeasonalSpecialistModalText": "You completed all the seasonal quests!", + "achievementVioletsAreBlue": "Violets are Blue", + "achievementVioletsAreBlueText": "Has collected all Cotton Candy Blue Pets.", + "achievementVioletsAreBlueModalText": "You collected all the Cotton Candy Blue Pets!", + "achievementWildBlueYonder": "Wild Blue Yonder", + "achievementWildBlueYonderText": "Has tamed all Cotton Candy Blue Mounts.", + "achievementWildBlueYonderModalText": "You tamed all the Cotton Candy Blue Mounts!" } diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index 68551150f3..e2dfdce6d6 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -242,6 +242,16 @@ const basicAchievs = { titleKey: 'achievementSeasonalSpecialist', textKey: 'achievementSeasonalSpecialistText', }, + violetsAreBlue: { + icon: 'achievement-violetsAreBlue', + titleKey: 'achievementVioletsAreBlue', + textKey: 'achievementVioletsAreBlueText', + }, + wildBlueYonder: { + icon: 'achievement-wildBlueYonder', + titleKey: 'achievementWildBlueYonder', + textKey: 'achievementWildBlueYonderText', + }, }; Object.assign(achievementsData, basicAchievs); diff --git a/website/common/script/content/constants/animalColorAchievements.js b/website/common/script/content/constants/animalColorAchievements.js index df2918faae..e507cb7355 100644 --- a/website/common/script/content/constants/animalColorAchievements.js +++ b/website/common/script/content/constants/animalColorAchievements.js @@ -55,6 +55,13 @@ const ANIMAL_COLOR_ACHIEVEMENTS = [ mountAchievement: 'redLetterDay', mountNotificationType: 'ACHIEVEMENT_RED_LETTER_DAY', }, + { + color: 'CottonCandyBlue', + petAchievement: 'violetsAreBlue', + petNotificationType: 'ACHIEVEMENT_VIOLETS_ARE_BLUE', + mountAchievement: 'wildBlueYonder', + mountNotificationType: 'ACHIEVEMENT_WILD_BLUE_YONDER', + }, ]; export default ANIMAL_COLOR_ACHIEVEMENTS; diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 6fb63a7225..9f59d5b501 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -210,6 +210,8 @@ function _getBasicAchievements (user, language) { _addSimple(result, user, { path: 'redLetterDay', language }); _addSimple(result, user, { path: 'legendaryBestiary', language }); _addSimple(result, user, { path: 'seasonalSpecialist', language }); + _addSimple(result, user, { path: 'violetsAreBlue', language }); + _addSimple(result, user, { path: 'wildBlueYonder', language }); _addSimpleWithMasterCount(result, user, { path: 'beastMaster', language }); _addSimpleWithMasterCount(result, user, { path: 'mountMaster', language }); diff --git a/website/raw_sprites/spritesmith/achievements/achievement-violetsAreBlue2x.png b/website/raw_sprites/spritesmith/achievements/achievement-violetsAreBlue2x.png new file mode 100644 index 0000000000..d360f2d47d Binary files /dev/null and b/website/raw_sprites/spritesmith/achievements/achievement-violetsAreBlue2x.png differ diff --git a/website/raw_sprites/spritesmith/achievements/achievement-wildBlueYonder2x.png b/website/raw_sprites/spritesmith/achievements/achievement-wildBlueYonder2x.png new file mode 100644 index 0000000000..6ae52e2704 Binary files /dev/null and b/website/raw_sprites/spritesmith/achievements/achievement-wildBlueYonder2x.png differ diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 5c64499f1d..9114767e3e 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -142,6 +142,8 @@ export default new Schema({ redLetterDay: Boolean, legendaryBestiary: Boolean, seasonalSpecialist: Boolean, + violetsAreBlue: Boolean, + wildBlueYonder: Boolean, // Onboarding Guide createdTask: Boolean, completedTask: Boolean, diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index 45b0f1bdba..5bee1533c8 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -65,6 +65,8 @@ const NOTIFICATION_TYPES = [ 'ACHIEVEMENT_RED_LETTER_DAY', 'ACHIEVEMENT_LEGENDARY_BESTIARY', 'ACHIEVEMENT_SEASONAL_SPECIALIST', + 'ACHIEVEMENT_VIOLETS_ARE_BLUE', + 'ACHIEVEMENT_WILD_BLUE_YONDER', 'ACHIEVEMENT', // generic achievement notification, details inside `notification.data` 'DROP_CAP_REACHED', ];