zenno: lint fixes
ci/woodpecker/push/horsebot Pipeline was successful
ci/woodpecker/push/zenno Pipeline was successful

This commit is contained in:
2026-08-21 15:45:53 -04:00
parent e92ce64b17
commit 444fea85f0
9 changed files with 3037 additions and 3040 deletions
+10 -8
View File
@@ -1,15 +1,17 @@
import * as z from 'zod';
const Scenarios = z.array(z.object({
scenario_id: z.int32(),
name: z.string(),
title: z.string(),
}));
const Scenarios = z.array(
z.object({
scenario_id: z.int32(),
name: z.string(),
title: z.string(),
}),
);
export type Scenario = z.infer<typeof Scenarios>[number];
export async function scenarios(): Promise<Scenario[]> {
const r = await fetch('/api/global/scenario');
const u = await r.json();
return Scenarios.parse(u);
const r = await fetch('/api/global/scenario');
const u = await r.json();
return Scenarios.parse(u);
}
+395 -397
View File
@@ -6,406 +6,404 @@ import * as bakushinSR from './testdata/support-20042-bakushin-SR.json';
import * as bakushinSSR from './testdata/support-30083-bakushin-SSR.json';
test.for([
{ rarity: support.Rarity.R, exp: 0, level: 1 },
{ rarity: support.Rarity.R, exp: 5, level: 2 },
{ rarity: support.Rarity.R, exp: 10, level: 2 },
{ rarity: support.Rarity.R, exp: 40510, level: 40 },
{ rarity: support.Rarity.SR, exp: 0, level: 1 },
{ rarity: support.Rarity.SR, exp: 10, level: 2 },
{ rarity: support.Rarity.SR, exp: 20, level: 2 },
{ rarity: support.Rarity.SR, exp: 74990, level: 45 },
{ rarity: support.Rarity.SSR, exp: 0, level: 1 },
{ rarity: support.Rarity.SSR, exp: 15, level: 2 },
{ rarity: support.Rarity.SSR, exp: 30, level: 2 },
{ rarity: support.Rarity.SSR, exp: 118185, level: 50 },
{ rarity: support.Rarity.R, exp: 0, level: 1 },
{ rarity: support.Rarity.R, exp: 5, level: 2 },
{ rarity: support.Rarity.R, exp: 10, level: 2 },
{ rarity: support.Rarity.R, exp: 40510, level: 40 },
{ rarity: support.Rarity.SR, exp: 0, level: 1 },
{ rarity: support.Rarity.SR, exp: 10, level: 2 },
{ rarity: support.Rarity.SR, exp: 20, level: 2 },
{ rarity: support.Rarity.SR, exp: 74990, level: 45 },
{ rarity: support.Rarity.SSR, exp: 0, level: 1 },
{ rarity: support.Rarity.SSR, exp: 15, level: 2 },
{ rarity: support.Rarity.SSR, exp: 30, level: 2 },
{ rarity: support.Rarity.SSR, exp: 118185, level: 50 },
])('level($rarity, $exp) -> $level', ({ rarity, exp, level }) => expect(support.level(rarity, exp)).toBe(level));
describe('effects', () => {
describe('Bakushin R', () => {
test('level 1', () => {
const got = support.effects(bakushinR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 5 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 5 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 6 },
{ type: support.EffectType.MoodEffect, value: 10 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 10 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 2 },
{ type: support.EffectType.SpecialtyPriority, value: 5 },
],
});
});
test('level 6', () => {
const got = support.effects(bakushinR as support.SupportCard, 6);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 6 },
{ type: support.EffectType.MoodEffect, value: 10 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 10 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 3 },
{ type: support.EffectType.SpecialtyPriority, value: 6 },
],
});
});
test('level 39', () => {
const got = support.effects(bakushinR as support.SupportCard, 39);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 25 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 14 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
});
});
test('level 40', () => {
const got = support.effects(bakushinR as support.SupportCard, 40);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 25 },
{ type: support.EffectType.SpeedBonus, value: 1 },
{ type: support.EffectType.InitialSpeed, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
});
});
});
describe('Bakushin R', () => {
test('level 1', () => {
const got = support.effects(bakushinR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 5 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 5 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 6 },
{ type: support.EffectType.MoodEffect, value: 10 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 10 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 2 },
{ type: support.EffectType.SpecialtyPriority, value: 5 },
],
});
});
test('level 6', () => {
const got = support.effects(bakushinR as support.SupportCard, 6);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 6 },
{ type: support.EffectType.MoodEffect, value: 10 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 10 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 3 },
{ type: support.EffectType.SpecialtyPriority, value: 6 },
],
});
});
test('level 39', () => {
const got = support.effects(bakushinR as support.SupportCard, 39);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 25 },
{ type: support.EffectType.SpeedBonus, value: 0 },
{ type: support.EffectType.InitialSpeed, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 14 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
});
});
test('level 40', () => {
const got = support.effects(bakushinR as support.SupportCard, 40);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 25 },
{ type: support.EffectType.SpeedBonus, value: 1 },
{ type: support.EffectType.InitialSpeed, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
});
});
});
describe('Bakushin SR', () => {
test('level 1', () => {
const got = support.effects(bakushinSR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinSR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinSR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 11 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 3 },
{ type: support.EffectType.SpecialtyPriority, value: 5 },
],
});
});
test('level 24', () => {
const got = support.effects(bakushinSR as support.SupportCard, 24);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 15 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 4 },
{ type: support.EffectType.FanBonus, value: 14 },
{ type: support.EffectType.SpecialtyPriority, value: 33 },
],
});
});
test('level 25', () => {
const got = support.effects(bakushinSR as support.SupportCard, 25);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 15 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
test('level 44', () => {
const got = support.effects(bakushinSR as support.SupportCard, 44);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 20 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 9 },
{ type: support.EffectType.FanBonus, value: 19 },
{ type: support.EffectType.SpecialtyPriority, value: 49 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
test('level 45', () => {
const got = support.effects(bakushinSR as support.SupportCard, 45);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 20 },
{ type: support.EffectType.PowerBonus, value: 1 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 10 },
{ type: support.EffectType.FanBonus, value: 20 },
{ type: support.EffectType.SpecialtyPriority, value: 50 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
})
describe('Bakushin SSR', () => {
test('level 1', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 5 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 6 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 12 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 2 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 9 },
],
});
});
test('level 10', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 10);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 14 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 5 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 15 },
],
});
});
test('level 11', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 11);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 5 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 17 },
],
});
});
test('level 30', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 30);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 25 },
{ type: support.EffectType.MoodEffect, value: 20 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.HintLevels, value: 2 },
{ type: support.EffectType.HintFrequency, value: 40 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
test('level 49', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 49);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 34 },
{ type: support.EffectType.MoodEffect, value: 30 },
{ type: support.EffectType.GutsBonus, value: 1 },
{ type: support.EffectType.InitialGuts, value: 27 },
{ type: support.EffectType.InitialFriendship, value: 25 },
{ type: support.EffectType.RaceBonus, value: 9 },
{ type: support.EffectType.FanBonus, value: 19 },
{ type: support.EffectType.HintLevels, value: 2 },
{ type: support.EffectType.HintFrequency, value: 49 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
test('level 50', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 50);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 35 },
{ type: support.EffectType.MoodEffect, value: 30 },
{ type: support.EffectType.GutsBonus, value: 1 },
{ type: support.EffectType.InitialGuts, value: 30 },
{ type: support.EffectType.InitialFriendship, value: 25 },
{ type: support.EffectType.RaceBonus, value: 10 },
{ type: support.EffectType.FanBonus, value: 20 },
{ type: support.EffectType.HintLevels, value: 3 },
{ type: support.EffectType.HintFrequency, value: 50 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
});
describe('Bakushin SR', () => {
test('level 1', () => {
const got = support.effects(bakushinSR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinSR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.SpecialtyPriority, value: 0 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinSR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 11 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 0 },
{ type: support.EffectType.InitialPower, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 3 },
{ type: support.EffectType.SpecialtyPriority, value: 5 },
],
});
});
test('level 24', () => {
const got = support.effects(bakushinSR as support.SupportCard, 24);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 15 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 4 },
{ type: support.EffectType.FanBonus, value: 14 },
{ type: support.EffectType.SpecialtyPriority, value: 33 },
],
});
});
test('level 25', () => {
const got = support.effects(bakushinSR as support.SupportCard, 25);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 15 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.SpecialtyPriority, value: 35 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
test('level 44', () => {
const got = support.effects(bakushinSR as support.SupportCard, 44);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 20 },
{ type: support.EffectType.PowerBonus, value: 0 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 9 },
{ type: support.EffectType.FanBonus, value: 19 },
{ type: support.EffectType.SpecialtyPriority, value: 49 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
test('level 45', () => {
const got = support.effects(bakushinSR as support.SupportCard, 45);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 20 },
{ type: support.EffectType.PowerBonus, value: 1 },
{ type: support.EffectType.TrainingEffectiveness, value: 5 },
{ type: support.EffectType.InitialPower, value: 20 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 10 },
{ type: support.EffectType.FanBonus, value: 20 },
{ type: support.EffectType.SpecialtyPriority, value: 50 },
],
unique: {
name: 'The Perfect Morning Greeting!',
description: 'Friendship Bonus and Specialty Priority',
card_level: 25,
type: support.EffectType.FriendshipBonus,
args: [10],
type2: support.EffectType.SpecialtyPriority,
args2: [20],
idle_mode_sub_rate: 0,
} satisfies support.UniqueEffect,
});
});
});
describe('Bakushin SSR', () => {
test('level 1', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 1);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 5 },
],
});
});
test('level 2', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 2);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 10 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 1 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 6 },
],
});
});
test('level 5', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 5);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 12 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 0 },
{ type: support.EffectType.RaceBonus, value: 1 },
{ type: support.EffectType.FanBonus, value: 2 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 9 },
],
});
});
test('level 10', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 10);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 14 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 5 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 15 },
],
});
});
test('level 11', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 11);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 15 },
{ type: support.EffectType.MoodEffect, value: 0 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 10 },
{ type: support.EffectType.RaceBonus, value: 2 },
{ type: support.EffectType.FanBonus, value: 5 },
{ type: support.EffectType.HintLevels, value: 1 },
{ type: support.EffectType.HintFrequency, value: 17 },
],
});
});
test('level 30', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 30);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 25 },
{ type: support.EffectType.MoodEffect, value: 20 },
{ type: support.EffectType.GutsBonus, value: 0 },
{ type: support.EffectType.InitialGuts, value: 0 },
{ type: support.EffectType.InitialFriendship, value: 20 },
{ type: support.EffectType.RaceBonus, value: 5 },
{ type: support.EffectType.FanBonus, value: 15 },
{ type: support.EffectType.HintLevels, value: 2 },
{ type: support.EffectType.HintFrequency, value: 40 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
test('level 49', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 49);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 34 },
{ type: support.EffectType.MoodEffect, value: 30 },
{ type: support.EffectType.GutsBonus, value: 1 },
{ type: support.EffectType.InitialGuts, value: 27 },
{ type: support.EffectType.InitialFriendship, value: 25 },
{ type: support.EffectType.RaceBonus, value: 9 },
{ type: support.EffectType.FanBonus, value: 19 },
{ type: support.EffectType.HintLevels, value: 2 },
{ type: support.EffectType.HintFrequency, value: 49 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
test('level 50', () => {
const got = support.effects(bakushinSSR as support.SupportCard, 50);
expect(got).toStrictEqual({
effects: [
{ type: support.EffectType.FriendshipBonus, value: 35 },
{ type: support.EffectType.MoodEffect, value: 30 },
{ type: support.EffectType.GutsBonus, value: 1 },
{ type: support.EffectType.InitialGuts, value: 30 },
{ type: support.EffectType.InitialFriendship, value: 25 },
{ type: support.EffectType.RaceBonus, value: 10 },
{ type: support.EffectType.FanBonus, value: 20 },
{ type: support.EffectType.HintLevels, value: 3 },
{ type: support.EffectType.HintFrequency, value: 50 },
],
unique: {
name: 'Super! Sonic! Flower Power!',
description: 'Increases Training Effectiveness when not on preferred training. (Friendship 80+)',
card_level: 30,
type: support.EffectType.UniqueOffSpecialty,
args: [80, 20],
idle_mode_sub_rate: 30,
} satisfies support.UniqueEffect,
});
});
});
});
+284 -293
View File
@@ -4,227 +4,215 @@ import * as support_card_level from './support_card_level.json';
* Support card definitions.
*/
export interface SupportCard {
/**
* Support card ID.
*/
id: number;
/**
* Support card name including variant, e.g.
* `[The Brightest Star in Japan!] Special Week`.
*/
name: string;
/**
* ID of the card's representative character.
*/
chara_id: number;
/**
* Name of the card's character.
* May differ from the name of the `chara_id` character.
*/
chara_name: string;
/**
* Support card rarity.
*/
rarity: Rarity;
/**
* Support card specialty.
*/
specialty: Specialty;
/**
* Blanket support card type.
*/
type: Type;
/**
* Support card effects per five level increment.
*/
effects: [
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
Effect[],
];
/**
* Unique effect, for cards that have them.
*/
unique_effect?: UniqueEffect;
/**
* Hints that the card can yield.
*/
hints: Hint[];
/**
* Number of recreation events available with the card,
* not including recreation with the individual characters in a group.
*/
outings?: number;
/**
* Support card ID.
*/
id: number;
/**
* Support card name including variant, e.g.
* `[The Brightest Star in Japan!] Special Week`.
*/
name: string;
/**
* ID of the card's representative character.
*/
chara_id: number;
/**
* Name of the card's character.
* May differ from the name of the `chara_id` character.
*/
chara_name: string;
/**
* Support card rarity.
*/
rarity: Rarity;
/**
* Support card specialty.
*/
specialty: Specialty;
/**
* Blanket support card type.
*/
type: Type;
/**
* Support card effects per five level increment.
*/
effects: [Effect[], Effect[], Effect[], Effect[], Effect[], Effect[], Effect[], Effect[], Effect[], Effect[], Effect[]];
/**
* Unique effect, for cards that have them.
*/
unique_effect?: UniqueEffect;
/**
* Hints that the card can yield.
*/
hints: Hint[];
/**
* Number of recreation events available with the card,
* not including recreation with the individual characters in a group.
*/
outings?: number;
}
/**
* Support card rarity levels.
*/
export enum Rarity {
R = 1,
SR = 2,
SSR = 3,
R = 1,
SR = 2,
SSR = 3,
}
/**
* Support card specialty attribute.
*/
export enum Specialty {
None = 0, // for pals and groups
Speed = 101,
Power = 102,
Guts = 103,
Stamina = 105,
Wit = 106,
None = 0, // for pals and groups
Speed = 101,
Power = 102,
Guts = 103,
Stamina = 105,
Wit = 106,
}
/**
* Support card types.
*/
export enum Type {
Normal = 1,
Pal = 2,
Group = 3,
Normal = 1,
Pal = 2,
Group = 3,
}
/**
* Support card basic effects.
*/
export interface Effect {
/**
* Type of the effect.
*/
type: EffectType;
/**
* When positive, the amount of the effect.
* When -1, interpolate between the previous and next nonnegative values.
*/
value: number;
/**
* Type of the effect.
*/
type: EffectType;
/**
* When positive, the amount of the effect.
* When -1, interpolate between the previous and next nonnegative values.
*/
value: number;
}
/**
* Support card unique effects.
*/
export interface UniqueEffect {
/**
* Name of the unique effect.
*/
name: string;
/**
* In-game description of the effect.
*/
description: string;
/**
* Card level at which the unique effect unlocks.
*/
card_level: number;
/**
* First type of the effect.
*/
type: EffectType;
/**
* Values parameterizing the first type.
*/
args: number[];
/**
* Second type of the effect, if applicable.
*/
type2?: EffectType;
/**
* Values parameterizing the second type.
*/
args2?: number[];
/**
* Mystery parameter that one day we might figure out.
*/
idle_mode_sub_rate: number;
/**
* Name of the unique effect.
*/
name: string;
/**
* In-game description of the effect.
*/
description: string;
/**
* Card level at which the unique effect unlocks.
*/
card_level: number;
/**
* First type of the effect.
*/
type: EffectType;
/**
* Values parameterizing the first type.
*/
args: number[];
/**
* Second type of the effect, if applicable.
*/
type2?: EffectType;
/**
* Values parameterizing the second type.
*/
args2?: number[];
/**
* Mystery parameter that one day we might figure out.
*/
idle_mode_sub_rate: number;
}
/**
* Basic and unique effect types.
*/
export enum EffectType {
FriendshipBonus = 1,
MoodEffect = 2,
SpeedBonus = 3,
StaminaBonus = 4,
PowerBonus = 5,
GutsBonus = 6,
WitBonus = 7,
TrainingEffectiveness = 8,
InitialSpeed = 9,
InitialStamina = 10,
InitialPower = 11,
InitialGuts = 12,
InitialWit = 13,
InitialFriendship = 14,
RaceBonus = 15,
FanBonus = 16,
HintLevels = 17,
HintFrequency = 18,
SpecialtyPriority = 19,
MaxSpeed = 20,
MaxStamina = 21,
MaxPower = 22,
MaxGuts = 23,
MaxWit = 24,
EventRecovery = 25,
EventEffectiveness = 26,
FailureProtection = 27,
EnergyCostReduction = 28,
MinigameEffectiveness = 29,
SkillPointBonus = 30,
WitFriendshipRecovery = 31,
FriendshipBonus = 1,
MoodEffect = 2,
SpeedBonus = 3,
StaminaBonus = 4,
PowerBonus = 5,
GutsBonus = 6,
WitBonus = 7,
TrainingEffectiveness = 8,
InitialSpeed = 9,
InitialStamina = 10,
InitialPower = 11,
InitialGuts = 12,
InitialWit = 13,
InitialFriendship = 14,
RaceBonus = 15,
FanBonus = 16,
HintLevels = 17,
HintFrequency = 18,
SpecialtyPriority = 19,
MaxSpeed = 20,
MaxStamina = 21,
MaxPower = 22,
MaxGuts = 23,
MaxWit = 24,
EventRecovery = 25,
EventEffectiveness = 26,
FailureProtection = 27,
EnergyCostReduction = 28,
MinigameEffectiveness = 29,
SkillPointBonus = 30,
WitFriendshipRecovery = 31,
UniqueFriendshipThreshold = 101,
UniqueOffSpecialty = 102,
UniqueColorThresholdTrainingEffectiveness = 103,
UniqueFanScalingTrainingEffectiveness = 104,
UniqueColorInitialStats = 105,
UniqueRainbowStack = 106,
UniqueLowEnergyScaling = 107,
UniqueMaxEnergyScaling = 108,
UniqueTotalFriendship = 109,
UniqueCardCount = 110,
UniqueFacilityLevel = 111,
UniqueDisableFailure = 112,
UniqueFriendshipTrainingAdditionalEffect = 113,
UniqueFriendshipThreshold = 101,
UniqueOffSpecialty = 102,
UniqueColorThresholdTrainingEffectiveness = 103,
UniqueFanScalingTrainingEffectiveness = 104,
UniqueColorInitialStats = 105,
UniqueRainbowStack = 106,
UniqueLowEnergyScaling = 107,
UniqueMaxEnergyScaling = 108,
UniqueTotalFriendship = 109,
UniqueCardCount = 110,
UniqueFacilityLevel = 111,
UniqueDisableFailure = 112,
UniqueFriendshipTrainingAdditionalEffect = 113,
}
/**
* Hint event bonuses.
*/
export interface Hint {
skill_id?: number;
speed?: number;
stamina?: number;
power?: number;
guts?: number;
wit?: number;
sp?: number;
skill_id?: number;
speed?: number;
stamina?: number;
power?: number;
guts?: number;
wit?: number;
sp?: number;
}
export async function supports(): Promise<SupportCard[]> {
const resp = await fetch('/api/global/support');
return resp.json();
const resp = await fetch('/api/global/support');
return resp.json();
}
export const SPECIALTY_ICON = {
Speed: 0,
Stamina: 1,
Power: 2,
Guts: 3,
Wit: 4,
Pal: 5,
Group: 6,
Speed: 0,
Stamina: 1,
Power: 2,
Guts: 3,
Wit: 4,
Pal: 5,
Group: 6,
} as const;
/**
@@ -234,135 +222,138 @@ export const SPECIALTY_ICON = {
* @returns Current support card effects and its unique effect if active at the given level.
*/
export function effects(card: SupportCard, level: number) {
const j = Math.floor(level / 5);
const k = Math.ceil(level / 5);
const effects = card.effects[0].map((e) => {
const s = card.effects.map((l) => l.find((f) => f.type === e.type)!);
const left = s.findLastIndex((f, i) => i <= j && f.value >= 0);
const right = s.findIndex((f, i) => k <= i && f.value >= 0);
if (left < 0) {
// Effect isn't yet active.
return { type: e.type, value: 0 };
}
if (right < 0) {
// Effect is at its maximum value.
return { type: e.type, value: s[left]!.value };
}
if (left === right) {
// The current level is an interpolation boundary.
// Don't try to interpolate.
return { type: e.type, value: s[left]!.value };
}
const xl = Math.max(left * 5, 1);
const xr = Math.max(right * 5, 1);
const lo = s[left]!.value;
const hi = s[right]!.value;
const value = Math.floor((hi * (level - xl) + lo * (xr - level)) / (xr - xl));
return { type: e.type, value };
});
if (card.unique_effect != null && level >= card.unique_effect.card_level) {
return { effects, unique: card.unique_effect }
}
return { effects };
const j = Math.floor(level / 5);
const k = Math.ceil(level / 5);
const effects = card.effects[0].map((e) => {
const s = card.effects.map((l) => l.find((f) => f.type === e.type)!);
const left = s.findLastIndex((f, i) => i <= j && f.value >= 0);
const right = s.findIndex((f, i) => k <= i && f.value >= 0);
if (left < 0) {
// Effect isn't yet active.
return { type: e.type, value: 0 };
}
if (right < 0) {
// Effect is at its maximum value.
return { type: e.type, value: s[left]!.value };
}
if (left === right) {
// The current level is an interpolation boundary.
// Don't try to interpolate.
return { type: e.type, value: s[left]!.value };
}
const xl = Math.max(left * 5, 1);
const xr = Math.max(right * 5, 1);
const lo = s[left]!.value;
const hi = s[right]!.value;
const value = Math.floor((hi * (level - xl) + lo * (xr - level)) / (xr - xl));
return { type: e.type, value };
});
if (card.unique_effect != null && level >= card.unique_effect.card_level) {
return { effects, unique: card.unique_effect };
}
return { effects };
}
export function level(rarity: Rarity, exp: number) {
// We use the ! assertion because we want an error when exp is nonsense.
return support_card_level.levels.findLast((l) => l.rarity === rarity && l.exp <= exp)!.level;
// We use the ! assertion because we want an error when exp is nonsense.
return support_card_level.levels.findLast((l) => l.rarity === rarity && l.exp <= exp)!.level;
}
function singleUniqueEffects(type: EffectType, args: number[], deck: SupportCard[]): Effect[] {
switch (type) {
case EffectType.FriendshipBonus:
case EffectType.MoodEffect:
case EffectType.SpeedBonus:
case EffectType.StaminaBonus:
case EffectType.PowerBonus:
case EffectType.GutsBonus:
case EffectType.WitBonus:
case EffectType.TrainingEffectiveness:
case EffectType.InitialSpeed:
case EffectType.InitialStamina:
case EffectType.InitialPower:
case EffectType.InitialGuts:
case EffectType.InitialWit:
case EffectType.InitialFriendship:
case EffectType.RaceBonus:
case EffectType.FanBonus:
case EffectType.HintLevels:
case EffectType.HintFrequency:
case EffectType.SpecialtyPriority:
case EffectType.MaxSpeed:
case EffectType.MaxStamina:
case EffectType.MaxPower:
case EffectType.MaxGuts:
case EffectType.MaxWit:
case EffectType.EventRecovery:
case EffectType.EventEffectiveness:
case EffectType.FailureProtection:
case EffectType.EnergyCostReduction:
case EffectType.MinigameEffectiveness:
case EffectType.SkillPointBonus:
case EffectType.WitFriendshipRecovery:
return [{ type, value: args[0] }];
switch (type) {
case EffectType.FriendshipBonus:
case EffectType.MoodEffect:
case EffectType.SpeedBonus:
case EffectType.StaminaBonus:
case EffectType.PowerBonus:
case EffectType.GutsBonus:
case EffectType.WitBonus:
case EffectType.TrainingEffectiveness:
case EffectType.InitialSpeed:
case EffectType.InitialStamina:
case EffectType.InitialPower:
case EffectType.InitialGuts:
case EffectType.InitialWit:
case EffectType.InitialFriendship:
case EffectType.RaceBonus:
case EffectType.FanBonus:
case EffectType.HintLevels:
case EffectType.HintFrequency:
case EffectType.SpecialtyPriority:
case EffectType.MaxSpeed:
case EffectType.MaxStamina:
case EffectType.MaxPower:
case EffectType.MaxGuts:
case EffectType.MaxWit:
case EffectType.EventRecovery:
case EffectType.EventEffectiveness:
case EffectType.FailureProtection:
case EffectType.EnergyCostReduction:
case EffectType.MinigameEffectiveness:
case EffectType.SkillPointBonus:
case EffectType.WitFriendshipRecovery:
return [{ type, value: args[0] }];
case EffectType.UniqueFriendshipThreshold:
return [{ type: args[1], value: args[2] }, { type: args[3], value: args[4] }];
case EffectType.UniqueOffSpecialty:
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }];
case EffectType.UniqueColorThresholdTrainingEffectiveness:
if (new Set(deck.map((card) => card.specialty)).size < args[0]) {
return [];
}
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }];
case EffectType.UniqueFanScalingTrainingEffectiveness:
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }]
case EffectType.UniqueColorInitialStats:
return deck.flatMap((card) => {
switch (card.specialty) {
case Specialty.Speed:
return [{ type: EffectType.InitialSpeed, value: args[0] }];
case Specialty.Power:
return [{ type: EffectType.InitialPower, value: args[0] }];
case Specialty.Guts:
return [{ type: EffectType.InitialGuts, value: args[0] }];
case Specialty.Stamina:
return [{ type: EffectType.InitialStamina, value: args[0] }];
case Specialty.Wit:
return [{ type: EffectType.InitialWit, value: args[0] }];
default:
return [
{ type: EffectType.InitialSpeed, value: args[1] },
{ type: EffectType.InitialStamina, value: args[1] },
{ type: EffectType.InitialPower, value: args[1] },
{ type: EffectType.InitialGuts, value: args[1] },
{ type: EffectType.InitialWit, value: args[1] },
];
}
})
case EffectType.UniqueRainbowStack:
return [{ type: args[1], value: args[0] * args[2] }];
case EffectType.UniqueLowEnergyScaling:
return [{ type: args[0], value: args[3] }];
case EffectType.UniqueMaxEnergyScaling:
return [{ type: args[0], value: args[4] }];
case EffectType.UniqueTotalFriendship:
return [{ type: args[0], value: 600 / args[1] }];
case EffectType.UniqueCardCount:
return [{ type: args[0], value: 5 * args[1] }];
case EffectType.UniqueFacilityLevel:
return [{ type: args[0], value: 5 * args[1] }];
case EffectType.UniqueDisableFailure:
return [];
case EffectType.UniqueFriendshipTrainingAdditionalEffect:
return [{ type: args[0], value: args[1] }];
}
case EffectType.UniqueFriendshipThreshold:
return [
{ type: args[1], value: args[2] },
{ type: args[3], value: args[4] },
];
case EffectType.UniqueOffSpecialty:
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }];
case EffectType.UniqueColorThresholdTrainingEffectiveness:
if (new Set(deck.map((card) => card.specialty)).size < args[0]) {
return [];
}
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }];
case EffectType.UniqueFanScalingTrainingEffectiveness:
return [{ type: EffectType.TrainingEffectiveness, value: args[1] }];
case EffectType.UniqueColorInitialStats:
return deck.flatMap((card) => {
switch (card.specialty) {
case Specialty.Speed:
return [{ type: EffectType.InitialSpeed, value: args[0] }];
case Specialty.Power:
return [{ type: EffectType.InitialPower, value: args[0] }];
case Specialty.Guts:
return [{ type: EffectType.InitialGuts, value: args[0] }];
case Specialty.Stamina:
return [{ type: EffectType.InitialStamina, value: args[0] }];
case Specialty.Wit:
return [{ type: EffectType.InitialWit, value: args[0] }];
default:
return [
{ type: EffectType.InitialSpeed, value: args[1] },
{ type: EffectType.InitialStamina, value: args[1] },
{ type: EffectType.InitialPower, value: args[1] },
{ type: EffectType.InitialGuts, value: args[1] },
{ type: EffectType.InitialWit, value: args[1] },
];
}
});
case EffectType.UniqueRainbowStack:
return [{ type: args[1], value: args[0] * args[2] }];
case EffectType.UniqueLowEnergyScaling:
return [{ type: args[0], value: args[3] }];
case EffectType.UniqueMaxEnergyScaling:
return [{ type: args[0], value: args[4] }];
case EffectType.UniqueTotalFriendship:
return [{ type: args[0], value: 600 / args[1] }];
case EffectType.UniqueCardCount:
return [{ type: args[0], value: 5 * args[1] }];
case EffectType.UniqueFacilityLevel:
return [{ type: args[0], value: 5 * args[1] }];
case EffectType.UniqueDisableFailure:
return [];
case EffectType.UniqueFriendshipTrainingAdditionalEffect:
return [{ type: args[0], value: args[1] }];
}
}
export function flattenUnique(unique: UniqueEffect, deck: SupportCard[]): Effect[] {
const r = [...singleUniqueEffects(unique.type, unique.args, deck)];
if (unique.type2 != null) {
r.push(...singleUniqueEffects(unique.type2, unique.args2!, deck));
}
return r;
const r = [...singleUniqueEffects(unique.type, unique.args, deck)];
if (unique.type2 != null) {
r.push(...singleUniqueEffects(unique.type2, unique.args2!, deck));
}
return r;
}
File diff suppressed because it is too large Load Diff
+413 -413
View File
@@ -1,415 +1,415 @@
{
"id": 10018,
"name": "[Tracen Academy] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 1,
"specialty": 101,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 5
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 10
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 10
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": 15
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 15
},
{
"type": 14,
"value": 15
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 20
}
],
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": 15
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 15
},
{
"type": 14,
"value": 15
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 10
},
{
"type": 19,
"value": 20
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 2,
"value": 25
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 20
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 35
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": 1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
]
],
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"speed": 6,
"power": 2
}
]
"id": 10018,
"name": "[Tracen Academy] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 1,
"specialty": 101,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 5
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 10
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 10
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": 15
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 15
},
{
"type": 14,
"value": 15
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 20
}
],
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": 15
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 15
},
{
"type": 14,
"value": 15
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 10
},
{
"type": 19,
"value": 20
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 2,
"value": 25
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": 20
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 35
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": 1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 3,
"value": -1
},
{
"type": 9,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
]
],
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"speed": 6,
"power": 2
}
]
}
+423 -427
View File
@@ -1,429 +1,425 @@
{
"id": 20042,
"name": "[The Perfect Morning Greeting!] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 2,
"specialty": 102,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 10
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 1
},
{
"type": 11,
"value": 10
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 15
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 15
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 19,
"value": 35
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 20
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 20
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": 1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 10
},
{
"type": 16,
"value": 20
},
{
"type": 19,
"value": 50
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
]
],
"unique_effect": {
"name": "The Perfect Morning Greeting!",
"description": "Friendship Bonus and Specialty Priority",
"card_level": 25,
"type": 1,
"args": [
10
],
"type2": 19,
"args2": [
20
],
"idle_mode_sub_rate": 0
},
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"stamina": 2,
"power": 6
}
]
"id": 20042,
"name": "[The Perfect Morning Greeting!] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 2,
"specialty": 102,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 10
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 1
},
{
"type": 11,
"value": 10
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 15
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 15
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 15
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 19,
"value": 35
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": 20
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": 5
},
{
"type": 11,
"value": 20
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": 1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 10
},
{
"type": 16,
"value": 20
},
{
"type": 19,
"value": 50
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 5,
"value": -1
},
{
"type": 8,
"value": -1
},
{
"type": 11,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 19,
"value": -1
}
]
],
"unique_effect": {
"name": "The Perfect Morning Greeting!",
"description": "Friendship Bonus and Specialty Priority",
"card_level": 25,
"type": 1,
"args": [10],
"type2": 19,
"args2": [20],
"idle_mode_sub_rate": 0
},
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"stamina": 2,
"power": 6
}
]
}
+466 -469
View File
@@ -1,471 +1,468 @@
{
"id": 30083,
"name": "[Super! Sonic! Flower Power!] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 3,
"specialty": 103,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 17,
"value": 1
},
{
"type": 18,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 10
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 20
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": 25
},
{
"type": 2,
"value": 20
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 17,
"value": 2
},
{
"type": 18,
"value": 40
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": 1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 30
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": 15
},
{
"type": 14,
"value": 25
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": 35
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": 30
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 10
},
{
"type": 16,
"value": 20
},
{
"type": 17,
"value": 3
},
{
"type": 18,
"value": 50
}
]
],
"unique_effect": {
"name": "Super! Sonic! Flower Power!",
"description": "Increases Training Effectiveness when not on preferred training. (Friendship 80+)",
"card_level": 30,
"type": 102,
"args": [
80,
20
],
"idle_mode_sub_rate": 30
},
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"speed": 1,
"power": 1,
"guts": 6
}
]
"id": 30083,
"name": "[Super! Sonic! Flower Power!] Sakura Bakushin O",
"chara_id": 1041,
"chara_name": "Sakura Bakushin O",
"rarity": 3,
"specialty": 103,
"type": 1,
"effects": [
[
{
"type": 1,
"value": 10
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 1
},
{
"type": 16,
"value": 1
},
{
"type": 17,
"value": 1
},
{
"type": 18,
"value": 5
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 10
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 10
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 20
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": 25
},
{
"type": 2,
"value": 20
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": 20
},
{
"type": 15,
"value": 5
},
{
"type": 16,
"value": 15
},
{
"type": 17,
"value": 2
},
{
"type": 18,
"value": 40
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": 1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": -1
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": -1
},
{
"type": 2,
"value": 30
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": 15
},
{
"type": 14,
"value": 25
},
{
"type": 15,
"value": -1
},
{
"type": 16,
"value": -1
},
{
"type": 17,
"value": -1
},
{
"type": 18,
"value": -1
}
],
[
{
"type": 1,
"value": 35
},
{
"type": 2,
"value": -1
},
{
"type": 6,
"value": -1
},
{
"type": 12,
"value": 30
},
{
"type": 14,
"value": -1
},
{
"type": 15,
"value": 10
},
{
"type": 16,
"value": 20
},
{
"type": 17,
"value": 3
},
{
"type": 18,
"value": 50
}
]
],
"unique_effect": {
"name": "Super! Sonic! Flower Power!",
"description": "Increases Training Effectiveness when not on preferred training. (Friendship 80+)",
"card_level": 30,
"type": 102,
"args": [80, 20],
"idle_mode_sub_rate": 30
},
"hints": [
{
"skill_id": 200652
},
{
"skill_id": 200791
},
{
"skill_id": 200841
},
{
"skill_id": 200962
},
{
"skill_id": 200972
},
{
"skill_id": 200982
},
{
"skill_id": 200992
},
{
"skill_id": 201012
},
{
"speed": 1,
"power": 1,
"guts": 6
}
]
}
+226 -226
View File
@@ -2,71 +2,71 @@ import * as z from 'zod';
import * as race from './race';
const Skill = z.strictObject({
skill_id: z.int32(),
level: z.int32(),
skill_id: z.int32(),
level: z.int32(),
});
export type Skill = z.infer<typeof Skill>;
const SkillTip = z.strictObject({
group_id: z.int32(),
rarity: z.int32(),
level: z.int32(),
group_id: z.int32(),
rarity: z.int32(),
level: z.int32(),
});
export type SkillTip = z.infer<typeof SkillTip>;
/** SingleModeSupportCard */
const SupportCard = z.strictObject({
position: z.int32(),
support_card_id: z.int32(),
limit_break_count: z.int32(),
exp: z.int32(),
owner_viewer_id: z.number(),
position: z.int32(),
support_card_id: z.int32(),
limit_break_count: z.int32(),
exp: z.int32(),
owner_viewer_id: z.number(),
});
export type SupportCard = z.infer<typeof SupportCard>;
const GroupOuting = z.strictObject({
chara_id: z.int32(),
is_outing: z.int32(),
story_step: z.int32(),
chara_id: z.int32(),
is_outing: z.int32(),
story_step: z.int32(),
});
export type GroupOuting = z.infer<typeof GroupOuting>;
const Evaluation = z.strictObject({
target_id: z.int32(),
training_partner_id: z.int32(),
evaluation: z.int32(),
is_outing: z.int32(),
story_step: z.int32(),
is_appear: z.int32(),
group_outing_info_array: z.array(GroupOuting),
target_id: z.int32(),
training_partner_id: z.int32(),
evaluation: z.int32(),
is_outing: z.int32(),
story_step: z.int32(),
is_appear: z.int32(),
group_outing_info_array: z.array(GroupOuting),
});
export type Evaluation = z.infer<typeof Evaluation>;
const TrainingLevel = z.strictObject({
command_id: z.int32(),
level: z.int32(),
command_id: z.int32(),
level: z.int32(),
});
export type TrainingLevel = z.infer<typeof TrainingLevel>;
const GuestOuting = z.strictObject({
support_card_id: z.int32(),
story_step: z.int32(),
group_outing_info_array: z.array(GroupOuting),
support_card_id: z.int32(),
story_step: z.int32(),
group_outing_info_array: z.array(GroupOuting),
});
export type GuestOuting = z.infer<typeof GuestOuting>;
/** SingleModeSkillUpgrade */
const SkillUpgrade = z.strictObject({
condition_id: z.int32(),
total_count: z.int32(),
current_count: z.int32(),
condition_id: z.int32(),
total_count: z.int32(),
current_count: z.int32(),
});
export type SkillUpgrade = z.infer<typeof SkillUpgrade>;
@@ -74,290 +74,290 @@ export type SkillUpgrade = z.infer<typeof SkillUpgrade>;
const AptitudeLevel = z.enum(race.AptitudeLevel);
const SingleModeChara = z.strictObject({
single_mode_chara_id: z.int32(),
card_id: z.int32(),
chara_grade: z.int32(),
speed: z.int32(),
stamina: z.int32(),
power: z.int32(),
wiz: z.int32(),
guts: z.int32(),
vital: z.int32(),
max_speed: z.int32(),
max_stamina: z.int32(),
max_power: z.int32(),
max_wiz: z.int32(),
max_guts: z.int32(),
default_max_speed: z.int32(),
default_max_stamina: z.int32(),
default_max_power: z.int32(),
default_max_wiz: z.int32(),
default_max_guts: z.int32(),
max_vital: z.int32(),
motivation: z.int32(),
fans: z.int32(),
rarity: z.int32(),
race_program_id: z.int32(),
reserve_race_program_id: z.int32(),
race_running_style: z.int32(),
is_short_race: z.int32(),
talent_level: z.int32(),
skill_array: z.array(Skill),
disable_skill_id_array: z.array(z.int32()),
skill_tips_array: z.array(SkillTip),
support_card_array: z.array(SupportCard),
succession_trained_chara_id_1: z.int32(),
succession_trained_chara_id_2: z.int32(),
proper_distance_short: AptitudeLevel,
proper_distance_mile: AptitudeLevel,
proper_distance_middle: AptitudeLevel,
proper_distance_long: AptitudeLevel,
proper_running_style_nige: AptitudeLevel,
proper_running_style_senko: AptitudeLevel,
proper_running_style_sashi: AptitudeLevel,
proper_running_style_oikomi: AptitudeLevel,
proper_ground_turf: AptitudeLevel,
proper_ground_dirt: AptitudeLevel,
turn: z.int32(),
skill_point: z.int32(),
short_cut_state: z.int32(),
state: z.int32(),
playing_state: z.int32(),
scenario_id: z.int32(),
route_id: z.int32(),
start_time: z.string(),
evaluation_info_array: z.array(Evaluation),
training_level_info_array: z.array(TrainingLevel),
nickname_id_array: z.array(z.int32()),
chara_effect_id_array: z.array(z.int32()),
route_race_id_array: z.array(z.int32()),
guest_outing_info_array: z.array(GuestOuting),
skill_upgrade_info_array: z.array(SkillUpgrade),
single_mode_chara_id: z.int32(),
card_id: z.int32(),
chara_grade: z.int32(),
speed: z.int32(),
stamina: z.int32(),
power: z.int32(),
wiz: z.int32(),
guts: z.int32(),
vital: z.int32(),
max_speed: z.int32(),
max_stamina: z.int32(),
max_power: z.int32(),
max_wiz: z.int32(),
max_guts: z.int32(),
default_max_speed: z.int32(),
default_max_stamina: z.int32(),
default_max_power: z.int32(),
default_max_wiz: z.int32(),
default_max_guts: z.int32(),
max_vital: z.int32(),
motivation: z.int32(),
fans: z.int32(),
rarity: z.int32(),
race_program_id: z.int32(),
reserve_race_program_id: z.int32(),
race_running_style: z.int32(),
is_short_race: z.int32(),
talent_level: z.int32(),
skill_array: z.array(Skill),
disable_skill_id_array: z.array(z.int32()),
skill_tips_array: z.array(SkillTip),
support_card_array: z.array(SupportCard),
succession_trained_chara_id_1: z.int32(),
succession_trained_chara_id_2: z.int32(),
proper_distance_short: AptitudeLevel,
proper_distance_mile: AptitudeLevel,
proper_distance_middle: AptitudeLevel,
proper_distance_long: AptitudeLevel,
proper_running_style_nige: AptitudeLevel,
proper_running_style_senko: AptitudeLevel,
proper_running_style_sashi: AptitudeLevel,
proper_running_style_oikomi: AptitudeLevel,
proper_ground_turf: AptitudeLevel,
proper_ground_dirt: AptitudeLevel,
turn: z.int32(),
skill_point: z.int32(),
short_cut_state: z.int32(),
state: z.int32(),
playing_state: z.int32(),
scenario_id: z.int32(),
route_id: z.int32(),
start_time: z.string(),
evaluation_info_array: z.array(Evaluation),
training_level_info_array: z.array(TrainingLevel),
nickname_id_array: z.array(z.int32()),
chara_effect_id_array: z.array(z.int32()),
route_race_id_array: z.array(z.int32()),
guest_outing_info_array: z.array(GuestOuting),
skill_upgrade_info_array: z.array(SkillUpgrade),
});
const ZeroedSingleModeChara = z.strictObject({
single_mode_chara_id: z.literal(0),
card_id: z.int32(),
chara_grade: z.literal(0),
speed: z.literal(0),
stamina: z.literal(0),
power: z.literal(0),
wiz: z.literal(0),
guts: z.literal(0),
vital: z.literal(0),
max_speed: z.literal(0),
max_stamina: z.literal(0),
max_power: z.literal(0),
max_wiz: z.literal(0),
max_guts: z.literal(0),
default_max_speed: z.literal(0),
default_max_stamina: z.literal(0),
default_max_power: z.literal(0),
default_max_wiz: z.literal(0),
default_max_guts: z.literal(0),
max_vital: z.literal(0),
motivation: z.literal(0),
fans: z.literal(0),
rarity: z.int32(),
race_program_id: z.literal(0),
reserve_race_program_id: z.literal(0),
race_running_style: z.literal(0),
is_short_race: z.literal(0),
talent_level: z.int32(),
skill_array: z.array(z.never()),
disable_skill_id_array: z.array(z.never()),
skill_tips_array: z.array(z.never()),
support_card_array: z.array(z.never()),
succession_trained_chara_id_1: z.int32(),
succession_trained_chara_id_2: z.int32(),
proper_distance_short: z.literal(0),
proper_distance_mile: z.literal(0),
proper_distance_middle: z.literal(0),
proper_distance_long: z.literal(0),
proper_running_style_nige: z.literal(0),
proper_running_style_senko: z.literal(0),
proper_running_style_sashi: z.literal(0),
proper_running_style_oikomi: z.literal(0),
proper_ground_turf: z.literal(0),
proper_ground_dirt: z.literal(0),
turn: z.literal(0),
skill_point: z.literal(0),
short_cut_state: z.literal(0),
state: z.literal(0),
playing_state: z.literal(0),
scenario_id: z.int32(),
route_id: z.literal(0),
start_time: z.literal(''),
evaluation_info_array: z.array(z.never()),
training_level_info_array: z.array(z.never()),
nickname_id_array: z.array(z.never()),
chara_effect_id_array: z.array(z.never()),
route_race_id_array: z.array(z.never()),
guest_outing_info_array: z.array(z.never()),
skill_upgrade_info_array: z.array(z.never()),
single_mode_chara_id: z.literal(0),
card_id: z.int32(),
chara_grade: z.literal(0),
speed: z.literal(0),
stamina: z.literal(0),
power: z.literal(0),
wiz: z.literal(0),
guts: z.literal(0),
vital: z.literal(0),
max_speed: z.literal(0),
max_stamina: z.literal(0),
max_power: z.literal(0),
max_wiz: z.literal(0),
max_guts: z.literal(0),
default_max_speed: z.literal(0),
default_max_stamina: z.literal(0),
default_max_power: z.literal(0),
default_max_wiz: z.literal(0),
default_max_guts: z.literal(0),
max_vital: z.literal(0),
motivation: z.literal(0),
fans: z.literal(0),
rarity: z.int32(),
race_program_id: z.literal(0),
reserve_race_program_id: z.literal(0),
race_running_style: z.literal(0),
is_short_race: z.literal(0),
talent_level: z.int32(),
skill_array: z.array(z.never()),
disable_skill_id_array: z.array(z.never()),
skill_tips_array: z.array(z.never()),
support_card_array: z.array(z.never()),
succession_trained_chara_id_1: z.int32(),
succession_trained_chara_id_2: z.int32(),
proper_distance_short: z.literal(0),
proper_distance_mile: z.literal(0),
proper_distance_middle: z.literal(0),
proper_distance_long: z.literal(0),
proper_running_style_nige: z.literal(0),
proper_running_style_senko: z.literal(0),
proper_running_style_sashi: z.literal(0),
proper_running_style_oikomi: z.literal(0),
proper_ground_turf: z.literal(0),
proper_ground_dirt: z.literal(0),
turn: z.literal(0),
skill_point: z.literal(0),
short_cut_state: z.literal(0),
state: z.literal(0),
playing_state: z.literal(0),
scenario_id: z.int32(),
route_id: z.literal(0),
start_time: z.literal(''),
evaluation_info_array: z.array(z.never()),
training_level_info_array: z.array(z.never()),
nickname_id_array: z.array(z.never()),
chara_effect_id_array: z.array(z.never()),
route_race_id_array: z.array(z.never()),
guest_outing_info_array: z.array(z.never()),
skill_upgrade_info_array: z.array(z.never()),
});
export type SingleModeChara = z.infer<typeof SingleModeChara>;
const ProgressInfo = z.strictObject({
chara_info: z.optional(SingleModeChara.or(ZeroedSingleModeChara)),
start_time: z.string(),
end_time: z.string(),
dress_id: z.int32(),
chara_info: z.optional(SingleModeChara.or(ZeroedSingleModeChara)),
start_time: z.string(),
end_time: z.string(),
dress_id: z.int32(),
});
export type ProgressInfo = z.infer<typeof ProgressInfo>;
const CharaEffectLog = z.strictObject({
chara_effect_id: z.int32(),
is_active: z.boolean(),
chara_effect_id: z.int32(),
is_active: z.boolean(),
});
export type CharaEffectLog = z.infer<typeof CharaEffectLog>;
/** IdleSingleModeSignedInt */
const SignedInt = z.strictObject({
sign: z.int32(),
value: z.int32(),
sign: z.int32(),
value: z.int32(),
});
export type SignedInt = z.infer<typeof SignedInt>;
/** IdleSingleModeGainInfo */
const GainInfo = z.strictObject({
speed: SignedInt,
stamina: SignedInt,
power: SignedInt,
wiz: SignedInt,
guts: SignedInt,
max_speed: z.int32(),
max_stamina: z.int32(),
max_power: z.int32(),
max_wiz: z.int32(),
max_guts: z.int32(),
proper_distance_short: z.int32(),
proper_distance_mile: z.int32(),
proper_distance_middle: z.int32(),
proper_distance_long: z.int32(),
proper_running_style_nige: z.int32(),
proper_running_style_senko: z.int32(),
proper_running_style_sashi: z.int32(),
proper_running_style_oikomi: z.int32(),
proper_ground_turf: z.int32(),
proper_ground_dirt: z.int32(),
skill_point: z.int32(),
skill_tips_array: z.array(SkillTip),
speed: SignedInt,
stamina: SignedInt,
power: SignedInt,
wiz: SignedInt,
guts: SignedInt,
max_speed: z.int32(),
max_stamina: z.int32(),
max_power: z.int32(),
max_wiz: z.int32(),
max_guts: z.int32(),
proper_distance_short: z.int32(),
proper_distance_mile: z.int32(),
proper_distance_middle: z.int32(),
proper_distance_long: z.int32(),
proper_running_style_nige: z.int32(),
proper_running_style_senko: z.int32(),
proper_running_style_sashi: z.int32(),
proper_running_style_oikomi: z.int32(),
proper_ground_turf: z.int32(),
proper_ground_dirt: z.int32(),
skill_point: z.int32(),
skill_tips_array: z.array(SkillTip),
});
export type GainInfo = z.infer<typeof GainInfo>;
/** IdleSingleModeSupportCardGain */
const SupportCardGain = z.strictObject({
support_card_id: z.int32(),
gain_info: GainInfo,
support_card_id: z.int32(),
gain_info: GainInfo,
});
export type SupportCardGain = z.infer<typeof SupportCardGain>;
const Factor = z.strictObject({
factor_id: z.int32(),
level: z.int32(),
factor_id: z.int32(),
level: z.int32(),
});
export type Factor = z.infer<typeof Factor>;
/** IdleSingleModeSuccessionFactorGain */
const SuccessionFactorGain = z.strictObject({
year: z.int32(),
gain_factor_info_array: z.array(Factor),
year: z.int32(),
gain_factor_info_array: z.array(Factor),
});
export type SuccessionFactorGain = z.infer<typeof SuccessionFactorGain>;
const SingleRaceHistory = z.strictObject({
turn: z.int32(),
program_id: z.int32(),
weather: z.int32(),
ground_condition: z.int32(),
running_style: z.int32(),
result_rank: z.int32(),
frame_order: z.int32(),
npc_count: z.int32(),
turn: z.int32(),
program_id: z.int32(),
weather: z.int32(),
ground_condition: z.int32(),
running_style: z.int32(),
result_rank: z.int32(),
frame_order: z.int32(),
npc_count: z.int32(),
});
export type SingleRaceHistory = z.infer<typeof SingleRaceHistory>;
const RaceReward = z.strictObject({
item_type: z.int32(),
item_id: z.int32(),
item_num: z.int32(),
item_type: z.int32(),
item_id: z.int32(),
item_num: z.int32(),
});
export type RaceReward = z.infer<typeof RaceReward>;
const RaceRewardLimit = z.strictObject({
reward_id: z.int32(),
item_type: z.int32(),
item_id: z.int32(),
item_num: z.int32(),
rest_count: z.int32(),
reward_id: z.int32(),
item_type: z.int32(),
item_id: z.int32(),
item_num: z.int32(),
rest_count: z.int32(),
});
export type RaceRewardLimit = z.infer<typeof RaceRewardLimit>;
const CharaRaceReward = z.strictObject({
result_rank: z.int32(),
result_time: z.int32(),
race_reward: z.array(RaceReward),
race_reward_bonus: z.array(RaceReward),
race_reward_plus_bonus: z.array(RaceReward),
race_reward_bonus_win: z.array(RaceReward),
race_reward_limit: z.optional(z.array(RaceRewardLimit)),
gained_fans: z.int32(),
campaign_id_array: z.array(z.int32()),
result_rank: z.int32(),
result_time: z.int32(),
race_reward: z.array(RaceReward),
race_reward_bonus: z.array(RaceReward),
race_reward_plus_bonus: z.array(RaceReward),
race_reward_bonus_win: z.array(RaceReward),
race_reward_limit: z.optional(z.array(RaceRewardLimit)),
gained_fans: z.int32(),
campaign_id_array: z.array(z.int32()),
});
export type CharaRaceReward = z.infer<typeof CharaRaceReward>;
/** IdleSingleModeRaceHistory */
const RaceHistory = z.strictObject({
race_history: SingleRaceHistory,
race_reward_info: CharaRaceReward,
lose_tips_id: z.int32(),
race_history: SingleRaceHistory,
race_reward_info: CharaRaceReward,
lose_tips_id: z.int32(),
});
export type RaceHistory = z.infer<typeof RaceHistory>;
/** IdleSingleModeProgressLog */
const ProgressLog = z.strictObject({
chara_effect_log_array: z.array(CharaEffectLog),
support_card_gain_info_array: z.array(SupportCardGain),
event_gain_info: GainInfo,
succession_gain_info: GainInfo,
succession_factor_gain_array: z.array(SuccessionFactorGain),
race_history_array: z.array(RaceHistory),
gain_skill_id_array: z.array(z.int32()),
total_skill_point: z.int32(),
chara_effect_log_array: z.array(CharaEffectLog),
support_card_gain_info_array: z.array(SupportCardGain),
event_gain_info: GainInfo,
succession_gain_info: GainInfo,
succession_factor_gain_array: z.array(SuccessionFactorGain),
race_history_array: z.array(RaceHistory),
gain_skill_id_array: z.array(z.int32()),
total_skill_point: z.int32(),
});
export type ProgressLog = z.infer<typeof ProgressLog>;
const AddItem = z.strictObject({
item_id: z.int32(),
number: z.int32(),
item_id: z.int32(),
number: z.int32(),
});
export type AddItem = z.infer<typeof AddItem>;
const RewardSummary = z.strictObject({
add_item_list: z.array(AddItem),
add_item_list: z.array(AddItem),
});
export type RewardSummary = z.infer<typeof RewardSummary>;
const End = z.strictObject({
chara_info: SingleModeChara,
reward_summary_info: RewardSummary,
chara_info: SingleModeChara,
reward_summary_info: RewardSummary,
});
export type End = z.infer<typeof End>;
@@ -366,9 +366,9 @@ export type End = z.infer<typeof End>;
* WorkIdleSingleMode, or all the data extracted from an ISM career.
*/
export const Career = z.object({
progress_info: ProgressInfo,
progress_log_info: ProgressLog,
end_info: End,
progress_info: ProgressInfo,
progress_log_info: ProgressLog,
end_info: End,
});
export type Career = z.infer<typeof Career>;
@@ -3,145 +3,158 @@ import * as support from '$lib/data/support';
import * as scenario from '$lib/data/scenario';
function gainTotalStats(gain: ism.GainInfo) {
return gain.speed.value + gain.stamina.value + gain.power.value + gain.guts.value + gain.wiz.value;
return gain.speed.value + gain.stamina.value + gain.power.value + gain.guts.value + gain.wiz.value;
}
export const DEPENDENT = [
{
name: 'Total Stats',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gainTotalStats(gain),
},
{
name: 'Total Stats + SP',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gainTotalStats(gain) + gain.skill_point,
},
{
name: 'Speed',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.speed.value,
},
{
name: 'Stamina',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.stamina.value,
},
{
name: 'Power',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.power.value,
},
{
name: 'Guts',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.guts.value,
},
{
name: 'Wit',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.wiz.value,
},
{
name: 'SP',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.skill_point,
},
{
name: 'Skill Hints',
map: (gain: ism.GainInfo, effects: support.Effect[]) => {
// NOTE(zephyr): reduction starts with 1 because we want the value
// of each hint, not just the bonus from effects
const hintLevels = effects.reduce((l, e) => e.type === support.EffectType.HintLevels ? l + e.value : l, 1);
return gain.skill_tips_array.reduce((n, t) => n + Math.ceil(t.level / hintLevels), 0);
},
},
{
name: 'Skills',
map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.skill_tips_array.length,
},
{
name: 'Total Stats',
map: (gain: ism.GainInfo) => gainTotalStats(gain),
},
{
name: 'Total Stats + SP',
map: (gain: ism.GainInfo) => gainTotalStats(gain) + gain.skill_point,
},
{
name: 'Speed',
map: (gain: ism.GainInfo) => gain.speed.value,
},
{
name: 'Stamina',
map: (gain: ism.GainInfo) => gain.stamina.value,
},
{
name: 'Power',
map: (gain: ism.GainInfo) => gain.power.value,
},
{
name: 'Guts',
map: (gain: ism.GainInfo) => gain.guts.value,
},
{
name: 'Wit',
map: (gain: ism.GainInfo) => gain.wiz.value,
},
{
name: 'SP',
map: (gain: ism.GainInfo) => gain.skill_point,
},
{
name: 'Skill Hints',
map: (gain: ism.GainInfo, effects: support.Effect[]) => {
// NOTE(zephyr): reduction starts with 1 because we want the value
// of each hint, not just the bonus from effects
const hintLevels = effects.reduce((l, e) => (e.type === support.EffectType.HintLevels ? l + e.value : l), 1);
return gain.skill_tips_array.reduce((n, t) => n + Math.ceil(t.level / hintLevels), 0);
},
},
{
name: 'Skills',
map: (gain: ism.GainInfo) => gain.skill_tips_array.length,
},
] as const;
function label(c: ism.Career): string {
return `${c.end_info.chara_info.single_mode_chara_id} - ${c.end_info.chara_info.start_time}`;
return `${c.end_info.chara_info.single_mode_chara_id} - ${c.end_info.chara_info.start_time}`;
}
export const INDEPENDENT = [
{
name: 'Friendship Bonus',
effect: support.EffectType.FriendshipBonus,
},
{
name: 'Mood Effect',
effect: support.EffectType.MoodEffect,
},
{
name: 'Speed Bonus',
effect: support.EffectType.SpeedBonus,
},
{
name: 'Stamina Bonus',
effect: support.EffectType.StaminaBonus,
},
{
name: 'Power Bonus',
effect: support.EffectType.PowerBonus,
},
{
name: 'Guts Bonus',
effect: support.EffectType.GutsBonus,
},
{
name: 'Wit Bonus',
effect: support.EffectType.WitBonus,
},
{
name: 'Skill Point Bonus',
effect: support.EffectType.SkillPointBonus,
},
{
name: 'Training Effectiveness',
effect: support.EffectType.TrainingEffectiveness,
},
{
name: 'Initial Friendship',
effect: support.EffectType.InitialFriendship,
},
{
name: 'Race Bonus',
effect: support.EffectType.RaceBonus,
},
{
name: 'Hint Frequency',
effect: support.EffectType.HintFrequency,
},
{
name: 'Specialty Priority',
effect: support.EffectType.SpecialtyPriority,
},
{
name: 'Friendship Bonus',
effect: support.EffectType.FriendshipBonus,
},
{
name: 'Mood Effect',
effect: support.EffectType.MoodEffect,
},
{
name: 'Speed Bonus',
effect: support.EffectType.SpeedBonus,
},
{
name: 'Stamina Bonus',
effect: support.EffectType.StaminaBonus,
},
{
name: 'Power Bonus',
effect: support.EffectType.PowerBonus,
},
{
name: 'Guts Bonus',
effect: support.EffectType.GutsBonus,
},
{
name: 'Wit Bonus',
effect: support.EffectType.WitBonus,
},
{
name: 'Skill Point Bonus',
effect: support.EffectType.SkillPointBonus,
},
{
name: 'Training Effectiveness',
effect: support.EffectType.TrainingEffectiveness,
},
{
name: 'Initial Friendship',
effect: support.EffectType.InitialFriendship,
},
{
name: 'Race Bonus',
effect: support.EffectType.RaceBonus,
},
{
name: 'Hint Frequency',
effect: support.EffectType.HintFrequency,
},
{
name: 'Specialty Priority',
effect: support.EffectType.SpecialtyPriority,
},
] as const;
export type SupportCardDatum = {
'Career': string;
'Scenario': string;
'Source': string;
'Unique Effect': string;
'Idle Mode Sub Rate': number;
'Races': number;
'Race Wins': number;
} & Record<typeof INDEPENDENT[number]['name'], number> & Record<typeof DEPENDENT[number]['name'], number>;
Career: string;
Scenario: string;
Source: string;
'Unique Effect': string;
'Idle Mode Sub Rate': number;
Races: number;
'Race Wins': number;
} & Record<(typeof INDEPENDENT)[number]['name'], number> &
Record<(typeof DEPENDENT)[number]['name'], number>;
export function supportVariables(career: ism.Career, lookupCard: (id: number) => support.SupportCard, lookupScenario: (id: number) => scenario.Scenario): SupportCardDatum[] {
const deck = career.progress_log_info.support_card_gain_info_array.map((g) => lookupCard(g.support_card_id));
const scen = lookupScenario(career.end_info.chara_info.scenario_id);
return career.progress_log_info.support_card_gain_info_array.map((g, i) => {
const card = deck[i];
const level = support.level(card.rarity, career.end_info.chara_info.support_card_array[i].exp);
const { effects, unique } = support.effects(lookupCard(g.support_card_id), level);
const indep = Object.fromEntries(INDEPENDENT.map((t) => [t.name, effects.find((e) => e.type === t.effect)?.value ?? 0])) as Record<typeof INDEPENDENT[number]['name'], number>;
const dep = Object.fromEntries(DEPENDENT.map((t) => [t.name, t.map(g.gain_info, effects)])) as Record<typeof DEPENDENT[number]['name'], number>;
return {
'Career': label(career),
'Scenario': scen.name,
'Source': card.name,
'Unique Effect': unique?.description ?? 'None',
'Idle Mode Sub Rate': unique?.idle_mode_sub_rate ?? 0,
'Races': career.progress_log_info.race_history_array.length,
'Race Wins': career.progress_log_info.race_history_array.reduce((w, r) => r.race_history.result_rank === 1 ? w + 1 : w, 0),
...indep,
...dep,
};
});
export function supportVariables(
career: ism.Career,
lookupCard: (id: number) => support.SupportCard,
lookupScenario: (id: number) => scenario.Scenario,
): SupportCardDatum[] {
const deck = career.progress_log_info.support_card_gain_info_array.map((g) => lookupCard(g.support_card_id));
const scen = lookupScenario(career.end_info.chara_info.scenario_id);
return career.progress_log_info.support_card_gain_info_array.map((g, i) => {
const card = deck[i];
const level = support.level(card.rarity, career.end_info.chara_info.support_card_array[i].exp);
const { effects, unique } = support.effects(lookupCard(g.support_card_id), level);
const indep = Object.fromEntries(
INDEPENDENT.map((t) => [t.name, effects.find((e) => e.type === t.effect)?.value ?? 0]),
) as Record<(typeof INDEPENDENT)[number]['name'], number>;
const dep = Object.fromEntries(DEPENDENT.map((t) => [t.name, t.map(g.gain_info, effects)])) as Record<
(typeof DEPENDENT)[number]['name'],
number
>;
return {
Career: label(career),
Scenario: scen.name,
Source: card.name,
'Unique Effect': unique?.description ?? 'None',
'Idle Mode Sub Rate': unique?.idle_mode_sub_rate ?? 0,
Races: career.progress_log_info.race_history_array.length,
'Race Wins': career.progress_log_info.race_history_array.reduce(
(w, r) => (r.race_history.result_rank === 1 ? w + 1 : w),
0,
),
...indep,
...dep,
};
});
}