From 0dd8cab2a0966f2b2be915af723095e3bbf967ef Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 22 Oct 2019 10:19:02 -0500 Subject: [PATCH] feat(content): Zombie color bingo achievements --- .../2019/20191022_pet_color_achievements.js | 82 ++++++++++++++++++ website/client/components/notifications.vue | 16 +++- website/common/locales/en/achievements.json | 8 +- website/common/script/content/achievements.js | 10 +++ website/common/script/content/constants.js | 1 + website/common/script/libs/achievements.js | 2 + .../achievement-monsterMagus2x.png | Bin 0 -> 11661 bytes .../achievement-undeadUndertaker2x.png | Bin 0 -> 4539 bytes .../promo_zombie_achievements.png | Bin 0 -> 2111 bytes website/server/controllers/api-v3/news.js | 15 ++-- website/server/models/user/schema.js | 2 + website/server/models/userNotification.js | 2 + 12 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 migrations/archive/2019/20191022_pet_color_achievements.js create mode 100644 website/raw_sprites/spritesmith/achievements/achievement-monsterMagus2x.png create mode 100644 website/raw_sprites/spritesmith/achievements/achievement-undeadUndertaker2x.png create mode 100644 website/raw_sprites/spritesmith_large/promo_zombie_achievements.png 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 0000000000000000000000000000000000000000..02a7c0d359401d5f561634b28b6e5b4bee7f02a8 GIT binary patch literal 11661 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6ffcceeeea8b9cd380e777e05da260700cbada94 GIT binary patch literal 4539 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0ef72d7927236cc985e63be79d4fa457a673ed75 GIT binary patch literal 2111 zcmYLK2{aqn8jfemlxpXd3MpFChKik`9g+6cQcEl)i-@JDeTkw}LNPrrMXV(RGql>; zf~v+&bRJKQ?SvS6(ITWGNhwVU@uD+t=H7GffA4?Jz303C{lD)|zv*NnCj*iJ0044! zwpOlUJ1ic7-%5$+)QTj6*hu-oZL9!?NBs%CDqrk5e#h251^|$iKN=E%l3$d>PU*XL zj@Hu4GT$hi(fycz&?@!}+gV*l#?Nv~lY>zKO8pK0Y*NywymF>i`FYlN@;0P*BIb?q zii>*I_H&@!R5VE`Um0@4l-%>=qfJd3+?cFE)UvRzTs0_Q<=ASbXA&BP}KW5x=t`fXGw>I;B_e&QaAE_ZJM)G&N>9hCzKsK>E>XX;vX6r&7*ODOH zkN9M?(6uiL=r~+eJKqd=YXC_9=rcP}%i95Q6p-~P>&g;sP;`sJSl4 z8Nv;s#Jh(Q>^^%%W=NxbBI<(!e{<}aO#sSd!uS|!2&P{(o1Ku8gAepXqYy9z>H0*q z!4V+3!@yD;w(j)nWd~x$9L1!qjrQpV8ucu{(9>H=pe<12vqKrwP_$;mWPgy?+epE} zgB9W*P+h@8ZdHAdO-0$-{Zd6#v#>oMUEPJLrq-0{hKT0+DxfVubA6-|0H>6qpykdi zE&Og$6+hkAak74;cfuQnAcSewD{Y;2V~|0ye~e%KgAQM zCHuO@qEX}NPGFp7eeC*OV9s3fc^B9?N99!uL>GU&JaFX!2A^IVw1;dkDU*>*$&*kh z&m)qB7tM6BJmD!pGV!(E;s{k@XYqjJ$1QUwRI2b6Sbko@9|9*DyEw(MrS(+``M<%_ z{5}*he-iPT_lM~XB9D*RCfl6c-hZfQyCvqBT~U%#-+GyQX=B^G0T)MH z=>wgM#-;VR&(p@Mo>dvu4>-bm&CJYd3;>su{{<=t7d8**C1J}l!)}?{E-2Qbe*$K>{>&z<_{g7j8B?G@wx(zGTd#DB?^kp&%_S?1fA zy$E>oO_N{9G(+=3>1|zQfW1`i_h6{WG73mRW|LxH6proeSiZ`h+M*|hm`8ZDYpjFZ zRcW5vIw2O5d0A{wF-5@SzrZ*LuQ!i?_0=09S*#D;6=T;V{;}W&4WWq0`{<^GhIWYI z*U((D0B2-`V&FZXO8GbfZgywb6CSt`-@qQP%+L@mA7oFOObO@|{BdPkupgO1lz99f zrU6F_2{|DKHY!UF|M;%@EyecoGq5w(kGqXiWe_bs6vu_c1-4krQyA!=30lqB7oBZh z8aod?_kOR%tf`f(F0B?#8Q;P;zEKfw;DI@hU+dOlTN(8+n>xF5VM&_nY!Q>t)0dh& z&!?}7)1G|d3Gh;}pYFUH^3+{!ZeapEwTbMZn$s3QpT3F0bVXH;(+VxmwHsO`6a`DDd;vF& zQpgZ@{IBw4brs=qYPCbq)4T(b_89Zv%%TuzA6N-ma@M3m{9n}_ zj>MmR&%c6rzjWO4g53CsA=Ajc1_NbW+nvs;TPuuA|D5LW>iq2H)!Q4V;tb`rMz<)Y zo$;*J<^@ABYw<39%n`l2mYIXY-uxoZ=qe%2FzWb`C`jXl*TG!#f7hv!GN=!D)-68B z=gJ($B%a}D(ObuFLrm%r$zKx736;SDhh!gH!74U#;}}o?lt)%5I;Sb%xMNuUmt3yD zt``HWZ~s$+xWTX zRf~^f#zV0ZF_*QRuA{i#>+X%Lzl4}}#Tz!G^^+cSD%+9sd$6pu>NES%Wr3mODA&^2 zB>P^}EY!!Yy5y=0v?U-X<8moxnU1rocKiPgqeJ9jCZMmx6H%AmlDkO_+S_IyRQf8S zQ^!j(TaXaojw?z1PbK%R^H--<5)|F&3iCAhWpc`4RVXzmY65Nt56b&m819^d6jub1 zDncjYYJxG*gh~t*J*;XVrru>S`wH)&Ea@b~1oNtv!&j~UyM`XE*cJQIZ@d

${res.t('newStuff')}

-

10/17/2019 - ${LAST_ANNOUNCEMENT_TITLE}

+

10/22/2019 - ${LAST_ANNOUNCEMENT_TITLE}


-

Habitica is Hiring! Android Developer Position

-

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.

-
-

Blog Post: Quest Shop

-

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.

-
by shanaqui and the Wiki Wizards
+
+

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.

+
By OuttaMyMind and SabreCat
`, }); diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index af67e08522..a1671af012 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -128,6 +128,8 @@ let schema = new Schema({ dustDevil: Boolean, aridAuthority: Boolean, kickstarter2019: Boolean, + monsterMagus: Boolean, + undeadUndertaker: Boolean, }, backer: { diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index aa3ed7abfc..003da20c63 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -40,6 +40,8 @@ const NOTIFICATION_TYPES = [ 'ACHIEVEMENT_MIND_OVER_MATTER', 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', + 'ACHIEVEMENT_MONSTER_MAGUS', + 'ACHIEVEMENT_UNDEAD_UNDERTAKER', ]; const Schema = mongoose.Schema;