-
.padStart(6,)
+
{meta?.name ?? t("UserHome.UnknownSong")}
-
- { mapRank ?? '-' }
+
+ { p.difficulty ?? '-' }
-
-
- {("" + getMult(mapData[gameIndex], game)[2]).replace("p", "+")}
-
- {
- rounding.value ?
- roundFloor(mapData[gameIndex], game, 1) :
- (mapData[gameIndex] / 10000).toFixed(4)
- }%
+
+ {p.rank.replace("p", "+")}
+
+ {rounding.value ? roundFloor(p.score, game, 1) : (p.score / 10000).toFixed(4)}%
- {#if game === 'mai2'}
-
- { mapRank ? Math.floor(mapRank * mult[1] * (Math.min(100.5, mapData[3] / 10000) / 100)) : '-' }
-
+ {#if p.ratingChange !== undefined}
+ { p.ratingChange.toFixed(1) }
{/if}
diff --git a/AquaNet/src/libs/scoring.ts b/AquaNet/src/libs/scoring.ts
index 91ded584..acee0e85 100644
--- a/AquaNet/src/libs/scoring.ts
+++ b/AquaNet/src/libs/scoring.ts
@@ -1,85 +1,151 @@
-export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
-
-const multTable = {
- 'mai2': [
- [ 100.5, 22.4, 'SSSp' ],
- [ 100.0, 21.6, 'SSS' ],
- [ 99.5, 21.1, 'SSp' ],
- [ 99, 20.8, 'SS' ],
- [ 98, 20.3, 'Sp' ],
- [ 97, 20, 'S' ],
- [ 94, 16.8, 'AAA' ],
- [ 90, 15.2, 'AA' ],
- [ 80, 13.6, 'A' ],
- [ 75, 12, 'BBB' ],
- [ 70, 11.2, 'BB' ],
- [ 60, 9.6, 'B' ],
- [ 50, 8, 'C' ],
- [ 40, 6.4, 'D' ],
- [ 30, 4.8, 'D' ],
- [ 20, 3.2, 'D' ],
- [ 10, 1.6, 'D' ],
- [ 0, 0, 'D' ]
- ],
-
- // TODO: Fill in multipliers for Chunithm and Ongeki
- 'chu3': [
- [ 100.75, 0, 'SSS' ],
- [ 100.0, 0, 'SS' ],
- [ 97.5, 0, 'S' ],
- [ 95.0, 0, 'AAA' ],
- [ 92.5, 0, 'AA' ],
- [ 90.0, 0, 'A' ],
- [ 80.0, 0, 'BBB' ],
- [ 70.0, 0, 'BB' ],
- [ 60.0, 0, 'B' ],
- [ 50.0, 0, 'C' ],
- [ 0.0, 0, 'D' ]
- ],
-
- 'ongeki': [
- [ 100.75, 0, 'SSS+' ],
- [ 100.0, 0, 'SSS' ],
- [ 99.0, 0, 'SS' ],
- [ 97.0, 0, 'S' ],
- [ 94.0, 0, 'AAA' ],
- [ 90.0, 0, 'AA' ],
- [ 85.0, 0, 'A' ],
- [ 80.0, 0, 'BBB' ],
- [ 75.0, 0, 'BB' ],
- [ 70.0, 0, 'B' ],
- [ 50.0, 0, 'C' ],
- [ 0.0, 0, 'D' ]
- ],
-
- 'wacca': [
- [ 100.0, 0, 'AP' ],
- [ 98.0, 0, 'SSS' ],
- [ 95.0, 0, 'SS' ],
- [ 90.0, 0, 'S' ],
- [ 85.0, 0, 'AAA' ],
- [ 80.0, 0, 'AA' ],
- [ 70.0, 0, 'A' ],
- [ 60.0, 0, 'B' ],
- [ 1.0, 0, 'C' ],
- [ 0.0, 0, 'D' ]
- ]
-}
-
-export function getMult(achievement: number, game: GameName) {
- achievement /= 10000
- const mt = multTable[game]
- for (let i = 0; i < mt.length; i++) {
- if (achievement >= (mt[i][0] as number)) return mt[i]
- }
- return [ 0, 0, 0 ]
-}
-
-export function roundFloor(achievement: number, game: GameName, digits = 2) {
- // Round, but if the rounded number reaches the next rank, use floor instead
- const mult = getMult(achievement, game);
- achievement /= 10000
- const rounded = achievement.toFixed(digits);
- if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
- return (+rounded - Math.pow(10, -digits)).toFixed(digits);
-}
+import { DATA_HOST } from "./config"
+import type { MusicMeta } from "./generalTypes"
+
+export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
+
+const multTable = {
+ 'mai2': [
+ [ 100.5, 22.4, 'SSSp' ],
+ [ 100.0, 21.6, 'SSS' ],
+ [ 99.5, 21.1, 'SSp' ],
+ [ 99, 20.8, 'SS' ],
+ [ 98, 20.3, 'Sp' ],
+ [ 97, 20, 'S' ],
+ [ 94, 16.8, 'AAA' ],
+ [ 90, 15.2, 'AA' ],
+ [ 80, 13.6, 'A' ],
+ [ 75, 12, 'BBB' ],
+ [ 70, 11.2, 'BB' ],
+ [ 60, 9.6, 'B' ],
+ [ 50, 8, 'C' ],
+ [ 40, 6.4, 'D' ],
+ [ 30, 4.8, 'D' ],
+ [ 20, 3.2, 'D' ],
+ [ 10, 1.6, 'D' ],
+ [ 0, 0, 'D' ]
+ ],
+
+ // TODO: Fill in multipliers for Chunithm and Ongeki
+ 'chu3': [
+ [ 100.9, 215, 'SSS+' ],
+ [ 100.75, 200, 'SSS' ],
+ [ 100.0, 0, 'SS' ],
+ [ 97.5, 0, 'S' ],
+ [ 95.0, 0, 'AAA' ],
+ [ 92.5, 0, 'AA' ],
+ [ 90.0, 0, 'A' ],
+ [ 80.0, 0, 'BBB' ],
+ [ 70.0, 0, 'BB' ],
+ [ 60.0, 0, 'B' ],
+ [ 50.0, 0, 'C' ],
+ [ 0.0, 0, 'D' ]
+ ],
+
+ 'ongeki': [
+ [ 100.75, 0, 'SSS+' ],
+ [ 100.0, 0, 'SSS' ],
+ [ 99.0, 0, 'SS' ],
+ [ 97.0, 0, 'S' ],
+ [ 94.0, 0, 'AAA' ],
+ [ 90.0, 0, 'AA' ],
+ [ 85.0, 0, 'A' ],
+ [ 80.0, 0, 'BBB' ],
+ [ 75.0, 0, 'BB' ],
+ [ 70.0, 0, 'B' ],
+ [ 50.0, 0, 'C' ],
+ [ 0.0, 0, 'D' ]
+ ],
+
+ 'wacca': [
+ [ 100.0, 0, 'AP' ],
+ [ 98.0, 0, 'SSS' ],
+ [ 95.0, 0, 'SS' ],
+ [ 90.0, 0, 'S' ],
+ [ 85.0, 0, 'AAA' ],
+ [ 80.0, 0, 'AA' ],
+ [ 70.0, 0, 'A' ],
+ [ 60.0, 0, 'B' ],
+ [ 1.0, 0, 'C' ],
+ [ 0.0, 0, 'D' ]
+ ]
+}
+
+export function getMult(achievement: number, game: GameName) {
+ achievement /= 10000
+ const mt = multTable[game]
+ for (let i = 0; i < mt.length; i++) {
+ if (achievement >= (mt[i][0] as number)) return mt[i]
+ }
+ return [ 0, 0, 0 ]
+}
+
+export function roundFloor(achievement: number, game: GameName, digits = 2) {
+ // Round, but if the rounded number reaches the next rank, use floor instead
+ const mult = getMult(achievement, game);
+ achievement /= 10000
+ const rounded = achievement.toFixed(digits);
+ if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
+ return (+rounded - Math.pow(10, -digits)).toFixed(digits);
+}
+
+export function chusanRating(lv: number, score: number) {
+ console.log(lv)
+ lv = lv * 100
+ if (score >= 1009000) return lv + 215; // SSS+
+ if (score >= 1007500) return lv + 200 + (score - 1007500) / 100; // SSS
+ if (score >= 1005000) return lv + 150 + (score - 1005000) / 50; // SS+
+ if (score >= 1000000) return lv + 100 + (score - 1000000) / 100; // SS
+ if (score >= 975000) return lv + (score - 975000) / 250; // S+, S
+ if (score >= 925000) return lv - 300 + (score - 925000) * 3 / 500; // AA
+ if (score >= 900000) return lv - 500 + (score - 900000) * 4 / 500; // A
+ if (score >= 800000) return ((lv - 500) / 2 + (score - 800000) * ((lv - 500) / 2) / (100000)); // BBB
+ return 0; // C
+}
+
+interface ParsedComposition {
+ musicId: number
+ diffId: number // ID of the difficulty
+ score: number
+ cutoff: number
+ mult: number
+ rank: string // e.g. 'SSS+'
+ difficulty?: number // Actual difficulty of the map
+ img: string
+ ratingChange?: number // Rating change after playing this map
+}
+
+
+export function parseComposition(item: string, meta: MusicMeta, game: GameName): ParsedComposition {
+ // Chuni & ongeki: musicId, difficultId, score
+ // Mai: musicId, level (difficultyId), romVersion, achievement (score)
+ const mapData = item.split(':').map(Number)
+ if (game === 'mai2') mapData.splice(2, 1)
+ const [ musicId, diffId, score ] = mapData
+
+ // Get score multiplier
+ const tup = getMult(score, game)
+ const [ cutoff, mult ] = [ +tup[0], +tup[1] ]
+ const rank = tup[2] as string
+
+ let diff = meta?.notes?.[mapData[1] === 10 ? 0 : mapData[1]]?.lv
+
+ function calcDxChange() {
+ if (!diff) return
+ if (game === 'mai2')
+ return Math.floor(diff * +mult * (Math.min(100.5, mapData[3] / 10000) / 100))
+ if (game === 'chu3')
+ return chusanRating(diff, score) / 100
+ }
+
+ return {
+ musicId,
+ diffId,
+ score,
+ cutoff,
+ mult,
+ rank,
+ difficulty: diff,
+ img: `${DATA_HOST}/d/${game}/music/00${mapData[0].toString().padStart(6, '0').substring(2)}.png`,
+ ratingChange: calcDxChange()
+ }
+}
diff --git a/AquaNet/src/pages/UserHome.svelte b/AquaNet/src/pages/UserHome.svelte
index b3be7134..46102176 100644
--- a/AquaNet/src/pages/UserHome.svelte
+++ b/AquaNet/src/pages/UserHome.svelte
@@ -260,6 +260,8 @@
+
+
{t('UserHome.RecentScores')}