mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-04 04:48:15 +03:00
checkpoint: insanity ascends (35)
127 errors to go
This commit is contained in:
+13
-1
@@ -13,7 +13,19 @@ import {
|
||||
import { GoalsOnChartReturn, GoalsOnFolderReturn } from "types/api-returns";
|
||||
|
||||
export function GetPBs(scoreInfo: SessionScoreInfo[]) {
|
||||
return scoreInfo.filter((e) => e.isNewScore === true || e.lampDelta > 0 || e.scoreDelta > 0);
|
||||
return scoreInfo.filter((e) => {
|
||||
if (e.isNewScore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const v of Object.values(e.deltas)) {
|
||||
if (v >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export function CreateSongMap<G extends Game = Game>(songs: SongDocument<G>[]) {
|
||||
|
||||
+18
-79
@@ -3,20 +3,16 @@ import toast from "react-hot-toast";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import {
|
||||
APIPermissions,
|
||||
AnyProfileRatingAlg,
|
||||
AnySessionRatingAlg,
|
||||
ChartDocument,
|
||||
Game,
|
||||
GamePTConfig,
|
||||
GetGamePTConfig,
|
||||
Grades,
|
||||
GPTString,
|
||||
Playtype,
|
||||
ProfileRatingLookup,
|
||||
QuestDocument,
|
||||
QuestSubscriptionDocument,
|
||||
ScoreCalculatedDataLookup,
|
||||
ScoreDocument,
|
||||
SessionCalculatedDataLookup,
|
||||
SessionDocument,
|
||||
integer,
|
||||
} from "tachi-common";
|
||||
|
||||
@@ -56,29 +52,10 @@ export function FormatTables(tables: { table: string; level: string }[]) {
|
||||
return tables.map((e) => `${e.table}${e.level}`).join(", ");
|
||||
}
|
||||
|
||||
export function FormatGPTRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
key: ScoreCalculatedDataLookup[GPTString],
|
||||
value: number | null
|
||||
) {
|
||||
if (value === null) {
|
||||
return "No Data.";
|
||||
}
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
if (gptConfig.scoreRatingAlgFormatters[key]) {
|
||||
return gptConfig.scoreRatingAlgFormatters[key]!(value);
|
||||
}
|
||||
|
||||
return value.toFixed(2);
|
||||
}
|
||||
|
||||
export function FormatGPTProfileRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
key: ProfileRatingLookup[GPTString],
|
||||
key: AnyProfileRatingAlg,
|
||||
value: number | null
|
||||
) {
|
||||
if (value === null) {
|
||||
@@ -87,8 +64,8 @@ export function FormatGPTProfileRating(
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
if (gptConfig.profileRatingAlgFormatters[key]) {
|
||||
return gptConfig.profileRatingAlgFormatters[key]!(value);
|
||||
if (gptConfig.profileRatingAlgs[key].formatter) {
|
||||
return gptConfig.profileRatingAlgs[key].formatter!(value);
|
||||
}
|
||||
|
||||
return value.toFixed(2);
|
||||
@@ -97,7 +74,7 @@ export function FormatGPTProfileRating(
|
||||
export function FormatGPTSessionRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
key: SessionCalculatedDataLookup[GPTString],
|
||||
key: AnySessionRatingAlg,
|
||||
value: number | null | undefined
|
||||
) {
|
||||
if (value === null || value === undefined) {
|
||||
@@ -106,8 +83,8 @@ export function FormatGPTSessionRating(
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
if (gptConfig.sessionRatingAlgFormatters[key]) {
|
||||
return gptConfig.sessionRatingAlgFormatters[key]!(value);
|
||||
if (gptConfig.sessionRatingAlgs[key].formatter) {
|
||||
return gptConfig.sessionRatingAlgs[key].formatter!(value);
|
||||
}
|
||||
|
||||
return value.toFixed(2);
|
||||
@@ -191,27 +168,14 @@ export function SelectRightChart(
|
||||
chartID: string,
|
||||
charts: ChartDocument[]
|
||||
) {
|
||||
if (gptConfig.difficulties.includes(chartID as any)) {
|
||||
for (const chart of charts) {
|
||||
if (chart.difficulty === chartID && chart.isPrimary) {
|
||||
return chart;
|
||||
}
|
||||
// try matching difficulty names, if it's not any of those, it's probably a chartID.
|
||||
for (const chart of charts) {
|
||||
if (chart.difficulty === chartID && chart.isPrimary) {
|
||||
return chart;
|
||||
}
|
||||
} else if (gptConfig.gptString === "itg:Stamina") {
|
||||
for (const chart of charts) {
|
||||
if (
|
||||
(chart as ChartDocument<"itg:Stamina">).data.hashGSV3 === chartID &&
|
||||
chart.isPrimary
|
||||
) {
|
||||
return chart;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else, its a chart ID.
|
||||
for (const chart of charts) {
|
||||
if (chartID === chart.chartID) {
|
||||
return chart;
|
||||
}
|
||||
|
||||
if (chartID === chart.chartID) {
|
||||
return chart;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +251,7 @@ export function FormatScoreRating(
|
||||
return "No Data.";
|
||||
}
|
||||
|
||||
const formatter = GetGamePTConfig(game, playtype).scoreRatingAlgFormatters[rating];
|
||||
const formatter = GetGamePTConfig(game, playtype).scoreRatingAlgs[rating].formatter;
|
||||
|
||||
if (!formatter) {
|
||||
return value.toFixed(2);
|
||||
@@ -299,14 +263,14 @@ export function FormatScoreRating(
|
||||
export function FormatSessionRating(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
rating: keyof SessionDocument["calculatedData"],
|
||||
rating: AnySessionRatingAlg,
|
||||
value: number | null | undefined
|
||||
) {
|
||||
if (value === null || value === undefined) {
|
||||
return "No Data.";
|
||||
}
|
||||
|
||||
const formatter = GetGamePTConfig(game, playtype).sessionRatingAlgFormatters[rating];
|
||||
const formatter = GetGamePTConfig(game, playtype).sessionRatingAlgs[rating].formatter;
|
||||
|
||||
if (!formatter) {
|
||||
return value.toFixed(2);
|
||||
@@ -315,31 +279,6 @@ export function FormatSessionRating(
|
||||
return formatter(value);
|
||||
}
|
||||
|
||||
export function GetGradeFromPercent<GPT extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
percent: number
|
||||
): Grades[GPT] {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
const boundaries = gptConfig.gradeBoundaries;
|
||||
const grades = gptConfig.grades;
|
||||
|
||||
if (!boundaries) {
|
||||
throw new Error(
|
||||
`Invalid call to GetGradeFromPercent! GPT ${game}:${playtype} does not use grade boundaries.`
|
||||
);
|
||||
}
|
||||
|
||||
// (hey, this for loop is backwards!)
|
||||
for (let i = boundaries.length; i >= 0; i--) {
|
||||
if (percent >= boundaries[i]) {
|
||||
return grades[i] as Grades[GPT];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Could not resolve grade for percent ${percent} on game ${game}`);
|
||||
}
|
||||
|
||||
export function ConditionalLeadingSpace(maybeStr: string | null) {
|
||||
if (maybeStr === null) {
|
||||
return "";
|
||||
|
||||
@@ -1,46 +1,32 @@
|
||||
import { Game, GetGamePTConfig, Playtypes } from "tachi-common";
|
||||
import { Game, GetGamePTConfig, GetScoreMetricConf, Playtypes } from "tachi-common";
|
||||
|
||||
export function HumanFriendlyStrToGradeIndex(game: Game, playtype: Playtypes[Game]) {
|
||||
export function HumanFriendlyStrToEnumIndex(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
enumMetric: string
|
||||
) {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
const lowerGrades = gptConfig.grades.map((e) => e.toLowerCase());
|
||||
const conf = GetScoreMetricConf(gptConfig, enumMetric);
|
||||
|
||||
if (conf.type !== "ENUM") {
|
||||
return () => 0; // wut
|
||||
}
|
||||
|
||||
const lowerValues = conf.values.map((e) => e.toLowerCase());
|
||||
|
||||
return (str: string) => {
|
||||
const lowerStr = str.toLowerCase();
|
||||
let partialMatch: number | null = null;
|
||||
|
||||
for (let i = 0; i < gptConfig.grades.length; i++) {
|
||||
const grade = lowerGrades[i];
|
||||
for (let i = 0; i < conf.values.length; i++) {
|
||||
const value = lowerValues[i];
|
||||
|
||||
if (grade === lowerStr) {
|
||||
if (value === lowerStr) {
|
||||
return i;
|
||||
}
|
||||
|
||||
if (grade.startsWith(lowerStr) && partialMatch === null) {
|
||||
partialMatch = i;
|
||||
}
|
||||
}
|
||||
|
||||
return partialMatch;
|
||||
};
|
||||
}
|
||||
|
||||
export function HumanFriendlyStrToLampIndex(game: Game, playtype: Playtypes[Game]) {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
const lowerLamps = gptConfig.lamps.map((e) => e.toLowerCase());
|
||||
|
||||
return (str: string) => {
|
||||
const lowerStr = str.toLowerCase();
|
||||
let partialMatch: number | null = null;
|
||||
|
||||
for (let i = 0; i < gptConfig.lamps.length; i++) {
|
||||
const lamp = lowerLamps[i];
|
||||
|
||||
if (lamp === lowerStr) {
|
||||
return i;
|
||||
}
|
||||
|
||||
if (lamp.startsWith(lowerStr) && partialMatch === null) {
|
||||
if (value.startsWith(lowerStr) && partialMatch === null) {
|
||||
partialMatch = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
|
||||
import { HumanFriendlyStrToEnumIndex } from "util/str-to-num";
|
||||
import { ValueGetterOrHybrid } from "util/ztable/search";
|
||||
import {
|
||||
BMS_TABLES,
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
GamePTConfig,
|
||||
GetGamePTConfig,
|
||||
GPTString,
|
||||
PBScoreDocument,
|
||||
Playtype,
|
||||
ScoreDocument,
|
||||
} from "tachi-common";
|
||||
import { ComparePBsDataset, FolderDataset, PBDataset, ScoreDataset } from "types/tables";
|
||||
|
||||
@@ -32,17 +34,8 @@ export function CreateDefaultScoreSearchParams<GPT extends GPTString = GPTString
|
||||
title: (x) => x.__related.song.title,
|
||||
difficulty: (x) => x.__related.chart.difficulty,
|
||||
level: (x) => x.__related.chart.levelNum,
|
||||
score: (x) => x.scoreData.score,
|
||||
percent: (x) => x.scoreData.percent,
|
||||
highlight: (x) => !!x.highlight,
|
||||
lamp: {
|
||||
valueGetter: (x) => [x.scoreData.lamp, x.scoreData.lamp.index],
|
||||
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
|
||||
},
|
||||
grade: {
|
||||
valueGetter: (x) => [x.scoreData.grade.string, x.scoreData.grade.string],
|
||||
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
|
||||
},
|
||||
...GetMetricSearchParams(game, playtype),
|
||||
...CreateCalcDataSearchFns(gptConfig),
|
||||
};
|
||||
|
||||
@@ -53,6 +46,33 @@ export function CreateDefaultScoreSearchParams<GPT extends GPTString = GPTString
|
||||
return searchFunctions;
|
||||
}
|
||||
|
||||
export function GetMetricSearchParams(game: Game, playtype: Playtype) {
|
||||
const searchFns: Record<string, ValueGetterOrHybrid<PBScoreDocument | ScoreDocument>> = {};
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
for (const [metric, conf] of Object.entries({
|
||||
...gptConfig.providedMetrics,
|
||||
...gptConfig.derivedMetrics,
|
||||
})) {
|
||||
switch (conf.type) {
|
||||
case "ENUM":
|
||||
searchFns[metric] = {
|
||||
// @ts-expect-error lol this is fine pls
|
||||
valueGetter: (x) => x.scoreData[metric],
|
||||
strToNum: HumanFriendlyStrToEnumIndex(game, playtype, metric),
|
||||
};
|
||||
break;
|
||||
case "INTEGER":
|
||||
case "DECIMAL":
|
||||
// @ts-expect-error lol this is fine pls
|
||||
searchFns[metric] = (x) => x.scoreData[metric];
|
||||
}
|
||||
}
|
||||
|
||||
return searchFns;
|
||||
}
|
||||
|
||||
export function CreateDefaultPBSearchParams<GPT extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtype
|
||||
@@ -64,20 +84,11 @@ export function CreateDefaultPBSearchParams<GPT extends GPTString = GPTString>(
|
||||
title: (x) => x.__related.song.title,
|
||||
difficulty: (x) => x.__related.chart.difficulty,
|
||||
level: (x) => x.__related.chart.levelNum,
|
||||
score: (x) => x.scoreData.score,
|
||||
percent: (x) => x.scoreData.percent,
|
||||
ranking: (x) => x.rankingData.rank,
|
||||
rivalRanking: (x) => x.rankingData.rivalRank,
|
||||
highlight: (x) => !!x.highlight,
|
||||
username: (x) => x.__related.user?.username ?? null,
|
||||
lamp: {
|
||||
valueGetter: (x) => [x.scoreData.lamp, x.scoreData.lamp.index],
|
||||
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
|
||||
},
|
||||
grade: {
|
||||
valueGetter: (x) => [x.scoreData.grade.string, x.scoreData.grade.string],
|
||||
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
|
||||
},
|
||||
...GetMetricSearchParams(game, playtype),
|
||||
...CreateCalcDataSearchFns(gptConfig),
|
||||
};
|
||||
|
||||
@@ -117,26 +128,11 @@ export function CreateDefaultFolderSearchParams<GPT extends GPTString = GPTStrin
|
||||
title: (x) => x.__related.song.title,
|
||||
difficulty: (x) => x.difficulty,
|
||||
level: (x) => x.levelNum,
|
||||
score: (x) => x.__related.pb?.scoreData.score ?? null,
|
||||
percent: (x) => x.__related.pb?.scoreData.percent ?? null,
|
||||
ranking: (x) => x.__related.pb?.rankingData.rank ?? null,
|
||||
rivalRanking: (x) => x.__related.pb?.rankingData.rivalRank ?? null,
|
||||
highlight: (x) => !!x.__related.pb?.highlight,
|
||||
played: (x) => !!x.__related.pb,
|
||||
lamp: {
|
||||
valueGetter: (x) =>
|
||||
x.__related.pb
|
||||
? [x.__related.pb.scoreData.lamp, x.__related.pb.scoreData.lamp.index]
|
||||
: null,
|
||||
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
|
||||
},
|
||||
grade: {
|
||||
valueGetter: (x) =>
|
||||
x.__related.pb
|
||||
? [x.__related.pb.scoreData.grade.string, x.__related.pb.scoreData.grade.string]
|
||||
: null,
|
||||
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
|
||||
},
|
||||
...GetMetricSearchParams(game, playtype),
|
||||
...CreateFolderCalcDataSearchFns(gptConfig),
|
||||
};
|
||||
|
||||
@@ -149,8 +145,9 @@ export function CreateDefaultFolderSearchParams<GPT extends GPTString = GPTStrin
|
||||
|
||||
function CreateFolderCalcDataSearchFns(gptConfig: GamePTConfig) {
|
||||
return Object.fromEntries(
|
||||
gptConfig.scoreRatingAlgs.map((e) => [
|
||||
Object.keys(gptConfig.scoreRatingAlgs).map((e) => [
|
||||
e.toLowerCase(),
|
||||
// @ts-expect-error this is fine please leave me alone
|
||||
(x: FolderDataset[0]) => x.__related.pb?.calculatedData[e] ?? null,
|
||||
])
|
||||
);
|
||||
@@ -158,7 +155,8 @@ function CreateFolderCalcDataSearchFns(gptConfig: GamePTConfig) {
|
||||
|
||||
function CreateCalcDataSearchFns(gptConfig: GamePTConfig) {
|
||||
return Object.fromEntries(
|
||||
gptConfig.scoreRatingAlgs.map(
|
||||
Object.keys(gptConfig.scoreRatingAlgs).map(
|
||||
// @ts-expect-error this is fine please leave me alone
|
||||
(e) => [e.toLowerCase(), (x: PBDataset[0]) => x.calculatedData[e]] ?? null
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import {
|
||||
Game,
|
||||
GPTTierlists,
|
||||
GPTString,
|
||||
IIDX_LAMPS,
|
||||
ScoreDocument,
|
||||
SDVX_LAMPS,
|
||||
Playtype,
|
||||
} from "tachi-common";
|
||||
|
||||
type ScaleAchievedFns = {
|
||||
[I in GPTString]: {
|
||||
[K in GPTTierlists[GPT]]: ((k: ScoreDocument<GPT>) => boolean) | null;
|
||||
};
|
||||
};
|
||||
|
||||
const ScaleNameAchievedFns: ScaleAchievedFns = {
|
||||
"bms:14K": {
|
||||
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
|
||||
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
|
||||
},
|
||||
"bms:7K": {
|
||||
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
|
||||
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
|
||||
},
|
||||
"pms:Controller": {
|
||||
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
|
||||
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
|
||||
},
|
||||
"pms:Keyboard": {
|
||||
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
|
||||
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
|
||||
},
|
||||
"chunithm:Single": {},
|
||||
"gitadora:Dora": {},
|
||||
"gitadora:Gita": {},
|
||||
"iidx:DP": {
|
||||
"dp-tier": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.CLEAR,
|
||||
},
|
||||
"iidx:SP": {
|
||||
"kt-NC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.CLEAR,
|
||||
"kt-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
|
||||
"kt-EXHC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EX_HARD_CLEAR,
|
||||
},
|
||||
"maimaidx:Single": {},
|
||||
"museca:Single": {
|
||||
"tachi-score": null,
|
||||
},
|
||||
"sdvx:Single": {
|
||||
clear: (k) => k.scoreData.lamp.index >= SDVX_LAMPS.CLEAR,
|
||||
},
|
||||
"usc:Controller": {},
|
||||
"usc:Keyboard": {},
|
||||
"wacca:Single": {},
|
||||
"popn:9B": {},
|
||||
"jubeat:Single": {},
|
||||
"itg:Stamina": {},
|
||||
};
|
||||
|
||||
export function GetScaleAchievedFn(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
tierlist: GPTTierlists[GPTString]
|
||||
) {
|
||||
const scl = ScaleNameAchievedFns[`${game}:${playtype}` as GPTString];
|
||||
|
||||
if (!scl) {
|
||||
throw new Error(`Invalid game + pt combination ${game}:${playtype}. Can't find scale.`);
|
||||
}
|
||||
|
||||
// @ts-expect-error hack haha
|
||||
return scl[tierlist];
|
||||
}
|
||||
Reference in New Issue
Block a user