diff --git a/migrations/archive/2019/20191022_pet_color_achievements.js b/migrations/archive/2019/20191022_pet_color_achievements.js new file mode 100644 index 0000000000..493d25412b --- /dev/null +++ b/migrations/archive/2019/20191022_pet_color_achievements.js @@ -0,0 +1,82 @@ +/* eslint-disable no-console */ +const MIGRATION_NAME = '20191022_pet_color_achievements'; +import { model as User } from '../../../website/server/models/user'; + +const progressCount = 1000; +let count = 0; + +async function updateUser (user) { + count++; + + let set = { + migration: MIGRATION_NAME, + }; + + if (user && user.items && user.items.pets) { + const pets = user.items.pets; + if (pets['Wolf-Zombie'] > 0 + && pets['TigerCub-Zombie'] > 0 + && pets['PandaCub-Zombie'] > 0 + && pets['LionCub-Zombie'] > 0 + && pets['Fox-Zombie'] > 0 + && pets['FlyingPig-Zombie'] > 0 + && pets['Dragon-Zombie'] > 0 + && pets['Cactus-Zombie'] > 0 + && pets['BearCub-Zombie'] > 0) { + set['achievements.monsterMagus'] = true; + } + } + + if (user && user.items && user.items.mounts) { + const mounts = user.items.mounts; + if (mounts['Wolf-Zombie'] + && mounts['TigerCub-Zombie'] + && mounts['PandaCub-Zombie'] + && mounts['LionCub-Zombie'] + && mounts['Fox-Zombie'] + && mounts['FlyingPig-Zombie'] + && mounts['Dragon-Zombie'] + && mounts['Cactus-Zombie'] + && mounts['BearCub-Zombie'] ) { + set['achievements.undeadUndertaker'] = 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('2019-10-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/components/notifications.vue b/website/client/components/notifications.vue index b5e688c1ad..ca8ba10dc1 100644 --- a/website/client/components/notifications.vue +++ b/website/client/components/notifications.vue @@ -175,6 +175,16 @@ const NOTIFICATIONS = { label: ($t) => `${$t('achievement')}: ${$t('achievementAridAuthority')}`, modalId: 'generic-achievement', }, + ACHIEVEMENT_MONSTER_MAGUS: { + achievement: true, + label: ($t) => `${$t('achievement')}: ${$t('achievementMonsterMagus')}`, + modalId: 'generic-achievement', + }, + ACHIEVEMENT_UNDEAD_UNDERTAKER: { + achievement: true, + label: ($t) => `${$t('achievement')}: ${$t('achievementUndeadUndertaker')}`, + modalId: 'generic-achievement', + }, }; export default { @@ -230,7 +240,9 @@ export default { 'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT', 'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL', 'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS', - 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', 'GENERIC_ACHIEVEMENT', + 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', + 'ACHIEVEMENT_MONSTER_MAGUS', 'ACHIEVEMENT_UNDEAD_UNDERTAKER', + 'GENERIC_ACHIEVEMENT', ].forEach(type => { handledNotifications[type] = true; }); @@ -607,6 +619,8 @@ export default { case 'ACHIEVEMENT_BACK_TO_BASICS': case 'ACHIEVEMENT_DUST_DEVIL': case 'ACHIEVEMENT_ARID_AUTHORITY': + case 'ACHIEVEMENT_MONSTER_MAGUS': + case 'ACHIEVEMENT_UNDEAD_UNDERTAKER': case 'GENERIC_ACHIEVEMENT': this.showNotificationWithModal(notification); break; diff --git a/website/common/locales/en/achievements.json b/website/common/locales/en/achievements.json index 7de4a1baf9..956ac4af35 100644 --- a/website/common/locales/en/achievements.json +++ b/website/common/locales/en/achievements.json @@ -26,5 +26,11 @@ "achievementAridAuthorityText": "Has tamed all Desert Mounts.", "achievementAridAuthorityModalText": "You tamed all the Desert Mounts!", "achievementKickstarter2019": "Pin Kickstarter Backer", - "achievementKickstarter2019Text": "Backed the 2019 Pin Kickstarter Project" + "achievementKickstarter2019Text": "Backed the 2019 Pin Kickstarter Project", + "achievementMonsterMagus": "Monster Magus", + "achievementMonsterMagusText": "Has collected all Zombie Pets.", + "achievementMonsterMagusModalText": "You collected all the Zombie Pets!", + "achievementUndeadUndertaker": "Undead Undertaker", + "achievementUndeadUndertakerText": "Has tamed all Zombie Mounts.", + "achievementUndeadUndertakerModalText": "You tamed all the Zombie Mounts!" } diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index c39939383d..56b31bae38 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -157,6 +157,16 @@ let basicAchievs = { titleKey: 'achievementAridAuthority', textKey: 'achievementAridAuthorityText', }, + monsterMagus: { + icon: 'achievement-monsterMagus', + titleKey: 'achievementMonsterMagus', + textKey: 'achievementMonsterMagusText', + }, + undeadUndertaker: { + icon: 'achievement-undeadUndertaker', + titleKey: 'achievementUndeadUndertaker', + textKey: 'achievementUndeadUndertakerText', + }, }; Object.assign(achievementsData, basicAchievs); diff --git a/website/common/script/content/constants.js b/website/common/script/content/constants.js index 5cc052e1b7..aff4d9fad9 100644 --- a/website/common/script/content/constants.js +++ b/website/common/script/content/constants.js @@ -266,4 +266,5 @@ export const QUEST_SERIES_ACHIEVEMENTS = { export const ANIMAL_COLOR_ACHIEVEMENTS = [ {color: 'Base', petAchievement: 'backToBasics', petNotificationType: 'ACHIEVEMENT_BACK_TO_BASICS', mountAchievement: 'allYourBase', mountNotificationType: 'ACHIEVEMENT_ALL_YOUR_BASE'}, {color: 'Desert', petAchievement: 'dustDevil', petNotificationType: 'ACHIEVEMENT_DUST_DEVIL', mountAchievement: 'aridAuthority', mountNotificationType: 'ACHIEVEMENT_ARID_AUTHORITY'}, + {color: 'Zombie', petAchievement: 'monsterMagus', petNotificationType: 'ACHIEVEMENT_MONSTER_MAGUS', mountAchievement: 'undeadUndertaker', mountNotificationType: 'ACHIEVEMENT_UNDEAD_UNDERTAKER'}, ]; diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 64ac15b06c..207d0babf7 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -190,6 +190,8 @@ function _getBasicAchievements (user, language) { _addSimple(result, user, {path: 'allYourBase', language}); _addSimple(result, user, {path: 'dustDevil', language}); _addSimple(result, user, {path: 'aridAuthority', language}); + _addSimple(result, user, {path: 'monsterMagus', language}); + _addSimple(result, user, {path: 'undeadUndertaker', language}); _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'mountMaster', language}); diff --git a/website/raw_sprites/spritesmith/achievements/achievement-monsterMagus2x.png b/website/raw_sprites/spritesmith/achievements/achievement-monsterMagus2x.png new file mode 100644 index 0000000000..02a7c0d359 Binary files /dev/null and b/website/raw_sprites/spritesmith/achievements/achievement-monsterMagus2x.png differ diff --git a/website/raw_sprites/spritesmith/achievements/achievement-undeadUndertaker2x.png b/website/raw_sprites/spritesmith/achievements/achievement-undeadUndertaker2x.png new file mode 100644 index 0000000000..6ffcceeeea Binary files /dev/null and b/website/raw_sprites/spritesmith/achievements/achievement-undeadUndertaker2x.png differ diff --git a/website/raw_sprites/spritesmith_large/promo_zombie_achievements.png b/website/raw_sprites/spritesmith_large/promo_zombie_achievements.png new file mode 100644 index 0000000000..0ef72d7927 Binary files /dev/null and b/website/raw_sprites/spritesmith_large/promo_zombie_achievements.png differ diff --git a/website/server/controllers/api-v3/news.js b/website/server/controllers/api-v3/news.js index 2f13e3726c..d86da3e8c6 100644 --- a/website/server/controllers/api-v3/news.js +++ b/website/server/controllers/api-v3/news.js @@ -3,7 +3,7 @@ import { authWithHeaders } from '../../middlewares/auth'; let api = {}; // @TODO export this const, cannot export it from here because only routes are exported from controllers -const LAST_ANNOUNCEMENT_TITLE = 'HABITICA HIRING ANDROID DEVELOPER! AND BLOG POST ON THE QUEST SHOP'; +const LAST_ANNOUNCEMENT_TITLE = 'NEW PET COLLECTION BADGES!'; const worldDmg = { // @TODO bailey: false, }; @@ -30,17 +30,14 @@ api.getNews = {
Want to join the Habitica team? We’re looking to hire a new developer to help with our Android app! Our ideal candidate is a mobile developer with experience in Kotlin who is capable of supporting existing legacy code and working with our team to continue improving the user experience. You'll make major contributions that help serve and grow an audience of millions of users!
-If this sounds like a job you'll love, check out the full job posting for more information and to fill out an application.
- -This month's featured Wiki article is about the Quest Shop! We hope that it will help you as you look for additional ways to keep your Habitica adventure fun and motivating. Be sure to check it out, and let us know what you think by reaching out on Twitter, Tumblr, and Facebook.
-We're releasing a new achievement so you can celebrate your successes in the world of Habitican pet collecting! Earn the Monster Magus and Undead Undertaker achievements by collecting Zombie pets and mounts and you'll earn a nifty badge for your profile.
+If you already have all the Zombie pets and/or mounts in your stable, you'll receive the badge automatically! Check your profile and celebrate your new achievement with pride.
+