fix: distinguish ID and JP generation bonuses
Publish GitHub Pages output / publish (push) Successful in 48s

This commit is contained in:
2026-09-04 23:45:46 +09:00
parent 397e3c00ac
commit 44ddf932e8
4 changed files with 41 additions and 5 deletions
+14
View File
@@ -27,6 +27,20 @@ const cards = [
{ id: 'card-b', characterId: 'b', type: '퓨어', generation: '1기생' }
];
test('JP 3기생 보너스가 코보의 ID 3기생 보너스 상한을 소모하지 않는다', () => {
const summary: BoardSummarySource = {
nodeGroups: {
'G-jp': [{ characterIds: [], effect: { type: 'all_parameter_up_for_character_grouping', value: 900, targetId: 'skill_tree_effect_target-character_grouping-grp-gen_3' } }],
'G-id': [{ characterIds: [], effect: { type: 'all_parameter_up_for_character_grouping', value: 150, targetId: 'skill_tree_effect_target-character_grouping-grp-indonesia-gen_3' } }]
}
};
const kobo = [{ id: 'kobo', characterId: 'chr-03009', type: '퓨어', generation: 'hololive ID 3기생' }];
const before = calculateBoardEffects(summary, { account: ['G-jp'] }, kobo, '');
const after = calculateBoardEffects(summary, { account: ['G-jp', 'G-id'] }, kobo, '');
assert.deepEqual(before.flatByCardId.kobo, { performance: 0, technique: 0, sense: 0 });
assert.deepEqual(after.flatByCardId.kobo, { performance: 150, technique: 150, sense: 150 });
});
test('편성 멤버의 파란 보드, 리더의 빨간 보드와 공통 초록 보드를 합산한다', () => {
const result = calculateBoardEffects(summary, {
a: ['B-1', 'B-2', 'Y-1'],
+20
View File
@@ -28,3 +28,23 @@ test('복수 소속 멤버는 표기된 각 기수에 모두 속한다', () => {
assert.equal(skillTargetBelongsTo(fubuki, 'GAMERS'), true);
assert.equal(skillConditionSatisfied('1기생(이)가 1명 이상이면', [fubuki]), true);
});
test('ID 기수는 같은 번호의 JP 기수와 구분한다', () => {
for (const number of [1, 2, 3]) {
const member = { type: '퓨어', generation: `hololive ID ${number}기생` };
assert.equal(skillTargetBelongsTo(member, `ID ${number}기생`), true);
assert.equal(skillTargetBelongsTo(member, `${number}기생`), false);
assert.equal(skillConditionSatisfied(`${number}기생(이)가 1명 이상이면`, [member]), false);
}
});
test('EN과 DEV_IS 소속 표기를 제거해 기수명을 판정한다', () => {
for (const [generation, group] of [
['hololive EN Myth', 'Myth'],
['hololive EN Promise', 'Promise'],
['hololive EN Advent', 'Advent'],
['DEV_IS ReGLOSS', 'ReGLOSS']
]) {
assert.equal(skillTargetBelongsTo({ type: '퓨어', generation }, group), true);
}
});
+2 -4
View File
@@ -10,6 +10,7 @@ export function plainSkillText(value: string): string {
function normalizedGroup(value: string): string {
return value
.replace(/^hololive\s+/i, '')
.replace(/^(?:EN|DEV_IS)\s+/i, '')
.replace(/\(이\)가$/, '')
.replace(/\s*타입$/, '')
.trim()
@@ -19,15 +20,12 @@ function normalizedGroup(value: string): string {
function memberBelongsTo(member: SkillConditionMember, group: string): boolean {
const normalized = normalizedGroup(group);
const type = normalizedGroup(member.type);
const generation = normalizedGroup(member.generation);
const generations = member.generation
.split('/')
.map((value) => normalizedGroup(value))
.filter(Boolean);
return type === normalized
|| generation === normalized
|| generation.endsWith(normalized)
|| generations.some((value) => value === normalized || value.endsWith(normalized));
|| generations.includes(normalized);
}
export function skillConditionSatisfied(