From 023f93e1fb565b8b720ff6df6573c21f57c803b3 Mon Sep 17 00:00:00 2001 From: Gyoo Date: Sat, 24 Aug 2024 09:31:36 +0000 Subject: [PATCH] feat: support exScore, fast, slow, maxCombo as optional metrics for DDR --- common/src/config/game-support/ddr.ts | 22 +++++++++++++++++ server/src/game-implementations/games/ddr.ts | 26 ++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/common/src/config/game-support/ddr.ts b/common/src/config/game-support/ddr.ts index f46436d4d..bebdc6b1b 100644 --- a/common/src/config/game-support/ddr.ts +++ b/common/src/config/game-support/ddr.ts @@ -4,6 +4,7 @@ import { ClassValue, zodNonNegativeInt } from "../config-utils"; import { p } from "prudence"; import { z } from "zod"; import type { INTERNAL_GAME_CONFIG, INTERNAL_GAME_PT_CONFIG } from "../../types/internals"; +import {FAST_SLOW_MAXCOMBO} from "./_common"; export const DDR_FLARE_CATEGORIES = z.enum(["CLASSIC", "WHITE", "GOLD", "NONE"]); @@ -122,6 +123,19 @@ export const DDR_SP_CONF = { minimumRelevantValue: "0", description: "The Flare rank. If no Flare is provided, Flare 0 is chosen by default.", }, + exScore: { + type: "INTEGER", + formatter: FmtNum, + validate: p.isPositiveInteger, + + // We want to track the best EXScore a user gets, but it is an optional + // metric. + partOfScoreID: true, + + description: + "The EXScore value. Marvelous and O.K. judgements are worth 3 points, Perfect judgements are worth 2 points, Great judgements are worth 1 point, and Good and lower judgements are not worth any points.", + }, + ...FAST_SLOW_MAXCOMBO }, defaultMetric: "score", @@ -132,6 +146,10 @@ export const DDR_SP_CONF = { description: "Flare Skill as it's implemented in DDR World.", formatter: FmtScoreNoCommas, }, + exScore: { + description: "The EXScore.", + formatter: FmtScoreNoCommas, + }, }, sessionRatingAlgs: { @@ -139,6 +157,10 @@ export const DDR_SP_CONF = { description: "Average of your 10 best Flare Points this session", formatter: FmtScoreNoCommas, }, + exScore: { + description: "Average of your 10 best EXScores this session", + formatter: FmtScoreNoCommas, + }, }, profileRatingAlgs: { diff --git a/server/src/game-implementations/games/ddr.ts b/server/src/game-implementations/games/ddr.ts index 96b0e3634..c304c8898 100644 --- a/server/src/game-implementations/games/ddr.ts +++ b/server/src/game-implementations/games/ddr.ts @@ -141,6 +141,25 @@ export const DDR_SCORE_VALIDATORS: Array> = default: } }, + (s) => { + const { MARVELOUS, PERFECT, GREAT, OK } = s.scoreData.judgements; + + if ( + IsNullish(MARVELOUS) || + IsNullish(PERFECT) || + IsNullish(GREAT) || + IsNullish(OK) || + IsNullish(s.scoreData.optional.exScore) + ) { + return; + } + + const calculatedExScore = MARVELOUS * 3 + OK * 3 + PERFECT * 2 + GREAT; + + if (calculatedExScore !== s.scoreData.optional.exScore) { + return `EXScore expected to be ${calculatedExScore} instead of ${s.scoreData.optional.exScore}`; + } + }, ]; export const DDR_IMPL: GPTServerImplementation<"ddr:DP" | "ddr:SP"> = { @@ -260,6 +279,9 @@ export const DDR_IMPL: GPTServerImplementation<"ddr:DP" | "ddr:SP"> = { base.scoreData.score = score.scoreData.score; base.scoreData.grade = score.scoreData.grade; }), + CreatePBMergeFor("largest", "optional.exScore", "Best EX Score", (base, score) => { + base.scoreData.optional.exScore = score.scoreData.optional.exScore; + }), ], profileCalcs: { flareSkill: async (game: Game, playtype: Playtype, userID: integer) => { @@ -336,9 +358,13 @@ export const DDR_IMPL: GPTServerImplementation<"ddr:DP" | "ddr:SP"> = { return DDRFlare.calculate(chart.levelNum, flareLevel); }, + exScore: (scoreData) => { + return scoreData.optional.exScore ?? 0; + }, }, scoreValidators: DDR_SCORE_VALIDATORS, sessionCalcs: { flareSkill: SessionAvgBest10For("flareSkill"), + exScore: SessionAvgBest10For("exScore"), }, };