mirror of https://github.com/hykilpikonna/AquaDX
[+] Chusan rating calculation
parent
038e76ed94
commit
8b90449970
|
@ -4,7 +4,7 @@
|
||||||
import { slide } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
import { DATA_HOST } from "../libs/config";
|
import { DATA_HOST } from "../libs/config";
|
||||||
import { t } from "../libs/i18n";
|
import { t } from "../libs/i18n";
|
||||||
import { type GameName, getMult, roundFloor } from "../libs/scoring";
|
import { type GameName, getMult, parseComposition, roundFloor } from "../libs/scoring";
|
||||||
import { coverNotFound } from "../libs/ui";
|
import { coverNotFound } from "../libs/ui";
|
||||||
import type { MusicMeta } from "../libs/generalTypes";
|
import type { MusicMeta } from "../libs/generalTypes";
|
||||||
import { tooltip } from "../libs/ui";
|
import { tooltip } from "../libs/ui";
|
||||||
|
@ -14,47 +14,35 @@
|
||||||
export let meta: MusicMeta
|
export let meta: MusicMeta
|
||||||
export let game: GameName
|
export let game: GameName
|
||||||
|
|
||||||
let mapData = g.split(":").map(Number)
|
// // mapData: [id, difficulty, score, rank]
|
||||||
let mult = getMult(mapData[3], game)
|
// let mapData = g.split(":").map(Number)
|
||||||
let mapRank: number | undefined = meta?.notes?.[mapData[1] === 10 ? 0 : mapData[1]]?.lv
|
// // mult: [score cutoff, rank multiplier, rank text]
|
||||||
const rounding = useLocalStorage("rounding", true);
|
// let mult = getMult(mapData[3], game)
|
||||||
|
// let mapRank: number | undefined = meta?.notes?.[mapData[1] === 10 ? 0 : mapData[1]]?.lv
|
||||||
let gameIndexMap = {
|
const p = parseComposition(g, meta, game)
|
||||||
'mai2': 3,
|
const rounding = useLocalStorage("rounding", true)
|
||||||
'ongeki': 2,
|
|
||||||
'chu3': 2
|
|
||||||
};
|
|
||||||
|
|
||||||
let gameIndex = gameIndexMap[game as keyof typeof gameIndexMap];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="map-detail-container" transition:slide>
|
<div class="map-detail-container" transition:slide>
|
||||||
<div class="scores">
|
<div class="scores">
|
||||||
<div>
|
<div>
|
||||||
<img src={`${DATA_HOST}/d/${game}/music/00${mapData[0].toString().padStart(6, '0').substring(2)}.png`} alt="" on:error={coverNotFound} />
|
<img src={p.img} alt="" on:error={coverNotFound} />
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="first-line">
|
<div class="first-line">
|
||||||
<div class="song-title">{meta?.name ?? t("UserHome.UnknownSong")}</div>
|
<div class="song-title">{meta?.name ?? t("UserHome.UnknownSong")}</div>
|
||||||
<span class={`lv level-${mapData[1] === 10 ? 3 : mapData[1]}`}>
|
<span class={`lv level-${p.diffId === 10 ? 3 : p.diffId}`}>
|
||||||
{ mapRank ?? '-' }
|
{ p.difficulty ?? '-' }
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="second-line">
|
<div class="second-line">
|
||||||
<span class={`rank-${getMult(mapData[gameIndex], game)[2].toString()[0]}`}>
|
<span class={`rank-${p.rank[0]}`}>
|
||||||
|
<span class="rank-text">{p.rank.replace("p", "+")}</span>
|
||||||
<span class="rank-text">{("" + getMult(mapData[gameIndex], game)[2]).replace("p", "+")}</span>
|
<span class="rank-num" use:tooltip={(p.score / 10000).toFixed(4)}>
|
||||||
<span class="rank-num" use:tooltip={(mapData[gameIndex] / 10000).toFixed(4)}>
|
{rounding.value ? roundFloor(p.score, game, 1) : (p.score / 10000).toFixed(4)}%
|
||||||
{
|
|
||||||
rounding.value ?
|
|
||||||
roundFloor(mapData[gameIndex], game, 1) :
|
|
||||||
(mapData[gameIndex] / 10000).toFixed(4)
|
|
||||||
}%
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{#if game === 'mai2'}
|
{#if p.ratingChange !== undefined}
|
||||||
<span class="dx-change">
|
<span class="dx-change">{ p.ratingChange.toFixed(1) }</span>
|
||||||
{ mapRank ? Math.floor(mapRank * mult[1] * (Math.min(100.5, mapData[3] / 10000) / 100)) : '-' }
|
|
||||||
</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { DATA_HOST } from "./config"
|
||||||
|
import type { MusicMeta } from "./generalTypes"
|
||||||
|
|
||||||
export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
|
export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
|
||||||
|
|
||||||
const multTable = {
|
const multTable = {
|
||||||
|
@ -24,7 +27,8 @@ const multTable = {
|
||||||
|
|
||||||
// TODO: Fill in multipliers for Chunithm and Ongeki
|
// TODO: Fill in multipliers for Chunithm and Ongeki
|
||||||
'chu3': [
|
'chu3': [
|
||||||
[ 100.75, 0, 'SSS' ],
|
[ 100.9, 215, 'SSS+' ],
|
||||||
|
[ 100.75, 200, 'SSS' ],
|
||||||
[ 100.0, 0, 'SS' ],
|
[ 100.0, 0, 'SS' ],
|
||||||
[ 97.5, 0, 'S' ],
|
[ 97.5, 0, 'S' ],
|
||||||
[ 95.0, 0, 'AAA' ],
|
[ 95.0, 0, 'AAA' ],
|
||||||
|
@ -83,3 +87,65 @@ export function roundFloor(achievement: number, game: GameName, digits = 2) {
|
||||||
if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
|
if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
|
||||||
return (+rounded - Math.pow(10, -digits)).toFixed(digits);
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -260,6 +260,8 @@
|
||||||
<RatingComposition title="B35" comp={d.user.ratingComposition.best35} {allMusics} {game}/>
|
<RatingComposition title="B35" comp={d.user.ratingComposition.best35} {allMusics} {game}/>
|
||||||
<RatingComposition title="B15" comp={d.user.ratingComposition.best15} {allMusics} {game}/>
|
<RatingComposition title="B15" comp={d.user.ratingComposition.best15} {allMusics} {game}/>
|
||||||
<RatingComposition title="Recent 10" comp={d.user.ratingComposition.recent10} {allMusics} {game}/>
|
<RatingComposition title="Recent 10" comp={d.user.ratingComposition.recent10} {allMusics} {game}/>
|
||||||
|
<RatingComposition title="N10" comp={d.user.ratingComposition.next10} {allMusics} {game}/>
|
||||||
|
<RatingComposition title="Recent 40" comp={d.user.ratingComposition.recent10} {allMusics} {game}/>
|
||||||
|
|
||||||
<div class="recent">
|
<div class="recent">
|
||||||
<h2>{t('UserHome.RecentScores')}</h2>
|
<h2>{t('UserHome.RecentScores')}</h2>
|
||||||
|
|
Loading…
Reference in New Issue