From d385c35eb7a114d0f7075889259d97268e93f6a2 Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Sat, 7 Jan 2023 12:00:19 +0000 Subject: [PATCH] checkpoint: 20 --- common/src/config/game-support/bms.ts | 1 + common/src/types/metrics.ts | 87 +++++++++++++++---- .../framework/calculated-data/games/bms.ts | 26 ++++++ .../calculated-data/games/chunithm.ts | 6 ++ .../framework/calculated-data/games/iidx.ts | 61 +++++++++++++ .../framework/calculated-data/games/jubeat.ts | 7 ++ .../framework/calculated-data/games/popn.ts | 12 +++ .../framework/calculated-data/games/wacca.ts | 6 ++ .../framework/calculated-data/profile.ts | 0 .../framework/calculated-data/score.ts | 25 ++++++ .../framework/calculated-data/session.ts | 0 .../framework/calculated-data/types.ts | 13 +++ .../score-import/framework/common/types.ts | 5 +- .../framework/derivers/derivers.ts | 62 ++++--------- .../framework/derivers/games/popn.ts | 24 +++++ .../framework/score-calculated-data/stats.ts | 68 --------------- 16 files changed, 272 insertions(+), 131 deletions(-) create mode 100644 server/src/lib/score-import/framework/calculated-data/games/bms.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/games/chunithm.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/games/iidx.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/games/jubeat.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/games/popn.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/games/wacca.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/profile.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/score.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/session.ts create mode 100644 server/src/lib/score-import/framework/calculated-data/types.ts create mode 100644 server/src/lib/score-import/framework/derivers/games/popn.ts diff --git a/common/src/config/game-support/bms.ts b/common/src/config/game-support/bms.ts index f557261f2..020b2ff4e 100644 --- a/common/src/config/game-support/bms.ts +++ b/common/src/config/game-support/bms.ts @@ -266,6 +266,7 @@ export const BMS_7K_CONF = { export const BMS_14K_CONF = { ...BMS_7K_CONF, + classes: { genocideDan: { type: "PROVIDED", values: BMSGenocideDans }, stslDan: { type: "PROVIDED", values: BMSDPSlDans }, diff --git a/common/src/types/metrics.ts b/common/src/types/metrics.ts index 6e984cc5d..068e8b3f8 100644 --- a/common/src/types/metrics.ts +++ b/common/src/types/metrics.ts @@ -7,6 +7,19 @@ import type { ConfProvidedMetrics, } from "./game-config"; +// WARNING! THIS FILE IS VERY COMPLEX. THERE IS SOME HIGH-TIER TYPESCRIPT MAGIC GOING +// ON HERE. + +// The rough outline is that Tachi allows games to have up to 5 kinds of metrics +// "DECIMAL" | "INTEGER" | "ENUM" | "GRAPH" and "NULLABLE_GRAPH". + +// Metrics themselves then come in three groups: +// - Provided: They **must** be provided for a score import to be usable +// - Derived: They **will** be put on a score document, but are a function +// of the provided metrics and the chart this score is on (i.e. Grades for IIDX) +// - Additional: They **may** exist. We want to store them if they exist, but don't +// mandate their existence (i.e. fast/slow/maxCombo) + export type DecimalMetricValidator = ( metric: number, chart: ChartDocument @@ -66,6 +79,8 @@ export type ConfScoreMetric = /** * When we store and interact with enum values, we want them to be both strings * and integers. + * + * This is used by score documents that are stored in the DB. */ export interface EnumValue { string: S; @@ -74,9 +89,9 @@ export interface EnumValue { /** * Given a Metric Type, turn it into its evaluated form. An IntegerScoreMetric - * becomes an integer, etc. + * becomes an integer and an enum becomes an EnumValue. */ -export type ExtractMetricType = M extends ConfDecimalScoreMetric +export type ExtractMetricValue = M extends ConfDecimalScoreMetric ? number : M extends ConfIntegerScoreMetric ? integer @@ -88,6 +103,26 @@ export type ExtractMetricType = M extends ConfDecimal ? Array : never; +/** + * Given a Metric Type, turn it into its evaluated form with the caveat that + * enums become strings instead of EnumValue. + * + * This is used on the intermediate score importing step, where ENUM metrics aren't + * fully realised. We'd be working with { lamp: "FAILED" } instead of + * { lamp: { string: "FAILED", index: 1 }}. + */ +export type DryExtractMetricType = M extends ConfDecimalScoreMetric + ? number + : M extends ConfIntegerScoreMetric + ? integer + : M extends ConfEnumScoreMetric + ? V + : M extends ConfGraphScoreMetric + ? Array + : M extends ConfNullableGraphScoreMetric + ? Array + : never; + /** * Extract all the names of enum types from this record of score metrics. * @@ -109,6 +144,9 @@ export type ExtractEnumMetricNames> = [K in keyof R]: R[K] extends ConfEnumScoreMetric ? K : never; }[keyof R]; +/** + * What are all the metrics available for this GPT? + */ export type AllMetrics = { [GPT in GPTString]: ConfDerivedMetrics[GPT] & ConfOptionalMetrics[GPT] & @@ -125,28 +163,47 @@ export type GetEnumValue< MetricName extends ExtractEnumMetricNames > = AllMetrics[GPT][MetricName] extends ConfEnumScoreMetric ? EnumValues : never; -type PossibleMetrics = ExtractMetricType; - /** - * Turn a record of ScoreMetrics into their actual literal values. + * Turn a record of ConfigScoreMetrics into their actual literal values. * - * @example ExtractMetrics<{ score: IntegerScoreMetric; lamp: EnumScoreMetric<"FAILED"|"CLEAR"> }> + * @example ExtractMetrics<{ + * score: ConfIntegerScoreMetric; lamp: ConfEnumScoreMetric<"FAILED"|"CLEAR"> + * }> * will equal - * { score: integer; lamp: "FAILED" | "CLEAR" } + * { score: integer; lamp: { string: "FAILED" | "CLEAR "..., index: number } } */ export type ExtractMetrics> = { - [K in keyof R]: ExtractMetricType; + [K in keyof R]: ExtractMetricValue; }; +/** + * Turn a record of score metrics in to their "dry" values. These are used by dry + * scores and consist of one change: + * + * Enums would normally be { string: "FAILED" | ..., index: number }. In dry extraction + * they are extracted to just "FAILED" | ... + * + * This is because dry scores don't fill out these values. + */ +export type DryExtractMetrics> = { + [K in keyof R]: DryExtractMetricType; +}; + +// We want some signatures for implementing metric "derivers". +// This complex type nonsense effectively gives us a typesafe form for: +// MetricDeriver<"iidx:SP", number> +// (metrics: IIDXSPMetrics, chart: Chart<"iidx:SP"> ) => number + export type DerivedMetricValue = Array | Array | integer | number | string; +export type MetricValue = ExtractMetricValue; + export type MetricDeriver< - M extends Record, GPT extends GPTString, // possible return values // from a derived fn V extends DerivedMetricValue = DerivedMetricValue -> = (mandatoryMetrics: M, chart: ChartDocument) => V; +> = (mandatoryMetrics: DryExtractMetrics, chart: ChartDocument) => V; /** * A function that will derive this metric, given a function of other metrics and @@ -155,14 +212,14 @@ export type MetricDeriver< export type ScoreMetricDeriver = // graph score metrics correspond to Array M extends ConfGraphScoreMetric - ? MetricDeriver, GPT, Array> + ? MetricDeriver> : // nullable graphs correspond to Array M extends ConfNullableGraphScoreMetric - ? MetricDeriver, GPT, Array> + ? MetricDeriver> : // enums correspond to their string unions ("FAILED"|"CLEAR") M extends ConfEnumScoreMetric - ? MetricDeriver, GPT, V> + ? MetricDeriver : // the other two are obvious M extends ConfIntegerScoreMetric - ? MetricDeriver, GPT, integer> - : MetricDeriver, GPT, number>; + ? MetricDeriver + : MetricDeriver; diff --git a/server/src/lib/score-import/framework/calculated-data/games/bms.ts b/server/src/lib/score-import/framework/calculated-data/games/bms.ts new file mode 100644 index 000000000..e196974a9 --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/bms.ts @@ -0,0 +1,26 @@ +import type { CalculateDataFunction, GPTScoreCalculators } from "../types"; + +const SGLCalc: CalculateDataFunction<"bms:7K" | "bms:14K"> = (dryScore, chart) => { + const ecValue = chart.data.sglEC ?? 0; + const hcValue = chart.data.sglHC ?? 0; + + switch (dryScore.scoreData.lamp) { + case "FULL COMBO": + case "EX HARD CLEAR": + case "HARD CLEAR": + return Math.max(hcValue, ecValue); + case "CLEAR": + case "EASY CLEAR": + return ecValue; + default: + return 0; + } +}; + +export const BMS_7K_SCORE_CALC: GPTScoreCalculators["bms:7K"] = { + sieglinde: SGLCalc, +}; + +export const BMS_14K_SCORE_CALC: GPTScoreCalculators["bms:14K"] = { + sieglinde: SGLCalc, +}; diff --git a/server/src/lib/score-import/framework/calculated-data/games/chunithm.ts b/server/src/lib/score-import/framework/calculated-data/games/chunithm.ts new file mode 100644 index 000000000..34f6a38dc --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/chunithm.ts @@ -0,0 +1,6 @@ +import { CHUNITHMRating } from "rg-stats"; +import type { GPTScoreCalculators } from "../types"; + +export const CHUNITHM_SCORE_CALC: GPTScoreCalculators["chunithm:Single"] = { + rating: (dryScore, chart) => CHUNITHMRating.calculate(dryScore.scoreData.score, chart.levelNum), +}; diff --git a/server/src/lib/score-import/framework/calculated-data/games/iidx.ts b/server/src/lib/score-import/framework/calculated-data/games/iidx.ts new file mode 100644 index 000000000..899a5b36d --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/iidx.ts @@ -0,0 +1,61 @@ +import { PoyashiBPI } from "rg-stats"; +import type { GPTScoreCalculators, CalculateDataFunction } from "../types"; + +const BPICalc: CalculateDataFunction<"iidx:DP" | "iidx:SP"> = (dryScore, chart) => { + if (chart.data.kaidenAverage === null || chart.data.worldRecord === null) { + return null; + } + + return PoyashiBPI.calculate( + dryScore.scoreData.score, + chart.data.kaidenAverage, + chart.data.worldRecord, + chart.data.notecount * 2, + chart.data.bpiCoefficient + ); +}; + +export const IIDX_SP_SCORE_CALC: GPTScoreCalculators["iidx:SP"] = { + BPI: BPICalc, + ktLampRating: (dryScore, chart) => { + // if chart has no ncValue, use the rating of the chart instead. + const ncValue = chart.data.ncTier?.value ?? chart.levelNum; + + // if hc < nc, hcValue = ncValue + // this means you can't lose score from getting a better clear. + // same for exhc < hc. + const hcValue = Math.max(chart.data.hcTier?.value ?? 0, ncValue); + const exhcValue = Math.max(chart.data.exhcTier?.value ?? 0, hcValue); + + switch (dryScore.scoreData.lamp) { + case "FULL COMBO": + case "EX HARD CLEAR": + return exhcValue; + case "HARD CLEAR": + return hcValue; + case "CLEAR": + return ncValue; + default: + return 0; + } + }, +}; + +export const IIDX_DP_SCORE_CALC: GPTScoreCalculators["iidx:DP"] = { + BPI: BPICalc, + ktLampRating: (dryScore, chart) => { + // if chart has no tier, use the rating of the chart instead. + const ecValue = chart.data.dpTier?.value ?? chart.levelNum; + + switch (dryScore.scoreData.lamp) { + case "FULL COMBO": + case "EX HARD CLEAR": + case "HARD CLEAR": + case "CLEAR": + case "EASY CLEAR": + return ecValue; + default: + return 0; + } + }, +}; diff --git a/server/src/lib/score-import/framework/calculated-data/games/jubeat.ts b/server/src/lib/score-import/framework/calculated-data/games/jubeat.ts new file mode 100644 index 000000000..a530c842d --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/jubeat.ts @@ -0,0 +1,7 @@ +import { Jubility } from "rg-stats"; +import type { GPTScoreCalculators } from "../types"; + +export const JUBEAT_SCORE_CALC: GPTScoreCalculators["jubeat:Single"] = { + jubility: (dryScore, chart) => + Jubility.calculate(dryScore.scoreData.score, dryScore.scoreData.musicRate, chart.levelNum), +}; diff --git a/server/src/lib/score-import/framework/calculated-data/games/popn.ts b/server/src/lib/score-import/framework/calculated-data/games/popn.ts new file mode 100644 index 000000000..3806f3599 --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/popn.ts @@ -0,0 +1,12 @@ +import { PopnClearMedalToLamp } from "../../derivers/games/popn"; +import { PopnClassPoints } from "rg-stats"; +import type { GPTScoreCalculators } from "../types"; + +export const POPN_9B_SCORE_CALC: GPTScoreCalculators["popn:9B"] = { + classPoints: (dryScore, chart) => + PopnClassPoints.calculate( + dryScore.scoreData.score, + PopnClearMedalToLamp(dryScore.scoreData.clearMedal), + chart.levelNum + ), +}; diff --git a/server/src/lib/score-import/framework/calculated-data/games/wacca.ts b/server/src/lib/score-import/framework/calculated-data/games/wacca.ts new file mode 100644 index 000000000..8931670eb --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/games/wacca.ts @@ -0,0 +1,6 @@ +import { WACCARate } from "rg-stats"; +import type { GPTScoreCalculators } from "../types"; + +export const WACCA_SCORE_CALC: GPTScoreCalculators["wacca:Single"] = { + rate: (dryScore, chart) => WACCARate.calculate(dryScore.scoreData.score, chart.levelNum), +}; diff --git a/server/src/lib/score-import/framework/calculated-data/profile.ts b/server/src/lib/score-import/framework/calculated-data/profile.ts new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/lib/score-import/framework/calculated-data/score.ts b/server/src/lib/score-import/framework/calculated-data/score.ts new file mode 100644 index 000000000..69a685b9e --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/score.ts @@ -0,0 +1,25 @@ +import { BMS_14K_SCORE_CALC, BMS_7K_SCORE_CALC } from "./games/bms"; +import { CHUNITHM_SCORE_CALC } from "./games/chunithm"; +import { IIDX_DP_SCORE_CALC, IIDX_SP_SCORE_CALC } from "./games/iidx"; +import type { GPTScoreCalculators } from "./types"; + +export const SCORE_CALCULATORS: GPTScoreCalculators = { + "iidx:SP": IIDX_SP_SCORE_CALC, + "iidx:DP": IIDX_DP_SCORE_CALC, + "bms:7K": BMS_7K_SCORE_CALC, + "bms:14K": BMS_14K_SCORE_CALC, + "chunithm:Single": CHUNITHM_SCORE_CALC, + "jubeat:Single": JUBEAT_SCORE_CALC, + "gitadora:Dora": GITADORA_DORA_SCORE_CALC, + "gitadora:Gita": GITADORA_GITA_SCORE_CALC, + "itg:Stamina": ITG_STAMINA_SCORE_CALC, + "maimaidx:Single": MAIMAI_DX_SCORE_CALC, + "museca:Single": MUSECA_SCORE_CALC, + "pms:Controller": PMS_CONTROLLER_SCORE_CALC, + "pms:Keyboard": PMS_KEYBOARD_SCORE_CALC, + "popn:9B": POPN_9B_SCORE_CALC, + "sdvx:Single": SDVX_SCORE_CALC, + "usc:Controller": USC_CONTROLLER_SCORE_CALC, + "usc:Keyboard": USC_KEYBOARD_SCORE_CALC, + "wacca:Single": WACCA_SCORE_CALC, +}; diff --git a/server/src/lib/score-import/framework/calculated-data/session.ts b/server/src/lib/score-import/framework/calculated-data/session.ts new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/lib/score-import/framework/calculated-data/types.ts b/server/src/lib/score-import/framework/calculated-data/types.ts new file mode 100644 index 000000000..4e02ee9e3 --- /dev/null +++ b/server/src/lib/score-import/framework/calculated-data/types.ts @@ -0,0 +1,13 @@ +import type { DryScore } from "../common/types"; +import type { KtLogger } from "lib/logger/logger"; +import type { GPTString, ChartDocument, ScoreRatingAlgorithms } from "tachi-common"; + +export type CalculateDataFunction = ( + dryScore: DryScore, + chart: ChartDocument, + logger: KtLogger +) => number | null; + +export type GPTScoreCalculators = { + [G in GPTString]: Record>; +}; diff --git a/server/src/lib/score-import/framework/common/types.ts b/server/src/lib/score-import/framework/common/types.ts index 641e7b4d4..b609e454a 100644 --- a/server/src/lib/score-import/framework/common/types.ts +++ b/server/src/lib/score-import/framework/common/types.ts @@ -6,8 +6,9 @@ import type { ScoreDocument, integer, OptionalMetrics, + ConfProvidedMetrics, } from "tachi-common"; -import type { EnumValue } from "tachi-common/types/metrics"; +import type { DryExtractMetrics, EnumValue } from "tachi-common/types/metrics"; import type { Mutable } from "utils/types"; /** @@ -21,7 +22,7 @@ export type ExtractEnumValues> = { [K in keyof TMetrics]: TMetrics[K] extends EnumValue ? V : TMetrics[K]; }; -type DryScoreData = ExtractEnumValues & { +export type DryScoreData = DryExtractMetrics & { optional: Mutable>; judgements: Partial>; }; diff --git a/server/src/lib/score-import/framework/derivers/derivers.ts b/server/src/lib/score-import/framework/derivers/derivers.ts index cabd1da5f..d8490d115 100644 --- a/server/src/lib/score-import/framework/derivers/derivers.ts +++ b/server/src/lib/score-import/framework/derivers/derivers.ts @@ -8,6 +8,7 @@ import { GetGrade } from "./common-utils"; import { IIDXLIKE_DERIVERS } from "./games/iidx-like"; +import { PopnClearMedalToLamp } from "./games/popn"; import { SDVXLIKE_DERIVERS } from "./games/sdvx-like"; import { InternalFailure } from "../common/converter-failures"; import { @@ -21,16 +22,10 @@ import { POPN_GBOUNDARIES, WACCA_GBOUNDARIES, } from "tachi-common"; -import type { DryScore } from "../common/types"; +import type { DryScore, DryScoreData } from "../common/types"; import type { GPTDerivers } from "./types"; -import type { - GPTString, - ChartDocument, - ConfProvidedMetrics, - ConfDerivedMetrics, - ScoreData, -} from "tachi-common"; -import type { MetricDeriver } from "tachi-common/types/metrics"; +import type { ChartDocument, DerivedMetrics, GPTString, ScoreData } from "tachi-common"; +import type { MetricValue } from "tachi-common/types/metrics"; type AllGPTDerivers = { [GPT in GPTString]: GPTDerivers; @@ -74,14 +69,14 @@ const GPT_DERIVERS: AllGPTDerivers = { // don't check if metrics.survivedPercent === 100, as due to floating // point inaccuracies, it's possible to have a 100% fail // (on extremely long charts, for example) - if (metrics.lamp.string === "FAILED") { + if (metrics.lamp === "FAILED") { return metrics.survivedPercent; } return 100 + metrics.scorePercent; }, grade: ({ scorePercent, lamp }) => { - if (lamp.string === "FAILED") { + if (lamp === "FAILED") { return "F"; } @@ -95,35 +90,16 @@ const GPT_DERIVERS: AllGPTDerivers = { grade: ({ score }) => GetGrade(MUSECA_GBOUNDARIES, score), }, "popn:9B": { - lamp: ({ clearMedal }) => { - switch (clearMedal.string) { - case "perfect": - return "PERFECT"; - case "fullComboCircle": - case "fullComboDiamond": - case "fullComboStar": - return "FULL COMBO"; - case "clearCircle": - case "clearDiamond": - case "clearStar": - return "CLEAR"; - case "easyClear": - return "EASY CLEAR"; - case "failedCircle": - case "failedDiamond": - case "failedStar": - return "FAILED"; - } - }, + lamp: ({ clearMedal }) => PopnClearMedalToLamp(clearMedal), grade: ({ score, clearMedal }) => { const gradeString = GetGrade(POPN_GBOUNDARIES, score); - const c = clearMedal.string; - // grades are kneecapped at "A" if you failed. if ( score >= 90_000 && - (c === "failedCircle" || c === "failedDiamond" || c === "failedStar") + (clearMedal === "failedCircle" || + clearMedal === "failedDiamond" || + clearMedal === "failedStar") ) { return "A"; } @@ -138,16 +114,13 @@ const GPT_DERIVERS: AllGPTDerivers = { * we want to store. */ function DeriveMetrics( - gpt: GPTString, - metrics: ConfProvidedMetrics[GPT], + gpt: GPT, + metrics: DryScoreData, chart: ChartDocument ) { - const deriverImplementation: Record< - string, - MetricDeriver - > = GPT_DERIVERS[gpt]; + const deriverImplementation: GPTDerivers = GPT_DERIVERS[gpt]; - const derivedMetrics: Record = {}; + const derivedMetrics: Record = {}; const gptConfig = GetGPTConfig(gpt); @@ -162,15 +135,12 @@ function DeriveMetrics( const value = fn(metrics, chart); - // enum values on scores are stored as { string, index }. One is convenient - // for sorting and stuff in the DB, the other is convenient to know what - // actually is what. Integers only sucks. if (metricConfig.type === "ENUM") { const index = metricConfig.values.indexOf(value); if (index === -1) { throw new InternalFailure( - `Failed to get the index for ENUM ${gpt} '${key}' for value ${value}. This should never happen!` + `Tried to turn a ${key} enum value of ${value} into an index, got -1?` ); } @@ -183,7 +153,7 @@ function DeriveMetrics( } } - return derivedMetrics as ConfDerivedMetrics[GPT]; + return derivedMetrics as DerivedMetrics[GPT]; } /** diff --git a/server/src/lib/score-import/framework/derivers/games/popn.ts b/server/src/lib/score-import/framework/derivers/games/popn.ts new file mode 100644 index 000000000..3bd108e27 --- /dev/null +++ b/server/src/lib/score-import/framework/derivers/games/popn.ts @@ -0,0 +1,24 @@ +import type { GetEnumValue } from "tachi-common/types/metrics"; + +export function PopnClearMedalToLamp( + clearMedal: GetEnumValue<"popn:9B", "clearMedal"> +): GetEnumValue<"popn:9B", "lamp"> { + switch (clearMedal) { + case "perfect": + return "PERFECT"; + case "fullComboCircle": + case "fullComboDiamond": + case "fullComboStar": + return "FULL COMBO"; + case "clearCircle": + case "clearDiamond": + case "clearStar": + return "CLEAR"; + case "easyClear": + return "EASY CLEAR"; + case "failedCircle": + case "failedDiamond": + case "failedStar": + return "FAILED"; + } +} diff --git a/server/src/lib/score-import/framework/score-calculated-data/stats.ts b/server/src/lib/score-import/framework/score-calculated-data/stats.ts index c8fd76184..701c74753 100644 --- a/server/src/lib/score-import/framework/score-calculated-data/stats.ts +++ b/server/src/lib/score-import/framework/score-calculated-data/stats.ts @@ -4,32 +4,6 @@ import type { KtLogger } from "lib/logger/logger"; import type { ChartDocument, Game, integer, Playtype, GPTStrings } from "tachi-common"; import type { GetEnumValue } from "tachi-common/types/metrics"; -export function CalculateKTLampRatingIIDXSP(dryScore: DryScore, chart: ChartDocument<"iidx:SP">) { - const ncValue = chart.tierlistInfo["kt-NC"]?.value ?? 0; - const hcValue = Math.max(chart.tierlistInfo["kt-HC"]?.value ?? 0, ncValue); - const exhcValue = Math.max(chart.tierlistInfo["kt-EXHC"]?.value ?? 0, hcValue); - - if (!exhcValue && !hcValue && !ncValue) { - return LampRatingNoTierlistInfo(dryScore, "iidx", chart.playtype, chart); - } - - const lamp = dryScore.scoreData.lamp; - - const gptConfig = GetGamePTConfig("iidx", chart.playtype); - - const lampIndex = gptConfig.lamps.indexOf(lamp); - - if (exhcValue && lampIndex >= IIDX_LAMPS.EX_HARD_CLEAR) { - return exhcValue; - } else if (hcValue && lampIndex >= IIDX_LAMPS.HARD_CLEAR) { - return hcValue; - } else if (ncValue && lampIndex >= IIDX_LAMPS.CLEAR) { - return ncValue; - } - - return 0; -} - export function CalculateKTLampRatingIIDXDP(dryScore: DryScore, chart: ChartDocument<"iidx:DP">) { const tierlistValue = chart.tierlistInfo["dp-tier"]?.value ?? 0; @@ -49,45 +23,3 @@ export function CalculateKTLampRatingIIDXDP(dryScore: DryScore, chart: ChartDocu return 0; } - -function LampRatingNoTierlistInfo( - dryScore: DryScore, - game: Game, - playtype: Playtype, - chart: ChartDocument -) { - const gptConfig = GetGamePTConfig(game, playtype); - - const lamps = gptConfig.lamps; - - const CLEAR_LAMP_INDEX = lamps.indexOf(gptConfig.clearLamp); - - // if this is a clear - if (lamps.indexOf(dryScore.scoreData.lamp) >= CLEAR_LAMP_INDEX) { - // return this chart's numeric level as the lamp rating - return chart.levelNum; - } - - // else, this score is worth 0. - return 0; -} - -export function CalculateSieglinde( - chart: ChartDocument, - lamp: GetEnumValue -) { - const ecValue = chart.data.sglEC ?? 0; - const hcValue = chart.data.sglHC ?? 0; - - switch (lamp) { - case "FULL COMBO": - case "EX HARD CLEAR": - case "HARD CLEAR": - return Math.max(hcValue, ecValue); - case "CLEAR": - case "EASY CLEAR": - return ecValue; - } - - return 0; -}