Squashed commit of the following:

commit bde71cc45d
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 20 15:25:11 2025 -0500

    fix(potions): add missing Cryptid release date

commit 1a50413402
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Tue Mar 18 16:52:19 2025 -0500

    fix(wacky): revise text, address linting

commit 2d49a16f55
Author: Phillip Thelen <phillip@habitica.com>
Date:   Tue Mar 18 16:05:17 2025 +0100

    Fix displaying countdown for recurring event items

commit cf20b30975
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Mar 14 18:01:37 2025 -0500

    fix(lint): line length

commit e39e490e41
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Fri Mar 14 17:55:12 2025 -0500

    fix(foolin): correct animation and end date of potions

commit 164e2eefdd
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 17:25:14 2025 -0500

    fix(event): Panda Cub typo, shift start date

commit 394c922287
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 17:02:12 2025 -0500

    fix(test): account for addition of Cryptid

commit bea9e40338
Author: Kalista Payne <sabrecat@gmail.com>
Date:   Thu Mar 13 16:52:37 2025 -0500

    feat(event): April Fools 2025
This commit is contained in:
Kalista Payne
2025-03-21 15:02:31 -05:00
parent 01881b2fd8
commit f3029953dc
15 changed files with 194 additions and 107 deletions

View File

@@ -190,7 +190,7 @@ describe('Content Schedule', () => {
const date = new Date('2024-04-15');
const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.premiumHatchingPotions).to.exist;
expect(matchers.premiumHatchingPotions.items.length).to.equal(5);
expect(matchers.premiumHatchingPotions.items.length).to.equal(6);
expect(matchers.premiumHatchingPotions.items.indexOf('Veggie')).to.not.equal(-1);
expect(matchers.premiumHatchingPotions.items.indexOf('Porcelain')).to.not.equal(-1);
});

View File

