210 lines
4.2 KiB
TypeScript
210 lines
4.2 KiB
TypeScript
"use server";
|
|
import { getAuth } from "@/auth/queries/getauth";
|
|
import { getSupportedVersionNumber } from "@/lib/api";
|
|
import { artemis } from "@/lib/prisma";
|
|
import { fromZonedTime, toZonedTime } from "date-fns-tz";
|
|
import { parse } from "date-fns";
|
|
|
|
const TACHI_CLASSES = [
|
|
undefined,
|
|
"DAN_I",
|
|
"DAN_II",
|
|
"DAN_III",
|
|
"DAN_IV",
|
|
"DAN_V",
|
|
"DAN_INFINITE",
|
|
] as const;
|
|
const TACHI_DIFFICULTIES = [
|
|
"BASIC",
|
|
"ADVANCED",
|
|
"EXPERT",
|
|
"MASTER",
|
|
"ULTIMA",
|
|
] as const;
|
|
|
|
type BatchManualLamp =
|
|
| "ALL JUSTICE CRITICAL"
|
|
| "ALL JUSTICE"
|
|
| "FULL COMBO"
|
|
| "CLEAR"
|
|
| "FAILED";
|
|
interface BatchManualScore {
|
|
identifier: string;
|
|
matchType: "inGameID";
|
|
score: number;
|
|
lamp: BatchManualLamp;
|
|
difficulty: "BASIC" | "ADVANCED" | "EXPERT" | "MASTER" | "ULTIMA";
|
|
timeAchieved?: number;
|
|
judgements?: {
|
|
jcrit: number;
|
|
justice: number;
|
|
attack: number;
|
|
miss: number;
|
|
};
|
|
optional?: {
|
|
maxCombo: number;
|
|
};
|
|
}
|
|
interface BatchManualImport {
|
|
meta: {
|
|
game: string;
|
|
playtype: string;
|
|
service: string;
|
|
};
|
|
scores: BatchManualScore[];
|
|
classes?: {
|
|
dan?: string;
|
|
emblem?: string;
|
|
};
|
|
}
|
|
|
|
export async function getTachiExport() {
|
|
const { user } = await getAuth();
|
|
const version = await getSupportedVersionNumber();
|
|
|
|
if (!user || !user.accessCode) {
|
|
throw new Error("User is not authenticated or accessCode is missing");
|
|
}
|
|
|
|
const profile = await artemis.chuni_profile_data.findFirst({
|
|
where: {
|
|
user: user.UserId,
|
|
version,
|
|
},
|
|
select: {
|
|
classEmblemBase: true,
|
|
classEmblemMedal: true,
|
|
},
|
|
});
|
|
const playlog = await artemis.chuni_score_playlog.findMany({
|
|
where: {
|
|
user: user.UserId,
|
|
},
|
|
select: {
|
|
romVersion: true,
|
|
userPlayDate: true,
|
|
musicId: true,
|
|
level: true,
|
|
score: true,
|
|
maxCombo: true,
|
|
judgeGuilty: true,
|
|
judgeAttack: true,
|
|
judgeJustice: true,
|
|
judgeCritical: true,
|
|
judgeHeaven: true,
|
|
isFullCombo: true,
|
|
isAllJustice: true,
|
|
isClear: true,
|
|
},
|
|
});
|
|
|
|
const tachiExport: BatchManualImport = {
|
|
meta: {
|
|
game: "chunithm",
|
|
playtype: "Single",
|
|
service: "Cozynet",
|
|
},
|
|
scores: [],
|
|
classes: {
|
|
dan: TACHI_CLASSES[profile?.classEmblemBase ?? 0],
|
|
emblem: TACHI_CLASSES[profile?.classEmblemMedal ?? 0],
|
|
},
|
|
};
|
|
|
|
for (const log of playlog) {
|
|
const {
|
|
romVersion,
|
|
userPlayDate,
|
|
musicId,
|
|
level,
|
|
score,
|
|
judgeHeaven,
|
|
judgeCritical,
|
|
judgeJustice,
|
|
judgeAttack,
|
|
judgeGuilty,
|
|
maxCombo,
|
|
isAllJustice,
|
|
isFullCombo,
|
|
isClear,
|
|
} = log;
|
|
|
|
if (
|
|
romVersion === null ||
|
|
musicId === null ||
|
|
level === null ||
|
|
score === null ||
|
|
judgeJustice === null ||
|
|
isAllJustice === null ||
|
|
isFullCombo === null ||
|
|
isClear === null
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
// Filter out WORLD'S END scores
|
|
if (romVersion.startsWith("1.") && level === 4) {
|
|
continue;
|
|
}
|
|
|
|
if (romVersion.startsWith("2.") && level === 5) {
|
|
continue;
|
|
}
|
|
|
|
let lamp: BatchManualLamp = "FAILED";
|
|
|
|
if (isAllJustice && judgeJustice === 0) {
|
|
lamp = "ALL JUSTICE CRITICAL";
|
|
} else if (isAllJustice) {
|
|
lamp = "ALL JUSTICE";
|
|
} else if (isFullCombo) {
|
|
lamp = "FULL COMBO";
|
|
} else if (isClear) {
|
|
lamp = "CLEAR";
|
|
}
|
|
|
|
const tachiScore: BatchManualScore = {
|
|
score,
|
|
lamp,
|
|
identifier: musicId.toString(),
|
|
matchType: "inGameID",
|
|
difficulty: TACHI_DIFFICULTIES[level],
|
|
};
|
|
|
|
if (userPlayDate !== null) {
|
|
tachiScore.timeAchieved = fromZonedTime(
|
|
parse(
|
|
userPlayDate,
|
|
"yyyy-MM-dd HH:mm:ss",
|
|
toZonedTime(new Date(), "Asia/Tokyo"),
|
|
),
|
|
"Asia/Tokyo",
|
|
).valueOf();
|
|
}
|
|
|
|
if (
|
|
judgeCritical !== null &&
|
|
judgeJustice !== null &&
|
|
judgeAttack !== null &&
|
|
judgeGuilty !== null
|
|
) {
|
|
tachiScore.judgements = {
|
|
jcrit: (judgeHeaven ?? 0) + judgeCritical,
|
|
justice: judgeJustice,
|
|
attack: judgeAttack,
|
|
miss: judgeGuilty,
|
|
};
|
|
}
|
|
|
|
if (maxCombo !== null) {
|
|
tachiScore.optional = {
|
|
maxCombo,
|
|
};
|
|
}
|
|
|
|
tachiExport.scores.push(tachiScore);
|
|
}
|
|
|
|
return tachiExport;
|
|
}
|