pull/13/head
Azalea 2024-02-16 01:46:11 -05:00
parent 6afcb364d1
commit 643e0e0c1f
5 changed files with 271 additions and 273 deletions

View File

@ -1,5 +1,5 @@
export interface TrendEntry {
date: string
rating: number
plays: number
export interface TrendEntry {
date: string
rating: number
plays: number
}

View File

@ -1,51 +1,51 @@
import {aqua_host, data_host} from "./config";
import type {TrendEntry} from "./generalTypes";
import type {MaimaiUserSummaryEntry} from "./maimaiTypes";
const multTable = [
[100.5, 22.4, "SSSp"],
[100, 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"]
]
export function getMult(achievement: number) {
achievement /= 10000
for (let i = 0; i < multTable.length; i++) {
if (achievement >= (multTable[i][0] as number)) return multTable[i]
}
return [0, 0, 0]
}
export async function getMaimai(endpoint: string, params: any) {
return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, {
method: "POST",
body: JSON.stringify(params)
}).then(res => res.json())
}
export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> {
return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json())
}
export async function getMaimaiApi(endpoint: string, params: any) {
let url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`)
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
return await fetch(url).then(res => res.json())
}
export async function getMaimaiTrend(userId: number): Promise<TrendEntry[]> {
return await getMaimaiApi("trend", {userId})
}
export async function getMaimaiUser(userId: number): Promise<MaimaiUserSummaryEntry> {
return await getMaimaiApi("user-summary", {userId})
import { aqua_host, data_host } from './config'
import type { TrendEntry } from './generalTypes'
import type { MaimaiUserSummaryEntry } from './maimaiTypes'
const multTable = [
[ 100.5, 22.4, 'SSSp' ],
[ 100, 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' ]
]
export function getMult(achievement: number) {
achievement /= 10000
for (let i = 0; i < multTable.length; i++) {
if (achievement >= (multTable[i][0] as number)) return multTable[i]
}
return [ 0, 0, 0 ]
}
export async function getMaimai(endpoint: string, params: any) {
return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, {
method: 'POST',
body: JSON.stringify(params)
}).then(res => res.json())
}
export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> {
return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json())
}
export async function getMaimaiApi(endpoint: string, params: any) {
const url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`)
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
return await fetch(url).then(res => res.json())
}
export async function getMaimaiTrend(userId: number): Promise<TrendEntry[]> {
return await getMaimaiApi('trend', { userId })
}
export async function getMaimaiUser(userId: number): Promise<MaimaiUserSummaryEntry> {
return await getMaimaiApi('user-summary', { userId })
}

View File

