fix: restore summer event calculation through source deployment
Publish GitHub Pages output / publish (push) Successful in 39s
Publish GitHub Pages output / publish (push) Successful in 39s
This commit is contained in:
@@ -7,6 +7,17 @@ const gamersFeaturedCards = [
|
||||
'card-00017-5-uniq-0077-00'
|
||||
] as const;
|
||||
|
||||
const summerFeaturedCards = ['card-00004-5-uniq-0081-00', 'card-04013-5-uniq-0083-00', 'card-00035-5-uniq-0082-00', 'card-03005-5-uniq-0084-00'] as const;
|
||||
// Schedule: https://dengekionline.com/article/202609/88492
|
||||
// Bonuses: https://gamerch.com/hololive-dreams/1015338
|
||||
// IDs/names: HolodoriDB kor 5517e40b931fec10253f5b850989346518e233c2.
|
||||
const summerChapters = [
|
||||
['아키로제식 여름 휴가', 'chr-00004', 'm0349', 19, 21],
|
||||
['더위와 풀장과 독서가와', 'chr-04013', 'm0351', 21, 23],
|
||||
['총수의 파라다이스 침략 작전!', 'chr-00035', 'm0350', 23, 25],
|
||||
['유능 감독, 성지순례에 나서다', 'chr-03005', 'm0352', 25, 27]
|
||||
] as const;
|
||||
|
||||
export const events = [
|
||||
{
|
||||
// HolodoriDB supplies the card/song IDs; the public master-data diff does not include
|
||||
@@ -32,5 +43,30 @@ export const events = [
|
||||
4: [0, 6, 7, 8, 9, 10],
|
||||
5: [0, 15, 18, 22, 26, 30]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'marathon_event-relay-002',
|
||||
name: '몽환의 서머 이스케이프?!',
|
||||
startsAt: '2026-09-19T11:00:00.000Z',
|
||||
endsAt: '2026-09-27T10:59:59.999Z',
|
||||
bonusCharacterIds: [],
|
||||
featuredCardIds: summerFeaturedCards,
|
||||
challengeSongBonusCardIds: {},
|
||||
formationBonusPercent: 50,
|
||||
featuredCardBonusPercent: 50,
|
||||
challengeSongScoreBonusPercent: 10,
|
||||
awakeningBonusPercentByRarity: {
|
||||
3: [0, 1, 1, 2, 2, 3],
|
||||
4: [0, 6, 7, 8, 9, 10],
|
||||
5: [0, 15, 18, 22, 26, 30]
|
||||
},
|
||||
chapters: summerChapters.map(([name, characterId, songId, start, end], index) => ({
|
||||
id: `marathon_event-relay-002-chapter-0${index + 1}`,
|
||||
name,
|
||||
startsAt: `2026-09-${start}T11:00:00.000Z`,
|
||||
endsAt: `2026-09-${end}T10:59:59.999Z`,
|
||||
bonusCharacterIds: [characterId],
|
||||
challengeSongBonusCardIds: { [songId]: [summerFeaturedCards[index]] }
|
||||
}))
|
||||
}
|
||||
] as const satisfies readonly EventDefinition[];
|
||||
|
||||
@@ -5,6 +5,40 @@ import { activeEventAt, compareEventRankings, eventBonusBreakdown, eventChalleng
|
||||
|
||||
const currentEvent = events[0];
|
||||
|
||||
test('솔로 이벤트는 챕터 경계에서 대상 멤버와 과제곡을 함께 전환한다', () => {
|
||||
assert.equal(activeEventAt(events, Date.parse('2026-09-19T10:59:59.999Z')), null);
|
||||
const expected = [
|
||||
[19, 'chr-00004', 'm0349'], [21, 'chr-04013', 'm0351'],
|
||||
[23, 'chr-00035', 'm0350'], [25, 'chr-03005', 'm0352']
|
||||
] as const;
|
||||
for (const [day, characterId, songId] of expected) {
|
||||
const start = Date.parse(`2026-09-${day}T11:00:00.000Z`);
|
||||
const chapter = activeEventAt(events, start)!;
|
||||
assert.deepEqual(chapter.bonusCharacterIds, [characterId]);
|
||||
assert.deepEqual(Object.keys(chapter.challengeSongBonusCardIds), [songId]);
|
||||
assert.equal(activeEventAt(events, start + 48 * 60 * 60 * 1000 - 1)?.id, chapter.id);
|
||||
assert.notEqual(activeEventAt(events, start - 1)?.id, chapter.id);
|
||||
}
|
||||
assert.equal(activeEventAt(events, Date.parse('2026-09-27T11:00:00.000Z')), null);
|
||||
});
|
||||
|
||||
test('신규 카드 공통 보너스와 현재 챕터 멤버 보너스를 따로 적용한다', () => {
|
||||
const aki = activeEventAt(events, Date.parse('2026-09-19T11:00:00Z'))!;
|
||||
const shiori = activeEventAt(events, Date.parse('2026-09-21T11:00:00Z'))!;
|
||||
const cards = [
|
||||
{ id: aki.featuredCardIds[0], characterId: 'chr-00004', rarity: 5 },
|
||||
{ id: 'shiori-regular', characterId: 'chr-04013', rarity: 3 }
|
||||
];
|
||||
assert.deepEqual(eventBonusBreakdown(aki, cards, () => 5), {
|
||||
formationPercent: 50, featuredCardPercent: 50, awakeningPercent: 33, totalPercent: 133
|
||||
});
|
||||
assert.equal(eventBonusBreakdown(shiori, [cards[0]], () => 0).totalPercent, 50);
|
||||
assert.equal(eventBonusBreakdown(shiori, [cards[1]], () => 0).totalPercent, 50);
|
||||
assert.equal(eventChallengeSongScoreBonusPercent(aki, 'm0349', [cards[0].id]), 10);
|
||||
assert.equal(eventChallengeSongScoreBonusPercent(shiori, 'm0349', [cards[0].id]), 0);
|
||||
assert.equal(eventChallengeSongScoreBonusPercent(aki, 'm0349', [aki.featuredCardIds[1]]), 0);
|
||||
});
|
||||
|
||||
test('이벤트 기간 안에서만 진행 중 이벤트를 반환한다', () => {
|
||||
assert.equal(activeEventAt(events, Date.parse('2026-08-29T02:59:59.999Z')), null);
|
||||
assert.equal(activeEventAt(events, Date.parse('2026-08-31T00:00:00.000Z'))?.id, currentEvent.id);
|
||||
|
||||
@@ -16,6 +16,14 @@ export type EventDefinition = {
|
||||
featuredCardBonusPercent: number;
|
||||
challengeSongScoreBonusPercent: number;
|
||||
awakeningBonusPercentByRarity: Readonly<Record<number, readonly number[]>>;
|
||||
chapters?: readonly {
|
||||
id: string;
|
||||
name: string;
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
bonusCharacterIds: readonly string[];
|
||||
challengeSongBonusCardIds: Readonly<Record<string, readonly string[]>>;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type EventBonusBreakdown = {
|
||||
@@ -30,7 +38,11 @@ export function eventOptimizationCardEligible(card: EventCard, bonusCharacterIds
|
||||
}
|
||||
|
||||
export function activeEventAt(events: readonly EventDefinition[], now: number): EventDefinition | null {
|
||||
return events.find((event) => Date.parse(event.startsAt) <= now && now <= Date.parse(event.endsAt)) ?? null;
|
||||
const event = events.find((event) => Date.parse(event.startsAt) <= now && now <= Date.parse(event.endsAt));
|
||||
if (!event) return null;
|
||||
if (!event.chapters) return event;
|
||||
const chapter = event.chapters.find((entry) => Date.parse(entry.startsAt) <= now && now <= Date.parse(entry.endsAt));
|
||||
return chapter ? { ...event, ...chapter, name: `${event.name} · ${chapter.name}` } : null;
|
||||
}
|
||||
|
||||
export function eventBonusBreakdown(
|
||||
|
||||
Reference in New Issue
Block a user