mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-05 05:18:12 +03:00
fix(api-cg/jb): use clearFlag bitfield to generate Lamp (#1276)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import { ConverterAPICGJubeat } from "./converter";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { ParseDateFromString } from "lib/score-import/framework/common/score-utils";
|
||||
@@ -8,6 +9,11 @@ import type { DryScore } from "lib/score-import/framework/common/types";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const BIT_FAILED = 1 << 0;
|
||||
const BIT_CLEAR = 1 << 1;
|
||||
const BIT_FULL_COMBO = 1 << 2;
|
||||
const BIT_EXCELLENT = 1 << 3;
|
||||
|
||||
function mkInput(modifant: Partial<CGJubeatScore> = {}) {
|
||||
const validInput: CGJubeatScore = {
|
||||
internalId: 10000001,
|
||||
@@ -22,7 +28,7 @@ function mkInput(modifant: Partial<CGJubeatScore> = {}) {
|
||||
poorCount: 0,
|
||||
missCount: 0,
|
||||
musicRate: 952,
|
||||
clearFlag: 0, // unknown, unused
|
||||
clearFlag: BIT_FULL_COMBO | BIT_CLEAR | BIT_FAILED,
|
||||
};
|
||||
|
||||
return dmf(validInput, modifant);
|
||||
@@ -34,9 +40,6 @@ function mkOutput(modifant: any = {}): DryScore<"jubeat:Single"> {
|
||||
comment: null,
|
||||
game: "jubeat",
|
||||
importType: "api/cg-dev-jubeat",
|
||||
|
||||
// we handle it like this because -- with no timezone info
|
||||
// this will fail in CI; it has a different timezone there!
|
||||
timeAchieved: ParseDateFromString("2019-06-06 08:14:22"),
|
||||
service: "CG Dev",
|
||||
scoreData: {
|
||||
@@ -47,6 +50,7 @@ function mkOutput(modifant: any = {}): DryScore<"jubeat:Single"> {
|
||||
great: 50,
|
||||
good: 25,
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
musicRate: 95.2,
|
||||
optional: {},
|
||||
@@ -62,7 +66,6 @@ t.test("#ConverterAPICGJubeat", (t) => {
|
||||
service: "dev",
|
||||
userID: 1,
|
||||
};
|
||||
|
||||
const convert = (modifant: Partial<CGJubeatScore> = {}) =>
|
||||
ConverterAPICGJubeat(mkInput(modifant), context, "api/cg-dev-jubeat", logger);
|
||||
|
||||
@@ -85,81 +88,181 @@ t.test("#ConverterAPICGJubeat", (t) => {
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Lamps", async (t) => {
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
score: 690_000,
|
||||
missCount: 1, // at least a miss otherwise it's an FC
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "FAILED",
|
||||
score: 690_000,
|
||||
judgements: {
|
||||
miss: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
score: 700_000,
|
||||
missCount: 1,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "CLEAR",
|
||||
score: 700_000,
|
||||
judgements: {
|
||||
miss: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
missCount: 0,
|
||||
poorCount: 0,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "FULL COMBO",
|
||||
judgements: {
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
missCount: 0,
|
||||
poorCount: 0,
|
||||
goodCount: 0,
|
||||
t.test("Lamp Interpretation from Bitfield", (t) => {
|
||||
t.test("should interpret EXCELLENT bitfield correctly", async (t) => {
|
||||
const clearFlag = BIT_EXCELLENT | BIT_FULL_COMBO | BIT_CLEAR | BIT_FAILED;
|
||||
const score = 1_000_000;
|
||||
const judgements = {
|
||||
perfectCount: 200,
|
||||
greatCount: 0,
|
||||
score: 1_000_000,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "EXCELLENT",
|
||||
score: 1_000_000,
|
||||
judgements: {
|
||||
great: 0,
|
||||
good: 0,
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
},
|
||||
goodCount: 0,
|
||||
poorCount: 0,
|
||||
missCount: 0,
|
||||
};
|
||||
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
clearFlag,
|
||||
score,
|
||||
...judgements,
|
||||
}),
|
||||
}
|
||||
);
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "EXCELLENT",
|
||||
score,
|
||||
judgements: {
|
||||
perfect: 200,
|
||||
great: 0,
|
||||
good: 0,
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
t.test("should interpret FULL COMBO bitfield correctly", async (t) => {
|
||||
const clearFlag = BIT_FULL_COMBO | BIT_CLEAR | BIT_FAILED;
|
||||
const judgements = {
|
||||
perfectCount: 100,
|
||||
greatCount: 50,
|
||||
goodCount: 25,
|
||||
poorCount: 0,
|
||||
missCount: 0,
|
||||
};
|
||||
const score = 947_184;
|
||||
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
clearFlag,
|
||||
score,
|
||||
...judgements,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "FULL COMBO",
|
||||
score,
|
||||
judgements: {
|
||||
perfect: 100,
|
||||
great: 50,
|
||||
good: 25,
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
t.test("should interpret CLEAR bitfield correctly", async (t) => {
|
||||
const clearFlag = BIT_CLEAR | BIT_FAILED;
|
||||
const score = 750_000;
|
||||
const judgements = {
|
||||
perfectCount: 90,
|
||||
greatCount: 40,
|
||||
goodCount: 20,
|
||||
poorCount: 5,
|
||||
missCount: 1,
|
||||
};
|
||||
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
clearFlag,
|
||||
score,
|
||||
...judgements,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "CLEAR",
|
||||
score,
|
||||
judgements: {
|
||||
perfect: 90,
|
||||
great: 40,
|
||||
good: 20,
|
||||
poor: 5,
|
||||
miss: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
t.test("should interpret FAILED bitfield correctly (standard)", async (t) => {
|
||||
const clearFlag = BIT_FAILED;
|
||||
const score = 650_000;
|
||||
const judgements = {
|
||||
perfectCount: 80,
|
||||
greatCount: 30,
|
||||
goodCount: 15,
|
||||
poorCount: 10,
|
||||
missCount: 5,
|
||||
};
|
||||
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
clearFlag,
|
||||
score,
|
||||
...judgements,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "FAILED",
|
||||
score,
|
||||
judgements: {
|
||||
perfect: 80,
|
||||
great: 30,
|
||||
good: 15,
|
||||
poor: 10,
|
||||
miss: 5,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
t.test("should interpret FAILED bitfield (case of challenge fail)", async (t) => {
|
||||
const clearFlag = BIT_FAILED;
|
||||
const score = 920_000;
|
||||
const judgements = {
|
||||
perfectCount: 110,
|
||||
greatCount: 55,
|
||||
goodCount: 30,
|
||||
poorCount: 0,
|
||||
missCount: 0,
|
||||
};
|
||||
|
||||
t.hasStrict(
|
||||
await convert({
|
||||
clearFlag,
|
||||
score,
|
||||
...judgements,
|
||||
}),
|
||||
{
|
||||
dryScore: mkOutput({
|
||||
scoreData: {
|
||||
lamp: "FAILED",
|
||||
score,
|
||||
judgements: {
|
||||
perfect: 110,
|
||||
great: 55,
|
||||
good: 30,
|
||||
poor: 0,
|
||||
miss: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ export const ConverterAPICGJubeat: ConverterFunction<CGJubeatScore, CGContext> =
|
||||
poor: data.poorCount,
|
||||
miss: data.missCount,
|
||||
};
|
||||
const lamp = GuessLamp(judgements, data.score);
|
||||
const lamp = GetLamp(data.clearFlag);
|
||||
|
||||
const chart = await FindChartOnInGameID("jubeat", data.internalId, "Single", difficulty);
|
||||
|
||||
@@ -125,23 +125,23 @@ function ConvertMusicRate(rate: number): number {
|
||||
return rate / 10;
|
||||
}
|
||||
|
||||
function GuessLamp(judgments: Record<Judgements["jubeat:Single"], integer | null>, score: number) {
|
||||
if (
|
||||
judgments.good === 0 &&
|
||||
judgments.great === 0 &&
|
||||
judgments.miss === 0 &&
|
||||
judgments.poor === 0
|
||||
) {
|
||||
if (score !== 1_000_000) {
|
||||
throw new InvalidScoreFailure(`Score is an EXC, yet score !== 1_000_000.`);
|
||||
}
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
function GetLamp(clearFlag: integer) {
|
||||
if ((clearFlag & (1 << 3)) !== 0) {
|
||||
return "EXCELLENT";
|
||||
} else if (judgments.miss === 0 && judgments.poor === 0) {
|
||||
}
|
||||
|
||||
if ((clearFlag & (1 << 2)) !== 0) {
|
||||
return "FULL COMBO";
|
||||
} else if (score >= 700_000) {
|
||||
}
|
||||
|
||||
if ((clearFlag & (1 << 1)) !== 0) {
|
||||
return "CLEAR";
|
||||
}
|
||||
|
||||
return "FAILED";
|
||||
if ((clearFlag & (1 << 0)) !== 0) {
|
||||
return "FAILED";
|
||||
}
|
||||
|
||||
throw new InvalidScoreFailure("Failed to decode Lamp using bitfield clearFlag.");
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface CGJubeatScore {
|
||||
internalId: integer;
|
||||
difficulty: integer;
|
||||
version: integer;
|
||||
clearFlag: unknown; // Weird bitwise field, unused.
|
||||
clearFlag: integer; // Weird bitwise field, unused.
|
||||
|
||||
score: integer;
|
||||
hardMode: boolean;
|
||||
|
||||
Reference in New Issue
Block a user