@ -1,116 +1,116 @@
export interface Rating {
musicId: number
level: number
achievement: number
}
export interface ParsedRating extends Rating {
music: MaimaiMusic,
calc: number,
rank: string
}
export interface MaimaiMusic {
name: string,
composer: string,
bpm: number,
ver: number,
note: {
lv: number
designer: string
lv_id: number
notes: number
}
}
export interface MaimaiUserSummaryEntry {
name: string
iconId: number
serverRank: number
accuracy: number
rating: number
ratingHighest: number
ranks: { name: string, count: number }[]
maxCombo: number
fullCombo: number
allPerfect: number
totalDxScore: number
plays: number
totalPlayTime: number
joined: string
lastSeen: string
lastVersion: string
best35: string
best15: string
recent: MaimaiUserPlaylog[]
}
export interface MaimaiUserPlaylog {
id: number;
musicId: number;
level: number;
userPlayDate: string;
trackNo: number;
vsRank: number;
achievement: number;
deluxscore: number;
scoreRank: number;
maxCombo: number;
totalCombo: number;
maxSync: number;
totalSync: number;
tapCriticalPerfect: number;
tapPerfect: number;
tapGreat: number;
tapGood: number;
tapMiss: number;
holdCriticalPerfect: number;
holdPerfect: number;
holdGreat: number;
holdGood: number;
holdMiss: number;
slideCriticalPerfect: number;
slidePerfect: number;
slideGreat: number;
slideGood: number;
slideMiss: number;
touchCriticalPerfect: number;
touchPerfect: number;
touchGreat: number;
touchGood: number;
touchMiss: number;
breakCriticalPerfect: number;
breakPerfect: number;
breakGreat: number;
breakGood: number;
breakMiss: number;
isTap: boolean;
isHold: boolean;
isSlide: boolean;
isTouch: boolean;
isBreak: boolean;
isCriticalDisp: boolean;
isFastLateDisp: boolean;
fastCount: number;
lateCount: number;
isAchieveNewRecord: boolean;
isDeluxscoreNewRecord: boolean;
comboStatus: number;
syncStatus: number;
isClear: boolean;
beforeRating: number;
afterRating: number;
beforeGrade: number;
afterGrade: number;
afterGradeRank: number;
beforeDeluxRating: number;
afterDeluxRating: number;
isPlayTutorial: boolean;
isEventMode: boolean;
isFreedomMode: boolean;
playMode: number;
isNewFree: boolean;
trialPlayAchievement: number;
extNum1: number;
extNum2: number;
}
export interface Rating {
musicId: number
level: number
achievement: number
}
export interface ParsedRating extends Rating {
music: MaimaiMusic,
calc: number,
rank: string
}
export interface MaimaiMusic {
name: string,
composer: string,
bpm: number,
ver: number,
note: {
lv: number
designer: string
lv_id: number
notes: number
}
}
export interface MaimaiUserSummaryEntry {
name: string
iconId: number
serverRank: number
accuracy: number
rating: number
ratingHighest: number
ranks: { name: string, count: number }[]
maxCombo: number
fullCombo: number
allPerfect: number
totalDxScore: number
plays: number
totalPlayTime: number
joined: string
lastSeen: string
lastVersion: string
best35: string
best15: string
recent: MaimaiUserPlaylog[]
}
export interface MaimaiUserPlaylog {
id: number;
musicId: number;
level: number;
userPlayDate: string;
trackNo: number;
vsRank: number;
achievement: number;
deluxscore: number;
scoreRank: number;
maxCombo: number;
totalCombo: number;
maxSync: number;
totalSync: number;
tapCriticalPerfect: number;
tapPerfect: number;
tapGreat: number;
tapGood: number;
tapMiss: number;
holdCriticalPerfect: number;
holdPerfect: number;
holdGreat: number;
holdGood: number;
holdMiss: number;
slideCriticalPerfect: number;
slidePerfect: number;
slideGreat: number;
slideGood: number;
slideMiss: number;
touchCriticalPerfect: number;
touchPerfect: number;
touchGreat: number;
touchGood: number;
touchMiss: number;
breakCriticalPerfect: number;
breakPerfect: number;
breakGreat: number;
breakGood: number;
breakMiss: number;
isTap: boolean;
isHold: boolean;
isSlide: boolean;
isTouch: boolean;
isBreak: boolean;
isCriticalDisp: boolean;
isFastLateDisp: boolean;
fastCount: number;
lateCount: number;
isAchieveNewRecord: boolean;
isDeluxscoreNewRecord: boolean;
comboStatus: number;
syncStatus: number;
isClear: boolean;
beforeRating: number;
afterRating: number;
beforeGrade: number;
afterGrade: number;
afterGradeRank: number;
beforeDeluxRating: number;
afterDeluxRating: number;
isPlayTutorial: boolean;
isEventMode: boolean;
isFreedomMode: boolean;
playMode: number;
isNewFree: boolean;
trialPlayAchievement: number;
extNum1: number;
extNum2: number;
}

View File

