mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-12 09:18:25 +03:00
Add ir/kshook-sv3c parsers and converters.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import { FindSDVXChartOnInGameIDVersion } from "utils/queries/charts";
|
||||
import { EmptyObject } from "utils/types";
|
||||
import { ConverterFunction } from "../../common/types";
|
||||
import { KsHookSV3CScore } from "./types";
|
||||
import { Lamps } from "tachi-common";
|
||||
import {
|
||||
InternalFailure,
|
||||
KTDataNotFoundFailure,
|
||||
} from "lib/score-import/framework/common/converter-failures";
|
||||
import { FindSongOnID } from "utils/queries/songs";
|
||||
import { GenericGetGradeAndPercent } from "lib/score-import/framework/common/score-utils";
|
||||
import { DryScore } from "lib/score-import/framework/common/types";
|
||||
|
||||
export const ConverterIRKShookSV3C: ConverterFunction<KsHookSV3CScore, EmptyObject> = async (
|
||||
data,
|
||||
context,
|
||||
importType,
|
||||
logger
|
||||
) => {
|
||||
const diff = ConvertDifficulty(data.difficulty);
|
||||
|
||||
const chart = await FindSDVXChartOnInGameIDVersion(data.music_id, diff, "konaste");
|
||||
|
||||
if (!chart) {
|
||||
throw new KTDataNotFoundFailure(
|
||||
`Could not find chart with songID ${data.music_id} (${diff} for Konaste).`,
|
||||
importType,
|
||||
data,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
const song = await FindSongOnID("sdvx", chart.songID);
|
||||
|
||||
if (!song) {
|
||||
logger.severe(`Song ${chart.songID} (sdvx) has no parent song?`);
|
||||
throw new InternalFailure(`Song ${chart.songID} (sdvx) has no parent song?`);
|
||||
}
|
||||
|
||||
const { percent, grade } = GenericGetGradeAndPercent("sdvx", data.score, chart);
|
||||
|
||||
const dryScore: DryScore<"sdvx:Single"> = {
|
||||
game: "sdvx",
|
||||
service: "kshook SV3C",
|
||||
comment: null,
|
||||
importType: "ir/kshook-sv3c",
|
||||
timeAchieved: Date.now(),
|
||||
scoreData: {
|
||||
score: data.score,
|
||||
percent,
|
||||
grade,
|
||||
lamp: ConvertLamp(data.clear),
|
||||
judgements: {
|
||||
critical: data.critical,
|
||||
near: data.near,
|
||||
miss: data.error,
|
||||
},
|
||||
hitMeta: {
|
||||
fast: data.early,
|
||||
slow: data.late,
|
||||
gauge: data.gauge,
|
||||
maxCombo: data.max_chain,
|
||||
},
|
||||
},
|
||||
scoreMeta: {},
|
||||
};
|
||||
|
||||
return { song, chart, dryScore };
|
||||
};
|
||||
|
||||
function ConvertLamp(clear: KsHookSV3CScore["clear"]): Lamps["sdvx:Single"] {
|
||||
if (clear === "CLEAR_PLAYED") {
|
||||
return "FAILED";
|
||||
} else if (clear === "CLEAR_EFFECTIVE") {
|
||||
return "CLEAR";
|
||||
} else if (clear === "CLEAR_EXCESSIVE") {
|
||||
return "EXCESSIVE CLEAR";
|
||||
} else if (clear === "CLEAR_ULTIMATE_CHAIN") {
|
||||
return "ULTIMATE CHAIN";
|
||||
}
|
||||
|
||||
return "PERFECT ULTIMATE CHAIN";
|
||||
}
|
||||
|
||||
function ConvertDifficulty(
|
||||
diff: KsHookSV3CScore["difficulty"]
|
||||
): "NOV" | "ADV" | "EXH" | "ANY_INF" | "MXM" {
|
||||
if (diff === "DIFFICULTY_NOVICE") {
|
||||
return "NOV";
|
||||
} else if (diff === "DIFFICULTY_ADVANCED") {
|
||||
return "ADV";
|
||||
} else if (diff === "DIFFICULTY_EXHAUST") {
|
||||
return "EXH";
|
||||
} else if (diff === "DIFFICULTY_INFINITE") {
|
||||
return "ANY_INF";
|
||||
}
|
||||
|
||||
return "MXM";
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { SDVXDans } from "lib/constants/classes";
|
||||
import { KtLogger } from "lib/logger/logger";
|
||||
import { InvalidScoreFailure } from "lib/score-import/framework/common/converter-failures";
|
||||
import p, { PrudenceSchema } from "prudence";
|
||||
import { FormatPrError } from "utils/prudence";
|
||||
import { EmptyObject } from "utils/types";
|
||||
import { ParserFunctionReturns } from "../../common/types";
|
||||
import { KsHookSV3CScore } from "./types";
|
||||
|
||||
const PR_KsHookSV3C: PrudenceSchema = {
|
||||
appeal_id: p.isInteger,
|
||||
clear: p.isIn(
|
||||
"CLEAR_PLAYED",
|
||||
"CLEAR_EFFECTIVE",
|
||||
"CLEAR_EXCESSIVE",
|
||||
"CLEAR_ULTIMATE_CHAIN",
|
||||
"CLEAR_PERFECT"
|
||||
),
|
||||
difficulty: p.isIn(
|
||||
"DIFFICULTY_NOVICE",
|
||||
"DIFFICULTY_ADVANCED",
|
||||
"DIFFICULTY_EXHAUST",
|
||||
"DIFFICULTY_INFINITE",
|
||||
"DIFFICULTY_MAXIMUM"
|
||||
),
|
||||
early: p.isPositiveInteger,
|
||||
late: p.isPositiveInteger,
|
||||
gauge: p.isBetween(0, 100),
|
||||
grade: p.isIn(
|
||||
"GRADE_D",
|
||||
"GRADE_C",
|
||||
"GRADE_B",
|
||||
"GRADE_A",
|
||||
"GRADE_A_PLUS",
|
||||
"GRADE_AA",
|
||||
"GRADE_AA_PLUS",
|
||||
"GRADE_AAA",
|
||||
"GRADE_AAA_PLUS",
|
||||
"GRADE_S"
|
||||
),
|
||||
max_chain: p.isPositiveInteger,
|
||||
music_id: p.isPositiveInteger,
|
||||
|
||||
btn_rate: p.isBetween(0, 200),
|
||||
long_rate: p.isBetween(0, 200),
|
||||
vol_rate: p.isBetween(0, 200),
|
||||
|
||||
player_name: "string",
|
||||
rate: p.isIn("RATE_EFFECTIVE", "RATE_PERMISSIVE", "RATE_EXCESSIVE", "RATE_BLASTIVE"),
|
||||
|
||||
skill_frame: p.isIn(
|
||||
"SKILL_FRAME_NONE",
|
||||
"SKILL_FRAME_SILVER",
|
||||
"SKILL_FRAME_GOLD",
|
||||
"SKILL_FRAME_GOLD_HALO"
|
||||
),
|
||||
skill_level: p.isIn(
|
||||
"SKILL_LEVEL_NONE",
|
||||
"SKILL_LEVEL_01",
|
||||
"SKILL_LEVEL_02",
|
||||
"SKILL_LEVEL_03",
|
||||
"SKILL_LEVEL_04",
|
||||
"SKILL_LEVEL_05",
|
||||
"SKILL_LEVEL_06",
|
||||
"SKILL_LEVEL_07",
|
||||
"SKILL_LEVEL_08",
|
||||
"SKILL_LEVEL_09",
|
||||
"SKILL_LEVEL_10",
|
||||
"SKILL_LEVEL_11",
|
||||
"SKILL_LEVEL_12"
|
||||
),
|
||||
track_no: p.isPositiveInteger,
|
||||
};
|
||||
|
||||
export function ParseKsHookSV3C(
|
||||
body: Record<string, unknown>,
|
||||
logger: KtLogger
|
||||
): ParserFunctionReturns<KsHookSV3CScore, EmptyObject> {
|
||||
// Ignore excess keys, as SV3C might add more features in the future.
|
||||
const err = p(body, PR_KsHookSV3C, undefined, { allowExcessKeys: true });
|
||||
|
||||
if (err) {
|
||||
throw new InvalidScoreFailure(FormatPrError(err));
|
||||
}
|
||||
|
||||
const score = body as unknown as KsHookSV3CScore;
|
||||
|
||||
return {
|
||||
game: "sdvx",
|
||||
iterable: [score],
|
||||
context: {},
|
||||
classHandler: () => {
|
||||
const skillLevel = ConvertSkillLevel(score.skill_level);
|
||||
|
||||
// If null, the player doesn't have a dan.
|
||||
if (!skillLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
dan: skillLevel,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function ConvertSkillLevel(skill: KsHookSV3CScore["skill_level"]): SDVXDans | null {
|
||||
switch (skill) {
|
||||
case "SKILL_LEVEL_01":
|
||||
return SDVXDans.DAN_1;
|
||||
case "SKILL_LEVEL_02":
|
||||
return SDVXDans.DAN_2;
|
||||
case "SKILL_LEVEL_03":
|
||||
return SDVXDans.DAN_3;
|
||||
case "SKILL_LEVEL_04":
|
||||
return SDVXDans.DAN_4;
|
||||
case "SKILL_LEVEL_05":
|
||||
return SDVXDans.DAN_5;
|
||||
case "SKILL_LEVEL_06":
|
||||
return SDVXDans.DAN_6;
|
||||
case "SKILL_LEVEL_07":
|
||||
return SDVXDans.DAN_7;
|
||||
case "SKILL_LEVEL_08":
|
||||
return SDVXDans.DAN_8;
|
||||
case "SKILL_LEVEL_09":
|
||||
return SDVXDans.DAN_9;
|
||||
case "SKILL_LEVEL_10":
|
||||
return SDVXDans.DAN_10;
|
||||
case "SKILL_LEVEL_11":
|
||||
return SDVXDans.DAN_11;
|
||||
case "SKILL_LEVEL_12":
|
||||
return SDVXDans.INF;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { integer } from "tachi-common";
|
||||
|
||||
export interface KsHookSV3CScore {
|
||||
appeal_id: integer;
|
||||
clear:
|
||||
| "CLEAR_PLAYED"
|
||||
| "CLEAR_EFFECTIVE"
|
||||
| "CLEAR_EXCESSIVE"
|
||||
| "CLEAR_ULTIMATE_CHAIN"
|
||||
| "CLEAR_PERFECT";
|
||||
difficulty: `DIFFICULTY_${"NOVICE" | "ADVANCED" | "EXHAUST" | "INFINITE" | "MAXIMUM"}`;
|
||||
early: integer;
|
||||
gauge: integer;
|
||||
grade: `GRADE_${
|
||||
| "D"
|
||||
| "C"
|
||||
| "B"
|
||||
| "A"
|
||||
| "A_PLUS"
|
||||
| "AA"
|
||||
| "AA_PLUS"
|
||||
| "AAA"
|
||||
| "AAA_PLUS"
|
||||
| "S"}`;
|
||||
late: integer;
|
||||
max_chain: integer;
|
||||
music_id: integer;
|
||||
|
||||
btn_rate: integer;
|
||||
long_rate: integer;
|
||||
vol_rate: integer;
|
||||
|
||||
critical: integer;
|
||||
near: integer;
|
||||
error: integer;
|
||||
|
||||
player_name: string;
|
||||
rate: `RATE_${"EFFECTIVE" | "PERMISSIVE" | "EXCESSIVE" | "BLASTIVE"}`;
|
||||
score: integer;
|
||||
|
||||
skill_frame: `SKILL_FRAME_${"NONE" | "SILVER" | "GOLD" | "GOLD_HALO"}`;
|
||||
skill_level: `SKILL_LEVEL_${
|
||||
| "NONE"
|
||||
| "01"
|
||||
| "02"
|
||||
| "03"
|
||||
| "04"
|
||||
| "05"
|
||||
| "06"
|
||||
| "07"
|
||||
| "08"
|
||||
| "09"
|
||||
| "10"
|
||||
| "11"
|
||||
| "12"}`;
|
||||
skill_name: integer;
|
||||
track_no: integer;
|
||||
}
|
||||
Reference in New Issue
Block a user