mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-04 12:58:11 +03:00
Rough baseline for pop'n music support.
This commit is contained in:
+1
-1
@@ -82,7 +82,7 @@
|
||||
"rimraf": "3.0.2",
|
||||
"safe-json-stringify": "1.2.0",
|
||||
"seq-logging": "^1.1.1",
|
||||
"tachi-common": "0.4.4",
|
||||
"tachi-common": "0.4.7",
|
||||
"ts-node": "10.0.0",
|
||||
"tsconfig-paths": "3.10.1",
|
||||
"typescript": "4.3.4",
|
||||
|
||||
Generated
+4
-4
@@ -63,7 +63,7 @@ specifiers:
|
||||
safe-json-stringify: 1.2.0
|
||||
seq-logging: ^1.1.1
|
||||
supertest: 6.1.3
|
||||
tachi-common: 0.4.4
|
||||
tachi-common: 0.4.7
|
||||
tap: 15.0.9
|
||||
ts-node: 10.0.0
|
||||
tsconfig-paths: 3.10.1
|
||||
@@ -107,7 +107,7 @@ dependencies:
|
||||
rimraf: 3.0.2
|
||||
safe-json-stringify: 1.2.0
|
||||
seq-logging: 1.1.1
|
||||
tachi-common: 0.4.4
|
||||
tachi-common: 0.4.7
|
||||
ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b
|
||||
tsconfig-paths: 3.10.1
|
||||
typescript: 4.3.4
|
||||
@@ -5386,8 +5386,8 @@ packages:
|
||||
strip-ansi: 6.0.0
|
||||
dev: true
|
||||
|
||||
/tachi-common/0.4.4:
|
||||
resolution: {integrity: sha512-vXLMkMUCEE5ja57HYSLqM7Mhe+oj416JPIKGMdz5ctrwjf7BX4eU8rEAYoBbibvl57SOFLylT6Bm1icyJuQC0w==}
|
||||
/tachi-common/0.4.7:
|
||||
resolution: {integrity: sha512-tlntA6tOJSG7l2GNx7byktIma/2upSr+s1tTS1c/t+6IUCED9scsCTwDcqaRSkjzz+fvx3SMg79jyyKIu0Wt9Q==}
|
||||
dependencies:
|
||||
monk: 7.3.4
|
||||
transitivePeerDependencies:
|
||||
|
||||
Vendored
-2
@@ -15,7 +15,6 @@ import {
|
||||
FolderDocument,
|
||||
Game,
|
||||
GoalDocument,
|
||||
IIDXBPIData,
|
||||
ImportDocument,
|
||||
ImportTimingsDocument,
|
||||
integer,
|
||||
@@ -157,7 +156,6 @@ const db = {
|
||||
imports: monkDB.get<ImportDocument>("imports"),
|
||||
"import-timings": monkDB.get<ImportTimingsDocument>("import-timings"),
|
||||
sessions: monkDB.get<SessionDocument>("sessions"),
|
||||
"iidx-bpi-data": monkDB.get<IIDXBPIData>("iidx-bpi-data"),
|
||||
invites: monkDB.get<InviteCodeDocument>("invites"),
|
||||
counters: monkDB.get<CounterDocument>("counters"),
|
||||
"game-stats": monkDB.get<UserGameStats>("game-stats"),
|
||||
|
||||
Vendored
+12
-1
@@ -773,6 +773,12 @@ export const DatabaseSchemas: Record<Databases, ValidatorFunction> = {
|
||||
displayVersion: "?string",
|
||||
})
|
||||
),
|
||||
"songs-popn": prSchemaify(
|
||||
PR_SongDocument({
|
||||
genre: "string",
|
||||
displayVersion: "?string",
|
||||
})
|
||||
),
|
||||
"charts-iidx": (self) => {
|
||||
const playtype = getPlaytype("iidx", self);
|
||||
|
||||
@@ -857,7 +863,12 @@ export const DatabaseSchemas: Record<Databases, ValidatorFunction> = {
|
||||
},
|
||||
"charts-wacca": prSchemaify(
|
||||
PR_ChartDocument("wacca", "Single", {
|
||||
releaseVersion: p.isIn("wacca", "s", "lily", "lily-r", "reverse"),
|
||||
isHot: "boolean",
|
||||
})
|
||||
),
|
||||
"charts-popn": prSchemaify(
|
||||
PR_ChartDocument("popn", "9B", {
|
||||
hashSHA256: "?string",
|
||||
})
|
||||
),
|
||||
goals: prSchemaify({
|
||||
|
||||
@@ -184,3 +184,14 @@ export enum WACCA_COLOURS {
|
||||
GOLD,
|
||||
RAINBOW,
|
||||
}
|
||||
|
||||
export enum POPN_CLASSES {
|
||||
KITTY,
|
||||
GRADE_SCHOOL,
|
||||
DELINQUENT,
|
||||
DETECTIVE,
|
||||
IDOL,
|
||||
GENERAL,
|
||||
HERMIT,
|
||||
GOD,
|
||||
}
|
||||
|
||||
@@ -45,57 +45,30 @@ export async function CreateCalculatedData(
|
||||
}
|
||||
|
||||
type CalculatedDataFunctions = {
|
||||
[G in Game]: {
|
||||
[P in Playtypes[G]]: (
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
logger: KtLogger
|
||||
) => Promise<ScoreDocument["calculatedData"]> | ScoreDocument["calculatedData"];
|
||||
};
|
||||
[I in IDStrings]: (
|
||||
dryScore: DryScore<I>,
|
||||
chart: ChartDocument<I>,
|
||||
logger: KtLogger
|
||||
) => Promise<ScoreDocument<I>["calculatedData"]> | ScoreDocument<I>["calculatedData"];
|
||||
};
|
||||
|
||||
const CalculatedDataFunctions: CalculatedDataFunctions = {
|
||||
iidx: {
|
||||
SP: CalculateDataIIDXSP,
|
||||
DP: CalculateDataIIDXDP,
|
||||
},
|
||||
sdvx: {
|
||||
Single: CalculateDataSDVXorUSC,
|
||||
},
|
||||
// popn: {
|
||||
// "9B": () => ({}),
|
||||
// },
|
||||
museca: {
|
||||
Single: CalculateDataMuseca,
|
||||
},
|
||||
chunithm: {
|
||||
Single: CalculateDataCHUNITHM,
|
||||
},
|
||||
maimai: {
|
||||
Single: CalculateDataMaimai,
|
||||
},
|
||||
gitadora: {
|
||||
Gita: CalculateDataGitadora,
|
||||
Dora: CalculateDataGitadora,
|
||||
},
|
||||
bms: {
|
||||
"7K": CalculateDataBMS7K,
|
||||
"14K": CalculateDataBMS14K,
|
||||
},
|
||||
ddr: {
|
||||
SP: CalculateDataDDR,
|
||||
DP: CalculateDataDDR,
|
||||
},
|
||||
// jubeat: {
|
||||
// Single: CalculateDataJubeat,
|
||||
// },
|
||||
usc: {
|
||||
Controller: CalculateDataSDVXorUSC,
|
||||
Keyboard: CalculateDataSDVXorUSC,
|
||||
},
|
||||
wacca: {
|
||||
Single: CalculateDataWACCA,
|
||||
},
|
||||
"iidx:SP": CalculateDataIIDXSP,
|
||||
"iidx:DP": CalculateDataIIDXDP,
|
||||
"sdvx:Single": CalculateDataSDVXorUSC,
|
||||
"popn:9B": CalculateDataPopn,
|
||||
"museca:Single": CalculateDataMuseca,
|
||||
"chunithm:Single": CalculateDataCHUNITHM,
|
||||
"maimai:Single": CalculateDataMaimai,
|
||||
"gitadora:Gita": CalculateDataGitadora,
|
||||
"gitadora:Dora": CalculateDataGitadora,
|
||||
"bms:7K": CalculateDataBMS7K,
|
||||
"bms:14K": CalculateDataBMS14K,
|
||||
"ddr:SP": CalculateDataDDR,
|
||||
"ddr:DP": CalculateDataDDR,
|
||||
"usc:Controller": CalculateDataSDVXorUSC,
|
||||
"usc:Keyboard": CalculateDataSDVXorUSC,
|
||||
"wacca:Single": CalculateDataWACCA,
|
||||
};
|
||||
|
||||
// Creates Game-Specific calculatedData for the provided game & playtype.
|
||||
@@ -110,39 +83,26 @@ export async function CalculateDataForGamePT<G extends Game>(
|
||||
esd: number | null,
|
||||
logger: KtLogger
|
||||
): Promise<ScoreDocument["calculatedData"]> {
|
||||
const GameRatingFns = CalculatedDataFunctions[game];
|
||||
|
||||
if (!HasOwnProperty(GameRatingFns, playtype)) {
|
||||
logger.error(
|
||||
`Invalid playtype of ${playtype} given for game ${game} in CalculateDataForGamePT, returning an empty object.`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
// @ts-expect-error standard game->pt stuff.
|
||||
return GameRatingFns[playtype](dryScore, chart, logger);
|
||||
// @ts-expect-error too many minor complains here...
|
||||
return CalculatedDataFunctions[`${game}:${playtype}` as IDStrings](dryScore, chart, logger);
|
||||
}
|
||||
|
||||
type CalculatedData<I extends IDStrings> = Required<ScoreDocument<I>["calculatedData"]>;
|
||||
|
||||
async function CalculateDataIIDXSP(
|
||||
function CalculateDataIIDXSP(
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
chart: ChartDocument<"iidx:SP">,
|
||||
logger: KtLogger
|
||||
): Promise<CalculatedData<"iidx:SP">> {
|
||||
const BPIData = await db["iidx-bpi-data"].findOne({
|
||||
chartID: chart.chartID,
|
||||
});
|
||||
|
||||
): CalculatedData<"iidx:SP"> {
|
||||
let bpi;
|
||||
|
||||
if (BPIData) {
|
||||
if (chart.data.kaidenAverage && chart.data.worldRecord) {
|
||||
bpi = CalculateBPI(
|
||||
BPIData.kavg,
|
||||
BPIData.wr,
|
||||
chart.data.kaidenAverage,
|
||||
chart.data.worldRecord,
|
||||
dryScore.scoreData.score,
|
||||
(chart as ChartDocument<"iidx:SP">).data.notecount * 2,
|
||||
BPIData.coef
|
||||
chart.data.bpiCoefficient
|
||||
);
|
||||
|
||||
// kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled
|
||||
@@ -156,24 +116,20 @@ async function CalculateDataIIDXSP(
|
||||
};
|
||||
}
|
||||
|
||||
async function CalculateDataIIDXDP(
|
||||
function CalculateDataIIDXDP(
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
chart: ChartDocument<"iidx:SP" | "iidx:DP">,
|
||||
logger: KtLogger
|
||||
): Promise<CalculatedData<"iidx:DP">> {
|
||||
const BPIData = await db["iidx-bpi-data"].findOne({
|
||||
chartID: chart.chartID,
|
||||
});
|
||||
|
||||
): CalculatedData<"iidx:DP"> {
|
||||
let bpi;
|
||||
|
||||
if (BPIData) {
|
||||
if (chart.data.kaidenAverage && chart.data.worldRecord) {
|
||||
bpi = CalculateBPI(
|
||||
BPIData.kavg,
|
||||
BPIData.wr,
|
||||
chart.data.kaidenAverage,
|
||||
chart.data.worldRecord,
|
||||
dryScore.scoreData.score,
|
||||
(chart as ChartDocument<"iidx:DP">).data.notecount * 2,
|
||||
BPIData.coef
|
||||
chart.data.bpiCoefficient
|
||||
);
|
||||
|
||||
// kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled
|
||||
@@ -213,11 +169,11 @@ function CalculateDataSDVXorUSC(
|
||||
};
|
||||
}
|
||||
|
||||
async function CalculateDataMuseca(
|
||||
function CalculateDataMuseca(
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
logger: KtLogger
|
||||
): Promise<CalculatedData<"museca:Single">> {
|
||||
): CalculatedData<"museca:Single"> {
|
||||
return {
|
||||
ktRating: CalculateKTRating(dryScore, "museca", "Single", chart, logger),
|
||||
};
|
||||
@@ -232,11 +188,11 @@ function CalculateDataCHUNITHM(
|
||||
};
|
||||
}
|
||||
|
||||
async function CalculateDataMaimai(
|
||||
function CalculateDataMaimai(
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
logger: KtLogger
|
||||
): Promise<CalculatedData<"maimai:Single">> {
|
||||
): CalculatedData<"maimai:Single"> {
|
||||
// @todo #373 Add maimai rating algorithms.
|
||||
return {
|
||||
ktRating: 0,
|
||||
@@ -360,3 +316,28 @@ function CalculateDataWACCA(
|
||||
// How discrete. How boring!
|
||||
return { rate: scoreCoef * chart.levelNum };
|
||||
}
|
||||
|
||||
function CalculateDataPopn(
|
||||
dryScore: DryScore,
|
||||
chart: ChartDocument,
|
||||
logger: KtLogger
|
||||
): CalculatedData<"popn:9B"> {
|
||||
const score = dryScore.scoreData.score;
|
||||
const lamp = dryScore.scoreData.lamp;
|
||||
|
||||
if (score <= 50000) {
|
||||
return { classPoints: 0 };
|
||||
}
|
||||
|
||||
let clearBonus = 0;
|
||||
|
||||
if (lamp === "CLEAR" || lamp === "EASY CLEAR") {
|
||||
clearBonus = 3000;
|
||||
} else if (lamp === "FULL COMBO" || lamp === "PERFECT") {
|
||||
clearBonus = 5000;
|
||||
}
|
||||
|
||||
return {
|
||||
classPoints: (10_000 * chart.levelNum + score - 50_000 + clearBonus) / 5440,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,9 +62,11 @@ const CalculatedDataFunctions: CalculatedDataFunctions = {
|
||||
};
|
||||
},
|
||||
},
|
||||
// popn: {
|
||||
// "9B": () => ({}),
|
||||
// },
|
||||
popn: {
|
||||
"9B": (scd) => ({
|
||||
classPoints: AvgBest10Map(scd, "classPoints"),
|
||||
}),
|
||||
},
|
||||
museca: {
|
||||
Single: (scd) => ({
|
||||
ktRating: AvgBest10Map(scd, "ktRating"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CHUNITHM_COLOURS,
|
||||
GitadoraColours,
|
||||
POPN_CLASSES,
|
||||
SDVXVFClasses,
|
||||
WACCA_COLOURS,
|
||||
} from "lib/constants/classes";
|
||||
@@ -132,6 +133,37 @@ function WACCARateToColour(rate: number) {
|
||||
return WACCA_COLOURS.ASH;
|
||||
}
|
||||
|
||||
export function CalculatePopnClass(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
userID: integer,
|
||||
ratings: Record<string, number>
|
||||
) {
|
||||
const cls = PopnClassPointsToClass(ratings.naiveClassPoints);
|
||||
|
||||
return { class: cls };
|
||||
}
|
||||
|
||||
function PopnClassPointsToClass(points: number) {
|
||||
if (points < 21) {
|
||||
return POPN_CLASSES.KITTY;
|
||||
} else if (points < 34) {
|
||||
return POPN_CLASSES.GRADE_SCHOOL;
|
||||
} else if (points < 46) {
|
||||
return POPN_CLASSES.DELINQUENT;
|
||||
} else if (points < 59) {
|
||||
return POPN_CLASSES.DETECTIVE;
|
||||
} else if (points < 68) {
|
||||
return POPN_CLASSES.IDOL;
|
||||
} else if (points < 79) {
|
||||
return POPN_CLASSES.GENERAL;
|
||||
} else if (points < 91) {
|
||||
return POPN_CLASSES.HERMIT;
|
||||
}
|
||||
|
||||
return POPN_CLASSES.GOD;
|
||||
}
|
||||
|
||||
export function CalculateChunithmColour(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ReturnClassIfGreater } from "utils/class";
|
||||
import {
|
||||
CalculateChunithmColour,
|
||||
CalculateGitadoraColour,
|
||||
CalculatePopnClass,
|
||||
CalculateSDVXClass,
|
||||
CalculateWACCAColour,
|
||||
} from "./builtin-class-handlers";
|
||||
@@ -46,7 +47,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = {
|
||||
// },
|
||||
maimai: null,
|
||||
museca: null,
|
||||
// popn: null,
|
||||
popn: {
|
||||
"9B": CalculatePopnClass,
|
||||
},
|
||||
sdvx: {
|
||||
Single: CalculateSDVXClass,
|
||||
},
|
||||
|
||||
@@ -119,9 +119,11 @@ const RatingFunctions: RatingFunctions = {
|
||||
naiveRating: await LazyMeanN("rating", 20)(g, p, u),
|
||||
}),
|
||||
},
|
||||
// popn: {
|
||||
// "9B": async () => ({}),
|
||||
// },
|
||||
popn: {
|
||||
"9B": async (g, p, u) => ({
|
||||
naiveClassPoints: await LazyMeanN("classPoints", 20)(g, p, u),
|
||||
}),
|
||||
},
|
||||
museca: {
|
||||
Single: async (g, p, u) => ({
|
||||
ktRating: await LazyMeanN("ktRating", 20)(g, p, u),
|
||||
|
||||
Reference in New Issue
Block a user