@ -1,101 +1,100 @@
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale, TimeScale, type ChartOptions, type LineOptions,
} from 'chart.js';
import moment from "moment/moment";
// @ts-ignore
import CalHeatmap from "cal-heatmap";
// @ts-ignore
import CalTooltip from 'cal-heatmap/plugins/Tooltip';
import type {Line} from "svelte-chartjs";
export function title(t: string) {
document.title = `AquaNet - ${t}`
}
export function registerChart() {
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
TimeScale
);
}
export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) {
const cal = new CalHeatmap();
return cal.paint({
itemSelector: el,
domain: {
type: 'month',
label: { text: 'MMM', textAlign: 'start', position: 'top' },
},
subDomain: {
type: 'ghDay',
radius: 2, width: 11, height: 11, gutter: 4
},
range: 12,
data: {source: d, x: 'date', y: 'value'},
scale: {
color: {
type: 'linear',
range: ['#14432a', '#4dd05a'],
domain: [0, d.reduce((a, b) => Math.max(a, b.value), 0)]
},
},
date: {start: moment().subtract(1, 'year').add(1, 'month').toDate()},
theme: "dark",
}, [
[CalTooltip, {text: (_: Date, v: number, d: any) =>
`${v ?? "No"} songs played on ${d.format('MMMM D, YYYY')}`}]
]);
}
export const CHARTJS_OPT: ChartOptions<"line"> = {
responsive: true,
maintainAspectRatio: false,
// TODO: Show point on hover
elements: {
point: {
radius: 0
}
},
scales: {
xAxis: {
type: 'time',
display: false
},
y: {
display: false,
}
},
plugins: {
legend: {
display: false
},
tooltip: {
mode: "index",
intersect: false
}
},
}
/**
* Usage: clazz({a: false, b: true}) -> "b"
*
* @param obj HashMap<string, boolean>
*/
export function clazz(obj: { [key: string]: boolean }) {
return Object.keys(obj).filter(k => obj[k]).join(" ")
}
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale, TimeScale, type ChartOptions, type LineOptions,
} from 'chart.js'
import moment from 'moment/moment'
// @ts-expect-error Cal-heatmap does not have proper types
import CalHeatmap from 'cal-heatmap'
// @ts-expect-error Cal-heatmap does not have proper types
import CalTooltip from 'cal-heatmap/plugins/Tooltip'
export function title(t: string) {
document.title = `AquaNet - ${t}`
}
export function registerChart() {
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
TimeScale
)
}
export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) {
const cal = new CalHeatmap()
return cal.paint({
itemSelector: el,
domain: {
type: 'month',
label: { text: 'MMM', textAlign: 'start', position: 'top' },
},
subDomain: {
type: 'ghDay',
radius: 2, width: 11, height: 11, gutter: 4
},
range: 12,
data: { source: d, x: 'date', y: 'value' },
scale: {
color: {
type: 'linear',
range: [ '#14432a', '#4dd05a' ],
domain: [ 0, d.reduce((a, b) => Math.max(a, b.value), 0) ]
},
},
date: { start: moment().subtract(1, 'year').add(1, 'month').toDate() },
theme: 'dark',
}, [
[ CalTooltip, { text: (_: Date, v: number, d: any) =>
`${v ?? 'No'} songs played on ${d.format('MMMM D, YYYY')}` }]
])
}
export const CHARTJS_OPT: ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: false,
// TODO: Show point on hover
elements: {
point: {
radius: 0
}
},
scales: {
xAxis: {
type: 'time',
display: false
},
y: {
display: false,
}
},
plugins: {
legend: {
display: false
},
tooltip: {
mode: 'index',
intersect: false
}
},
}
/**
* Usage: clazz({a: false, b: true}) -> "b"
*
* @param obj HashMap<string, boolean>
*/
export function clazz(obj: { [key: string]: boolean }) {
return Object.keys(obj).filter(k => obj[k]).join(' ')
}

View File

@ -1,7 +1,6 @@
import './app.sass'
import App from './App.svelte'
// @ts-ignore
const app = new App({target: document.getElementById('app')})
const app = new App({ target: document.getElementById('app')! })
export default app