@@ -22,7 +22,8 @@
height: 219px;
}
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup, .Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi {
.Pet_HatchingPotion_Dessert, .Pet_HatchingPotion_Veggie, .Pet_HatchingPotion_Windup,
.Pet_HatchingPotion_VirtualPet, .Pet_HatchingPotion_Fungi, .Pet_HatchingPotion_Cryptid {
width: 68px;
height: 68px;
}
@@ -47,6 +48,10 @@
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Fungi.gif") no-repeat;
}
.Pet_HatchingPotion_Cryptid {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Cryptid.gif") no-repeat;
}
.Gems {
display:inline-block;
margin-right:5px;

View File

@@ -52283,6 +52283,11 @@
width: 81px;
height: 99px;
}
.Pet-BearCub-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-BearCub-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-BearCub-Cupid.png');
width: 81px;
@@ -52763,6 +52768,11 @@
width: 81px;
height: 99px;
}
.Pet-Cactus-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-Cactus-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Cactus-Cupid.png');
width: 81px;
@@ -53543,6 +53553,11 @@
width: 81px;
height: 99px;
}
.Pet-Dragon-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-Dragon-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Dragon-Cupid.png');
width: 81px;
@@ -54028,6 +54043,11 @@
width: 81px;
height: 99px;
}
.Pet-FlyingPig-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-FlyingPig-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-FlyingPig-Cupid.png');
width: 81px;
@@ -54353,6 +54373,11 @@
width: 81px;
height: 99px;
}
.Pet-Fox-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-Fox-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Fox-Cupid.png');
width: 81px;
@@ -55123,6 +55148,11 @@
width: 81px;
height: 99px;
}
.Pet-LionCub-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-LionCub-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-LionCub-Cupid.png');
width: 81px;
@@ -55718,6 +55748,11 @@
width: 81px;
height: 99px;
}
.Pet-PandaCub-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-PandaCub-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-PandaCub-Cupid.png');
width: 81px;
@@ -57053,6 +57088,11 @@
width: 81px;
height: 99px;
}
.Pet-TigerCub-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-TigerCub-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-TigerCub-Cupid.png');
width: 81px;
@@ -57688,6 +57728,11 @@
width: 81px;
height: 99px;
}
.Pet-Wolf-Cryptid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Cryptid.png');
width: 81px;
height: 99px;
}
.Pet-Wolf-Cupid {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-Wolf-Cupid.png');
width: 81px;

View File

@@ -19,7 +19,7 @@
top: -16px !important;
}
$foolPets: Veggie, Dessert, VirtualPet, TeaShop, Fungi;
$foolPets: Veggie, Dessert, VirtualPet, TeaShop, Fungi, Cryptid;
@each $foolPet in $foolPets {
.Pet.Pet-FlyingPig-#{$foolPet} {

View File

@@ -155,7 +155,6 @@
</style>
<script>
import some from 'lodash/some';
import moment from 'moment';
import { mapState } from '@/libs/store';
import foolPet from '../mixins/foolPet';
@@ -322,11 +321,10 @@ export default {
return null;
},
petClass () {
if (some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'Fungi',
)) {
return this.foolPet(this.member.items.currentPet);
const foolEvent = this.currentEventList?.find(event => moment()
.isBetween(event.start, event.end) && event.aprilFools);
if (foolEvent) {
return this.foolPet(this.member.items.currentPet, foolEvent.aprilFools);
}
if (this.member?.items.currentPet) return `Pet-${this.member.items.currentPet}`;
return '';

View File

@@ -114,7 +114,6 @@
</style>
<script>
import some from 'lodash/some';
import moment from 'moment';
import { v4 as uuid } from 'uuid';
import { mapState } from '@/libs/store';
@@ -183,13 +182,12 @@ export default {
return 'GreyedOut';
},
imageName () {
if (this.isOwned() && some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'Fungi',
)) {
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key)}`;
const foolEvent = this.currentEventList?.find(event => moment()
.isBetween(event.start, event.end) && event.aprilFools);
if (this.isOwned() && foolEvent) {
if (this.isSpecial()) return `stable_${this.foolPet(this.item.key, foolEvent.aprilFools)}`;
const petString = `${this.item.eggKey}-${this.item.key}`;
return `stable_${this.foolPet(petString)}`;
return `stable_${this.foolPet(petString, foolEvent.aprilFools)}`;
}
if (this.isOwned() || (this.mountOwned() && this.isHatchable())) {

View File

@@ -2,54 +2,55 @@ import includes from 'lodash/includes';
export default {
methods: {
foolPet (pet) {
foolPet (pet, prank) {
const SPECIAL_PETS = [
'Wolf-Veteran',
'Wolf-Cerberus',
'Dragon-Hydra',
'Turkey-Base',
'BearCub-Polar',
'MantisShrimp-Base',
'JackOLantern-Base',
'Mammoth-Base',
'Tiger-Veteran',
'Phoenix-Base',
'Turkey-Gilded',
'MagicalBee-Base',
'Lion-Veteran',
'Gryphon-RoyalPurple',
'JackOLantern-Ghost',
'Jackalope-RoyalPurple',
'Orca-Base',
'Bear-Veteran',
'Hippogriff-Hopeful',
'Fox-Veteran',
'JackOLantern-Glow',
'Gryphon-Gryphatrice',
'Gryphatrice-Jubilant',
'JackOLantern-RoyalPurple',
'BearCub-Polar',
'Cactus-Veteran',
'Dragon-Hydra',
'Dragon-Veteran',
'Fox-Veteran',
'Gryphatrice-Jubilant',
'Gryphon-Gryphatrice',
'Gryphon-RoyalPurple',
'Hippogriff-Hopeful',
'Jackalope-RoyalPurple',
'JackOLantern-Base',
'JackOLantern-Ghost',
'JackOLantern-Glow',
'JackOLantern-RoyalPurple',
'Lion-Veteran',
'MagicalBee-Base',
'Mammoth-Base',
'MantisShrimp-Base',
'Orca-Base',
'Phoenix-Base',
'Tiger-Veteran',
'Turkey-Base',
'Turkey-Gilded',
'Wolf-Cerberus',
'Wolf-Veteran',
];
const BASE_PETS = [
'Wolf',
'TigerCub',
'PandaCub',
'LionCub',
'Fox',
'FlyingPig',
'BearCub',
'Dragon',
'Cactus',
'Dragon',
'FlyingPig',
'Fox',
'LionCub',
'PandaCub',
'TigerCub',
'Wolf',
];
if (!pet) return 'Pet-TigerCub-Fungi';
if (!pet) return `Pet-TigerCub-${prank}`;
if (SPECIAL_PETS.indexOf(pet) !== -1) {
return 'Pet-Dragon-Fungi';
return `Pet-Dragon-${prank}`;
}
const species = pet.slice(0, pet.indexOf('-'));
if (includes(BASE_PETS, species)) {
return `Pet-${species}-Fungi`;
return `Pet-${species}-${prank}`;
}
return 'Pet-BearCub-Fungi';
return `Pet-BearCub-${prank}`;
},
},
};

View File

@@ -350,9 +350,12 @@
"hatchingPotionGingerbread": "Gingerbread",
"hatchingPotionJade": "Jade",
"hatchingPotionBalloon": "Balloon",
"hatchingPotionCryptid": "Cryptid",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> Pet.",
"premiumPotionUnlimitedNotes": "Not usable on Quest Pet eggs.",
"wackyPotionNotes": "Pour this on an egg, and it will hatch as a Wacky <%= potText(locale) %> Pet.",
"wackyPotionAddlNotes": "Cannot be raised to Mounts or used on Quest Pet eggs.",
"foodMeat": "Meat",
"foodMeatThe": "the Meat",

View File

@@ -50,8 +50,13 @@ export const REPEATING_EVENTS = {
end: new Date('1970-03-16T03:59-04:00'),
foodSeason: 'Pie',
},
aprilFools: {
start: new Date('1970-04-01T04:00-04:00'),
end: new Date('1970-04-02T03:59-04:00'),
aprilFools: 'Cryptid',
},
aprilFoolsResale: {
start: new Date('1970-04-07T04:00-04:00'),
start: new Date('1970-04-03T04:00-04:00'),
end: new Date('1970-05-01T03:59-04:00'),
content: [
{
@@ -65,6 +70,7 @@ export const REPEATING_EVENTS = {
{
type: 'premiumHatchingPotions',
items: [
'Cryptid',
'Veggie',
'TeaShop',
],
@@ -146,7 +152,6 @@ export const EVENTS = {
start: '2024-04-09T08:00-04:00',
end: '2024-04-30T23:59-04:00',
},
aprilFools2024: {
start: '2024-04-01T00:00-04:00',
end: '2024-04-02T08:00-04:00',

View File

@@ -1,41 +1,40 @@
export default [
'head_special_1',
'broad_armor_special_1',
'slim_armor_special_1',
'head_special_0',
'slim_armor_special_0',
'broad_armor_special_0',
'weapon_special_critical',
'weapon_special_0',
'shield_special_0',
'Pet-Wolf-Cerberus',
'stable_Pet-Wolf-Cerberus',
'armor_special_ks2019',
'slim_armor_special_ks2019',
'back_special_heroicAureole',
'background_airship',
'background_clocktower',
'background_steamworks',
'broad_armor_special_0',
'broad_armor_special_1',
'broad_armor_special_ks2019',
'eyewear_special_ks2019',
'head_special_0',
'head_special_1',
'head_special_ks2019',
'shield_special_ks2019',
'weapon_special_ks2019',
'Pet-Gryphon-Gryphatrice',
'stable_Pet-Gryphon-Gryphatrice',
'Mount_Head_Gryphon-Gryphatrice',
'Mount_Body_Gryphon-Gryphatrice',
'background_clocktower',
'background_airship',
'background_steamworks',
'Pet_HatchingPotion_Veggie',
'Mount_Head_Gryphon-Gryphatrice',
'Pet_HatchingPotion_Cryptid',
'Pet_HatchingPotion_Dessert',
'Pet-HatchingPotion-Dessert',
'quest_windup',
'Pet-HatchingPotion_Windup',
'Pet_HatchingPotion_Fungi',
'Pet_HatchingPotion_Veggie',
'Pet_HatchingPotion_VirtualPet',
'Pet_HatchingPotion_Windup',
'Pet-Gryphatrice-Jubilant',
'Pet-Gryphon-Gryphatrice',
'Pet-Wolf-Cerberus',
'quest_solarSystem',
'quest_virtualpet',
'Pet_HatchingPotion_VirtualPet',
'Pet-Gryphatrice-Jubilant',
'stable_Pet-Gryphatrice-Jubilant',
'back_special_heroicAureole',
'Pet-HatchingPotion-Funghi',
'quest_windup',
'shield_special_0',
'shield_special_ks2019',
'shop_armoire',
'slim_armor_special_0',
'slim_armor_special_1',
'slim_armor_special_ks2019',
'stable_Pet-Gryphatrice-Jubilant',
'stable_Pet-Gryphon-Gryphatrice',
'stable_Pet-Wolf-Cerberus',
'weapon_special_0',
'weapon_special_critical',
'weapon_special_ks2019',
];

View File

@@ -36,5 +36,6 @@ export const HATCHING_POTIONS_RELEASE_DATES = {
Koi: { year: 2024, month: 6, day: 1 },
Gingerbread: { year: 2024, month: 12, day: 21 },
Jade: { year: 2025, month: 3, day: 14 },
Cryptid: { year: 2025, month: 4, day: 3 },
Balloon: { year: 2025, month: 4, day: 21 },
};

View File

@@ -841,6 +841,30 @@ function getGalaIndex (date) {
return parseInt((galaCount / 12) * galaMonth, 10);
}
function makeEndDate (checkedDate, matcher) {
let end = moment.utc(checkedDate);
end.hour(SWITCHOVER_TIME);
end.minute(0);
end.second(0);
if (matcher.end !== undefined) {
end.date(matcher.end.getDate());
end.month(matcher.end.getMonth());
} else {
end.date(TYPE_SCHEDULE[matcher.type]);
if (matcher.endMonth !== undefined) {
if (matcher.startMonth
&& matcher.startMonth > matcher.endMonth
&& checkedDate.getMonth() > matcher.endMonth) {
end.year(checkedDate.getFullYear() + 1);
}
end.month(matcher.endMonth);
} else if (end.valueOf() <= checkedDate.getTime()) {
end = moment(end).add(1, 'months');
}
}
return end.toDate();
}
export function assembleScheduledMatchers (date) {
const items = [];
const month = getMonth(date);
@@ -865,7 +889,14 @@ export function assembleScheduledMatchers (date) {
items.push(...galaMatchers);
getRepeatingEvents(date).forEach(event => {
if (event.content) {
items.push(...event.content);
const { content } = event;
const end = makeEndDate(date, event);
const m = content.map(matcher => {
const newMatcher = { ...matcher };
newMatcher.end = end;
return newMatcher;
});
items.push(...m);
}
});
return items;
@@ -878,8 +909,15 @@ function makeMatcherClass (date) {
return {
matchers: [],
end: new Date(),
specialEnds: {},
items: [],
matchingDate: date,
getEnd (key) {
if (this.specialEnds[key]) {
return this.specialEnds[key];
}
return this.end;
},
match (key) {
if (this.matchers.length === 0) {
if (this.items.length > 0) {
@@ -896,25 +934,6 @@ function makeMatcherClass (date) {
};
}
function makeEndDate (checkedDate, matcher) {
let end = moment.utc(checkedDate);
end.date(TYPE_SCHEDULE[matcher.type]);
end.hour(SWITCHOVER_TIME);
end.minute(0);
end.second(0);
if (matcher.endMonth !== undefined) {
if (matcher.startMonth
&& matcher.startMonth > matcher.endMonth
&& checkedDate.getMonth() > matcher.endMonth) {
end.year(checkedDate.getFullYear() + 1);
}
end.month(matcher.endMonth);
} else if (end.valueOf() <= checkedDate.getTime()) {
end = moment(end).add(1, 'months');
}
return end.toDate();
}
export function clearCachedMatchers () {
cacheDate = null;
cachedScheduleMatchers = null;
@@ -939,11 +958,19 @@ export function getAllScheduleMatchingGroups (date) {
if (!cachedScheduleMatchers[matcher.type]) {
cachedScheduleMatchers[matcher.type] = makeMatcherClass(adjustedDate);
}
cachedScheduleMatchers[matcher.type].end = makeEndDate(checkedDate, matcher);
if (matcher.end === undefined) {
// we want the default end date to be for matcher type
cachedScheduleMatchers[matcher.type].end = makeEndDate(checkedDate, matcher);
}
if (matcher.matcher instanceof Function) {
cachedScheduleMatchers[matcher.type].matchers.push(matcher.matcher);
} else if (matcher.items instanceof Array) {
cachedScheduleMatchers[matcher.type].items.push(...matcher.items);
if (matcher.end !== undefined) {
matcher.items.forEach(item => {
cachedScheduleMatchers[matcher.type].specialEnds[item] = matcher.end;
});
}
}
});
}

View File

@@ -150,6 +150,7 @@ const wacky = {
questPotion: true,
canBuy: hasQuestAchievementFunction('fungi'),
},
Cryptid: {},
};
each(drops, (pot, key) => {
@@ -190,10 +191,10 @@ each(wacky, (pot, key) => {
key,
value: 2,
text: t(`hatchingPotion${key}`),
notes: t('hatchingPotionNotes', {
notes: t('wackyPotionNotes', {
potText: t(`hatchingPotion${key}`),
}),
_addlNotes: t('premiumPotionUnlimitedNotes'),
_addlNotes: t('wackyPotionAddlNotes'),
premium: false,
limited: true,
wacky: true,

View File

@@ -489,7 +489,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang
if (matcher && (!itemInfo.set
|| ALWAYS_AVAILABLE_CUSTOMIZATIONS.indexOf(itemInfo.set.key) === -1)) {
itemInfo.end = matcher.end;
itemInfo.end = matcher.getEnd(itemInfo.key);
}
return itemInfo;

View File

@@ -74,11 +74,15 @@ shops.getMarketCategories = function getMarket (user, language) {
const matchers = getScheduleMatchingGroup('premiumHatchingPotions');
premiumHatchingPotionsCategory.items = sortBy(values(content.hatchingPotions)
.filter(hp => hp.limited
&& (matchers.match(hp.key) || (hp.questPotion === true && hp.canBuy(user))))
&& (matchers.match(hp.key)
|| (hp.questPotion === true && hp.canBuy(user))))
.map(premiumHatchingPotion => {
if (premiumHatchingPotion.questPotion) {
return getItemInfo(user, 'premiumHatchingPotion', premiumHatchingPotion, officialPinnedItems, language);
}
if (premiumHatchingPotion.wacky) {
return getItemInfo(user, 'premiumHatchingPotion', premiumHatchingPotion, officialPinnedItems, language, matchers);
}
return getItemInfo(user, 'premiumHatchingPotion', premiumHatchingPotion, officialPinnedItems, language, matchers);
}), 'key');
if (premiumHatchingPotionsCategory.items.length > 0) {