diff --git a/migrations/archive/2024/20240318_pet_group_achievements.js b/migrations/archive/2024/20240318_pet_group_achievements.js new file mode 100644 index 0000000000..3ff3bd4280 --- /dev/null +++ b/migrations/archive/2024/20240318_pet_group_achievements.js @@ -0,0 +1,89 @@ +/* eslint-disable no-console */ +const MIGRATION_NAME = '202403_pet_group_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['GuineaPig-Zombie'] > 0 + && pets['GuineaPig-Skeleton'] > 0 + && pets['GuineaPig-Base'] > 0 + && pets['GuineaPig-Desert'] > 0 + && pets['GuineaPig-Red'] > 0 + && pets['GuineaPig-Shade'] > 0 + && pets['GuineaPig-White']> 0 + && pets['GuineaPig-Golden'] > 0 + && pets['GuineaPig-CottonCandyBlue'] > 0 + && pets['GuineaPig-CottonCandyPink'] > 0 + && pets['Squirrel-Zombie'] > 0 + && pets['Squirrel-Skeleton'] > 0 + && pets['Squirrel-Base'] > 0 + && pets['Squirrel-Desert'] > 0 + && pets['Squirrel-Red'] > 0 + && pets['Squirrel-Shade'] > 0 + && pets['Squirrel-White'] > 0 + && pets['Squirrel-Golden'] > 0 + && pets['Squirrel-CottonCandyBlue'] > 0 + && pets['Squirrel-CottonCandyPink'] > 0 + && pets['Rat-Zombie'] > 0 + && pets['Rat-Skeleton'] > 0 + && pets['Rat-Base'] > 0 + && pets['Rat-Desert'] > 0 + && pets['Rat-Red'] > 0 + && pets['Rat-Shade'] > 0 + && pets['Rat-White'] > 0 + && pets['Rat-Golden'] > 0 + && pets['Rat-CottonCandyBlue'] > 0 + && pets['Rat-CottonCandyPink'] > 0 ) { + set['achievements.rodentRuler'] = true; + + } + } + + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + + return await User.updateOne({ _id: user._id }, { $set: set }).exec(); +} + +export default async function processUsers () { + let query = { + migration: { $ne: MIGRATION_NAME }, + 'auth.timestamps.loggedin': { $gt: new Date('2024-02-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/migrations/users/full-stable.js b/migrations/users/full-stable.js index df29a1fd38..01d6e04464 100644 --- a/migrations/users/full-stable.js +++ b/migrations/users/full-stable.js @@ -51,7 +51,7 @@ async function updateUser (user) { if (count % progressCount === 0) console.warn(`${count} ${user._id}`); - return User.update({ _id: user._id }, { $set: set }).exec(); + return User.updateOne({ _id: user._id }, { $set: set }).exec(); } export default async function processUsers () { diff --git a/migrations/users/pi-day.js b/migrations/users/pi-day.js index dd3f7d4f3f..211ba92c69 100644 --- a/migrations/users/pi-day.js +++ b/migrations/users/pi-day.js @@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid'; import { model as User } from '../../website/server/models/user'; -const MIGRATION_NAME = '20230314_pi_day'; +const MIGRATION_NAME = '20240314_pi_day'; const progressCount = 1000; let count = 0; diff --git a/test/common/ops/buy/purchase.js b/test/common/ops/buy/purchase.js index 962175d2ae..8f70523d1c 100644 --- a/test/common/ops/buy/purchase.js +++ b/test/common/ops/buy/purchase.js @@ -197,7 +197,7 @@ describe('shared.ops.purchase', () => { it('purchases quest bundles', async () => { const startingBalance = user.balance; - const clock = sandbox.useFakeTimers(moment('2022-03-16').valueOf()); + const clock = sandbox.useFakeTimers(moment('2024-03-20').valueOf()); const type = 'bundles'; const key = 'cuddleBuddies'; const price = 1.75; diff --git a/website/common/locales/en/achievements.json b/website/common/locales/en/achievements.json index 37cacdba52..cf9ab6edbd 100644 --- a/website/common/locales/en/achievements.json +++ b/website/common/locales/en/achievements.json @@ -156,8 +156,12 @@ "achievementBonelessBossModalText": "You collected all the invertebrate pets!", "achievementDuneBuddy": "Dune Buddy", "achievementDuneBuddyText": "Has hatched all standard colors of desert dwelling pets: Armadillo, Cactus, Fox, Frog, Snake, and Spider!", - "achievementDuneBuddyNotes": "You collected all the desert dwelling pets!", + "achievementDuneBuddyModalText": "You collected all the desert dwelling pets!", "achievementRoughRider": "Rough Rider", "achievementRoughRiderText": "Has hatched all basic colors of the uncomfortable pets and mounts: Cactus, Hedgehog, and Rock!", - "achievementRoughRiderNotes": "You collected all the basic colors of the uncomfortable pets and mounts!" + "achievementRoughRiderModalText": "You collected all the basic colors of the uncomfortable pets and mounts!", + "achievementRodentRuler": "Rodent Ruler", + "achievementRodentRulerText": "Has hatched all standard colors of rodent pets: Guinea Pig, Rat, and Squirrel!", + "achievementRodentRulerModalText": "You collected all the rodent pets!" + } diff --git a/website/common/locales/en/backgrounds.json b/website/common/locales/en/backgrounds.json index e57cfb38a2..b354ddc531 100644 --- a/website/common/locales/en/backgrounds.json +++ b/website/common/locales/en/backgrounds.json @@ -955,6 +955,14 @@ "backgroundHeartTreeTunnelText": "Heart Tree Tunnel", "backgroundHeartTreeTunnelNotes": "Drift through the Heart Tree Tunnel.", + "backgrounds032024": "SET 117: Released March 2024", + "backgroundFloweringForestText": "Flowering Forest", + "backgroundFloweringForestNotes": "Breathe in the perfume of a Flowering Forest.", + "backgroundRainyRainforestText": "Rainy Rainforest", + "backgroundRainyRainforestNotes": "Enjoy a refreshing downpour in the Rainy Ranforest.", + "backgroundDogParkText": "Dog Park", + "backgroundDogParkNotes": "Frolic at the Dog Park.", + "timeTravelBackgrounds": "Steampunk Backgrounds", "backgroundAirshipText": "Airship", "backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.", diff --git a/website/common/locales/en/gear.json b/website/common/locales/en/gear.json index b78605e1f2..0036294dbd 100644 --- a/website/common/locales/en/gear.json +++ b/website/common/locales/en/gear.json @@ -538,6 +538,8 @@ "weaponMystery202306Notes": "Shine proud and bring a shimmering prism of color wherever you go! Confers no benefit. June 2023 Subscriber Item.", "weaponMystery202311Text": "All-Seeing Staff", "weaponMystery202311Notes": "See beyond the bounds of space and time! Confers no benefit. November 2023 Subscriber Item.", + "weaponMystery202403Text": "Lucky Emerald Sword", + "weaponMystery202403Notes": "Carrying the biggest sword around is surely a way to create your own luck! Confers no benefit. March 2024 Subscriber Item.", "weaponMystery301404Text": "Steampunk Cane", "weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.", @@ -738,6 +740,8 @@ "weaponArmoireRollingPinNotes": "Roll your dough as thin as you like in-between bonking bad habits when they pop up around you like a certain rodent-bopping game. Increases Strength by <%= str %>. Enchanted Armoire: Cooking Implements Set 2 (Item 2 of 2).", "weaponArmoireScholarlyTextbooksText": "Scholarly Textbooks", "weaponArmoireScholarlyTextbooksNotes": "Here’s your chance to dive deep and learn about any topic that interests you. What’s your current hyperfixation? Increases Intelligence by <%= int %>. Enchanted Armoire: School Uniform Set (Item 3 of 4).", + "weaponArmoireHattersShearsText": "Sharp Shears", + "weaponArmoireHattersShearsNotes": "Cut right through overwhelm and complications. These shears do a great job cutting fabric, as well, of course. Increases Strength by <%= str %>. Enchanted Armoire: Hatter Set (Item 3 of 4).", "armor": "armor", "armorCapitalized": "Armor", @@ -1554,6 +1558,8 @@ "armorArmoireSchoolUniformPantsNotes": "Whether you’re attending a school for magical wizards, dragon riders, sportsball players, creative artisans, or members of a profession too secret to list here, you’ll fit right in with this uniform. Increases Intelligence by <%= int %>. Enchanted Armoire: School Uniform Set (Item 2 of 4).", "armorArmoireSoftWhiteSuitText": "Soft White Suit", "armorArmoireSoftWhiteSuitNotes": "White is a peaceful color. Whether you’re facing a crisp white bedsheet or a blanket of newly fallen snow, you’ll have a clear and ready mind. Increases Constitution by <% con %> and Perception by <%= per %>. Enchanted Armoire: White Loungewear Set (Item 2 of 3).", + "armorArmoireHattersSuitText": "Hatter's Suit", + "armorArmoireHattersSuitNotes": "Your outfit isn’t complete without your lucky green bowtie. Wear this to your next mad tea party. Or pleasant tea party. Or excited tea party. Or... Increases Constitution by <%= con %>. Enchanted Armoire: Hatter Set (Item 2 of 4).", "headgear": "helm", "headgearCapitalized": "Headgear", @@ -2192,7 +2198,9 @@ "headMystery202312Text": "Wintry Blue Hair", "headMystery202312Notes": "This fancy hairdo evokes the snowy colors of the season. Confers no benefit. December 2023 Subscriber Item.", "headMystery202402Text": "Paradise Pink Hair", - "headMystery202402Notes": "This pretty pink mane is the perfect accessory for February and beyond. Confers no benefit. February 2024 Subscriber Item.", + "headMystery202402Notes": "This pretty pink mane is the perfect accessory for February and beyond. Confers no benefit. February 2024 Subscriber Item.", + "headMystery202403Text": "Lucky Aquamarine Cap", + "headMystery202403Notes": "Lucky you, to be able to don this fine cap of emerald velvet with its fine sea-green gem. Confers no benefit. March 2024 Subscriber Item.", "headMystery301404Text": "Fancy Top Hat", "headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.", @@ -2384,7 +2392,9 @@ "headArmoireGreenTrapperHatText": "Green Trapper Hat", "headArmoireGreenTrapperHatNotes": "Everyone says your hat looks so warm! And it really is. Just be sure to lift the flaps off your ears when they’re talking to you, or everyone will sound more like “yrrr hah ooks ss wrrm!” Increases Constitution and Perception by <%= attrs %> each. Enchanted Armoire: Trapper Hat Set (Item 1 of 2).", "headArmoireWhiteFloppyHatText": "White Floppy Hat", - "headArmoireWhiteFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a wondrous white color. Increases Strength, Intelligence, and Constitution by <%= attrs %> each. Enchanted Armoire: White Loungewear Set (Item 1 of 3).", + "headArmoireWhiteFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a wondrous white color. Increases Strength, Intelligence, and Constitution by <%= attrs %> each. Enchanted Armoire: White Loungewear Set (Item 1 of 3).", + "headArmoireHattersTopHatText": "Hatter's Top Hat", + "headArmoireHattersTopHatNotes": "Our hats are off to you, and yours is on! What’s hidden in your hat is anybody’s guess (but we’re hoping it’s a bunny). Increases Perception by <%= per %>. Enchanted Armoire: Hatter Set (Item 1 of 4).", "offhand": "off-hand item", "offHandCapitalized": "Off-Hand Item", @@ -2827,6 +2837,8 @@ "shieldArmoireTrustyPencilNotes": "You know what they say: the pencil is mightier than the sword-cil. Wait... that doesn’t sound quite right... Increases Intelligence by <%= int %>. Enchanted Armoire: School Uniform Set (Item 4 of 4).", "shieldArmoireSoftWhitePillowText": "Soft White Pillow", "shieldArmoireSoftWhitePillowNotes": "The organized warrior packs a pillow for any expedition. Protect yourself from overlooked obligations… even while you nap. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: White Loungewear Set (Item 3 of 3)", + "shieldArmoireHattersPocketWatchText": "Shiny Pocketwatch", + "shieldArmoireHattersPocketWatchNotes": "Don’t be late for a very important date! Check your pocketwatch and your notifications often. Increases Intelligence by <%= int %>. Enchanted Armoire: Hatter Set (Item 4 of 4).", "back": "Back Accessory", "backBase0Text": "No Back Accessory", diff --git a/website/common/locales/en/subscriber.json b/website/common/locales/en/subscriber.json index 297d0c7190..1c7c05b6b7 100644 --- a/website/common/locales/en/subscriber.json +++ b/website/common/locales/en/subscriber.json @@ -159,6 +159,7 @@ "mysterySet202312": "Wintry Blue Set", "mysterySet202401": "Snowy Spellbinder Set", "mysterySet202402": "Paradise Pink Set", + "mysterySet202403": "Lucky Legend Set", "mysterySet301404": "Steampunk Standard Set", "mysterySet301405": "Steampunk Accessories Set", "mysterySet301703": "Peacock Steampunk Set", diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index 4280489746..e034be8ec8 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -214,6 +214,12 @@ const animalSetAchievs = { titleKey: 'achievementReptacularRumble', textKey: 'achievementReptacularRumbleText', }, + rodentRuler: { + icon: 'achievement-rodentRuler', + titleKey: 'achievementRodentRuler', + textKey: 'achievementRodentRulerText', + release: '2024-03-19T08:00-05:00', + }, roughRider: { icon: 'achievement-roughRider', titleKey: 'achievementRoughRider', diff --git a/website/common/script/content/appearance/backgrounds.js b/website/common/script/content/appearance/backgrounds.js index bc9d1c011f..e68b524da9 100644 --- a/website/common/script/content/appearance/backgrounds.js +++ b/website/common/script/content/appearance/backgrounds.js @@ -607,6 +607,11 @@ const plannedBackgrounds = { swan_boat: { }, heart_tree_tunnel: { }, }, + backgrounds032024: { + flowering_forest: { }, + dog_park: { }, + rainy_rainforest: { }, + }, eventBackgrounds: { birthday_bash: { price: 0, @@ -655,6 +660,7 @@ const releaseDates = { backgrounds122023: '2023-12-05T08:00-05:00', backgrounds012024: '2024-01-04T08:00-05:00', backgrounds022024: '2024-02-06T08:00-05:00', + backgrounds032024: '2024-03-05T08:00-05:00', }; const flat = {}; diff --git a/website/common/script/content/bundles.js b/website/common/script/content/bundles.js index 2cef3fbdb7..049fb70f04 100644 --- a/website/common/script/content/bundles.js +++ b/website/common/script/content/bundles.js @@ -110,14 +110,15 @@ const bundles = { cuddleBuddies: { key: 'cuddleBuddies', text: t('cuddleBuddiesText'), - notes: t('cuddleBuddiesNotes', { date: moment('2022-03-31').format('LL') }), // needs update next time we run this + notes: t('cuddleBuddiesNotes', { date: moment(EVENTS.bundle202403.end).format('LL') }), bundleKeys: [ 'bunny', 'ferret', 'guineapig', ], + event: EVENTS.bundle202403, canBuy () { - return moment().isBetween('2022-03-15T08:00-04:00', '2022-03-31T20:00-04:00'); + return moment().isBetween(EVENTS.bundle202403.start, EVENTS.bundle202403.end); }, type: 'quests', value: 7, diff --git a/website/common/script/content/constants/animalSetAchievements.js b/website/common/script/content/constants/animalSetAchievements.js index 4a0746af6e..49b04447ca 100644 --- a/website/common/script/content/constants/animalSetAchievements.js +++ b/website/common/script/content/constants/animalSetAchievements.js @@ -120,6 +120,16 @@ const ANIMAL_SET_ACHIEVEMENTS = { achievementKey: 'reptacularRumble', notificationType: 'ACHIEVEMENT_ANIMAL_SET', }, + rodentRuler: { + type: 'pet', + species: [ + 'Rat', + 'GuineaPig', + 'Squirrel', + ], + achievementKey: 'rodentRuler', + notificationType: 'ACHIEVEMENT_ANIMAL_SET', + }, roughRider: { type: 'petMount', species: [ diff --git a/website/common/script/content/constants/events.js b/website/common/script/content/constants/events.js index 59221537c3..7b4a09eb25 100644 --- a/website/common/script/content/constants/events.js +++ b/website/common/script/content/constants/events.js @@ -15,6 +15,10 @@ export const EVENTS = { season: 'normal', npcImageSuffix: '', }, + bundle202403: { + start: '2024-03-19T00:00-05:00', + end: '2024-03-31T08:00-05:00', + }, bundle202402: { start: '2024-02-20T00:00-05:00', end: '2024-02-29T08:00-05:00', diff --git a/website/common/script/content/gear/sets/armoire.js b/website/common/script/content/gear/sets/armoire.js index 1888461132..fb58444348 100644 --- a/website/common/script/content/gear/sets/armoire.js +++ b/website/common/script/content/gear/sets/armoire.js @@ -462,6 +462,10 @@ const armor = { per: 10, set: 'whiteLoungeWear', }, + hattersSuit: { + con: 9, + set: 'hatterSet', + }, }; const body = { @@ -959,6 +963,10 @@ const head = { con: 5, set: 'whiteLoungeWear', }, + hattersTopHat: { + per: 10, + set: 'hatterSet', + }, }; const shield = { @@ -1291,6 +1299,10 @@ const shield = { per: 6, set: 'whiteLoungeWear', }, + hattersPocketWatch: { + int: 9, + set: 'hatterSet', + }, }; const headAccessory = { @@ -1760,6 +1772,10 @@ const weapon = { int: 10, set: 'schoolUniform', }, + hattersShears: { + str: 10, + set: 'hatterSet', + }, }; const releaseDates = { @@ -1768,6 +1784,7 @@ const releaseDates = { greenTrapper: '2023-12-05T08:00-05:00', schoolUniform: '2024-01-04T08:00-05:00', whiteLoungeWear: '2024-02-06T08:00-05:00', + hatterSet: '2024-03-05T08:00-05:00', }; forEach({ diff --git a/website/common/script/content/gear/sets/mystery.js b/website/common/script/content/gear/sets/mystery.js index 833191f281..160a448154 100644 --- a/website/common/script/content/gear/sets/mystery.js +++ b/website/common/script/content/gear/sets/mystery.js @@ -220,6 +220,7 @@ const head = { 202311: { }, 202312: { }, 202402: { }, + 202403: { }, 301404: { }, 301405: { }, 301703: { }, @@ -286,6 +287,7 @@ const weapon = { 202209: { }, 202306: { }, 202311: { }, + 202403: { }, 301404: { }, }; diff --git a/website/common/script/content/shop-featuredItems.js b/website/common/script/content/shop-featuredItems.js index f5fafeb50f..08067ba5f7 100644 --- a/website/common/script/content/shop-featuredItems.js +++ b/website/common/script/content/shop-featuredItems.js @@ -47,19 +47,19 @@ const featuredItems = { ]; }, quests () { - if (moment().isBetween(EVENTS.bundle202402.start, EVENTS.bundle202402.end)) { + if (moment().isBetween(EVENTS.bundle202403.start, EVENTS.bundle202403.end)) { return [ { type: 'bundles', - path: 'bundles.mythicalMarvels', + path: 'bundles.cuddleBuddies', }, { type: 'quests', - path: 'quests.nudibranch', + path: 'quests.hedgehog', }, { type: 'quests', - path: 'quests.axolotl', + path: 'quests.sheep', }, ]; } diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 60982250b2..fbe4446b98 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -229,6 +229,7 @@ function _getBasicAchievements (user, language) { _addSimple(result, user, { path: 'bonelessBoss', language }); _addSimple(result, user, { path: 'duneBuddy', language }); _addSimple(result, user, { path: 'roughRider', language }); + _addSimple(result, user, { path: 'rodentRuler', language }); _addSimpleWithMasterCount(result, user, { path: 'beastMaster', language }); _addSimpleWithMasterCount(result, user, { path: 'mountMaster', language }); diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 27e4846e74..487e46b92b 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -158,6 +158,7 @@ export const UserSchema = new Schema({ bonelessBoss: Boolean, duneBuddy: Boolean, roughRider: Boolean, + rodentRuler: Boolean, // Onboarding Guide createdTask: Boolean, completedTask: Boolean,