diff --git a/.env.development b/.env.development index 85eb71d..765430b 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,4 @@ -VITE_APP_VERSION="3.0.36" +VITE_APP_VERSION="3.0.37" VITE_API_URL="http://localhost:8000/" VITE_API_KEY="your_api_key_should_be_here" VITE_ASSET_PATH="/assets" diff --git a/.env.production b/.env.production index f63e129..e04c35f 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,4 @@ -VITE_APP_VERSION="3.0.36" +VITE_APP_VERSION="3.0.37" VITE_API_URL="https://restfulsleep.phaseii.network" VITE_API_KEY="your_api_key_should_be_here" VITE_ASSET_PATH="https://cdn.phaseii.network/file/PhaseII/web-assets" diff --git a/public/data-sources/changelog.json b/public/data-sources/changelog.json index d43ab46..0bd2715 100644 --- a/public/data-sources/changelog.json +++ b/public/data-sources/changelog.json @@ -35,5 +35,6 @@ "3.0.33": ["- (Major) Condense all score tables to one parser and format", "- (Major) Add more game metadata", "- (Bugfix) Fix difficulties showing as `NaN`", "- (Beta) Add a developmental device plugin for card effect"], "3.0.34": ["- (Major) Add support for Jubeat titles", "- (Major) Add base BTA support"], "3.0.35": ["- (Bugfix) Fix cursor hover state on news card and profile card", "- (Minor) Update DDR game options", "- (Minor) Update IIDX event settings", "- (Minor) Add IIDX 32 basic QPro settings"], - "3.0.36": ["- (Major) Finish more game metadata (DRS, Museca, Nostalgia, pop'n, jubeat)"] + "3.0.36": ["- (Major) Finish more game metadata (DRS, Museca, Nostalgia, pop'n, jubeat)"], + "3.0.37": ["- (Major) Finish more game metadata (RB, SDVX, FTT)", "- (Bugfix) Fix personal record button showing for games without scores."] } \ No newline at end of file diff --git a/src/components/Cards/GameHeader.vue b/src/components/Cards/GameHeader.vue index a0cc261..c04f638 100644 --- a/src/components/Cards/GameHeader.vue +++ b/src/components/Cards/GameHeader.vue @@ -82,15 +82,15 @@ function loadRoutes() { path: `/games/${props.game.id}/scores/${mainStore.userId}`, color: "info", }); - } - navigationData.push({ - label: "My Records", - icon: PhListStar, - path: `/games/${props.game.id}/records/${mainStore.userId}`, - route: "personal_records", - color: "success", - }); + navigationData.push({ + label: "My Records", + icon: PhListStar, + path: `/games/${props.game.id}/records/${mainStore.userId}`, + route: "personal_records", + color: "success", + }); + } } if (!props.game.noScores) { diff --git a/src/constants/index.js b/src/constants/index.js index f961d61..3878cdf 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -362,6 +362,7 @@ export const gameData = [ noRivals: true, useUnicode: true, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Clear Gauge", value: "data.clear_gauge", width: 120 }, { text: "Combos", value: "data.combo", width: 120 }, { text: "Medal", value: "data.medal", width: 120 }, @@ -444,6 +445,7 @@ export const gameData = [ cardBG: null, noRivals: true, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 120 }, { text: "Halo", value: "data.param", width: 120 }, ], @@ -490,6 +492,7 @@ export const gameData = [ VersionConstants.DDR_A3, ], scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Halo", value: "data.halo", width: 80 }, ], @@ -700,6 +703,7 @@ export const gameData = [ useActiveRival: true, videoTable: [VersionConstants.DDR_A20_PLUS], scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Halo", value: "data.halo", width: 80 }, ], @@ -842,6 +846,7 @@ export const gameData = [ }, ], scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "medal", width: 100 }, { text: "SKILL %", value: "data.skill_perc", width: 90 }, @@ -1010,6 +1015,7 @@ export const gameData = [ }, ], scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "medal", width: 140 }, { text: "SKILL %", value: "data.perc", width: 90 }, @@ -1061,6 +1067,7 @@ export const gameData = [ }, ], scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "medal", width: 140 }, { text: "SKILL %", value: "data.perc", width: 90 }, @@ -1096,6 +1103,7 @@ export const gameData = [ cardBG: null, gameOptions: GFDMOptions, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "medal", width: 100 }, { text: "SKILL %", value: "data.skill_perc", width: 90 }, @@ -1232,6 +1240,7 @@ export const gameData = [ assetId: "iidx", gameOptions: IIDXOptions, splitRivals: true, + maxLength: 6, videoTable: [ VersionConstants.IIDX_TRICORO, VersionConstants.IIDX_SPADA, @@ -1248,7 +1257,8 @@ export const gameData = [ VersionConstants.IIDX_PINKY_CRUSH, ], scoreHeaders: [ - { text: "Misses", value: "data.misses", width: 100 }, + { text: "Grade", value: "data.rank", width: 80 }, + { text: "Misses", value: "data.miss_count", width: 100 }, { text: "Clear Status", value: "data.medal", width: 100 }, ], chartTable: { @@ -1264,14 +1274,24 @@ export const gameData = [ 9: "DP LEGGENDARIA", }, rankTable: { - 100: "E", - 200: "D", - 300: "C", - 400: "B", - 500: "A", - 600: "S", - 700: "SS", - 800: "EXC", + 100: "F", + 200: "E", + 300: "D", + 400: "C", + 500: "B", + 600: "A", + 700: "AA", + 800: "AAA", + }, + percTable: { + 0: 100, + 2222: 200, + 3333: 300, + 4444: 400, + 5556: 500, + 6667: 600, + 7778: 700, + 8889: 800, }, medalTable: { 50: "NO PLAY", @@ -1554,6 +1574,7 @@ export const gameData = [ icon: null, cardBG: null, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "data.medal", width: 120 }, { text: "Music Rate", value: "data.music_rate", width: 120 }, @@ -1732,6 +1753,7 @@ export const gameData = [ 300: "FULL COMBO", }, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "data.clear_type", width: 80 }, ], @@ -1871,6 +1893,7 @@ export const gameData = [ 1100: "PERFECT", }, scoreHeaders: [ + { text: "Grade", value: "data.rank", width: 80 }, { text: "Combos", value: "data.combo", width: 80 }, { text: "Medal", value: "data.medal", width: 120 }, ], @@ -2022,10 +2045,43 @@ export const gameData = [ 2: "HARD", 3: "SPECIAL", }, + rankTable: { + 100: "C", + 200: "B", + 300: "A", + 400: "AA", + 500: "AAA", + 600: "AAA+", + 700: "S", + }, + percTable: { + 0: 100, + 6000: 200, + 7000: 300, + 8000: 400, + 9000: 500, + 9500: 600, + 9800: 700, + }, + medalTable: { + 100: "NO PLAY", + 200: "FAILED", + 300: "CLEARED", + 400: "HARD CLEAR", + 500: "S HARD CLEAR", + }, + comboTable: { + 100: "NONE", + 200: "ALMOST FULL COMBO", + 300: "FULL COMBO", + 400: "ALL JUST FULL COMBO", + }, scoreHeaders: [ - { text: "Combos", value: "combo", width: 80 }, - { text: "Misses", value: "misses", width: 80 }, - { text: "Halo", value: "halo", width: 80 }, + { text: "Grade", value: "data.rank", width: 80 }, + { text: "Rate", value: "data.achievement_rate", width: 100 }, + { text: "Misses", value: "data.miss_count", width: 80 }, + { text: "Medal", value: "data.clear_type", width: 80 }, + { text: "Combo Medal", value: "data.combo_type", width: 120 }, ], versions: [ { @@ -2084,9 +2140,31 @@ export const gameData = [ 3: "INFINITE", 4: "MAXIMUM", }, + rankTable: { + 100: "NO PLAY", + 200: "D", + 300: "C", + 400: "B", + 500: "A", + 550: "A+", + 600: "AA", + 650: "AA+", + 700: "AAA", + 800: "AAA+", + 900: "S", + }, + medalTable: { + 50: "NO PLAY", + 100: "FAILED", + 200: "CLEARED", + 300: "HARD CLEAR", + 400: "UC", + 500: "PUC", + }, scoreHeaders: [ - { text: "Combos", value: "combo", width: 80 }, - { text: "Halo", value: "halo", width: 80 }, + { text: "Grade", value: "data.rank", width: 80 }, + { text: "Combos", value: "data.combo", width: 80 }, + { text: "Medal", value: "data.clear_type", width: 80 }, ], playerHeaders: [ { text: "Skill Level", value: "skill_level", sortable: true, width: 100 }, diff --git a/src/constants/scoreDataFilters.js b/src/constants/scoreDataFilters.js index 0a28ec9..0d44951 100644 --- a/src/constants/scoreDataFilters.js +++ b/src/constants/scoreDataFilters.js @@ -7,7 +7,7 @@ export function shouldRenderChart(difficulty, chartTable, chartKey) { export function formatDifficulty(difficulty, difficultyDenom = 1) { if (isNaN(difficulty / difficultyDenom)) { - return difficulty.difnum ? difficulty.difnum : difficulty; + return difficulty?.difnum ? difficulty.difnum : difficulty; } return difficulty / difficultyDenom; } diff --git a/src/constants/table/scores.js b/src/constants/table/scores.js index 5243534..62072be 100644 --- a/src/constants/table/scores.js +++ b/src/constants/table/scores.js @@ -4,13 +4,12 @@ import { formatDifficulty, rankFromScore } from "@/constants/scoreDataFilters"; export function scoreHeaders(thisGame) { const headers = [ { text: "Player", value: "username", width: 120 }, - { text: "New PB", value: "newRecord", width: 100 }, - { text: "Timestamp", value: "timestamp", width: 140 }, + { text: "New PB", value: "newRecord", width: 80 }, + { text: "Timestamp", value: "timestamp", width: 120 }, { text: "Song", value: "song.name", width: 180 }, { text: "Artist", value: "song.artist", width: 150 }, { text: "Chart", value: "song.chart", width: 100 }, - { text: "Grade", value: "data.rank", width: 80 }, - { text: "Score", value: "points", width: 120 }, + { text: "Score", value: "points", width: 100 }, ]; if (thisGame.scoreHeaders) { @@ -24,13 +23,12 @@ export function scoreHeaders(thisGame) { export function personalScoreHeaders(thisGame) { const headers = [ - { text: "Timestamp", value: "timestamp", width: 140 }, - { text: "New PB", value: "newRecord", width: 100 }, + { text: "Timestamp", value: "timestamp", width: 120 }, + { text: "New PB", value: "newRecord", width: 80 }, { text: "Song", value: "song.name", width: 180 }, { text: "Artist", value: "song.artist", width: 180 }, { text: "Chart", value: "song.chart", width: 120 }, - { text: "Grade", value: "data.rank", width: 80 }, - { text: "Score", value: "points", width: 120 }, + { text: "Score", value: "points", width: 100 }, ]; if (thisGame.scoreHeaders) { @@ -42,6 +40,27 @@ export function personalScoreHeaders(thisGame) { return headers; } +export function topScoreHeaders(thisGame) { + const headers = [ + { text: "Player", value: "username", width: 120, sortable: true }, + { text: "Timestamp", value: "timestamp", width: 140, sortable: true }, + { text: "Score", value: "points", width: 100, sortable: true }, + ]; + + if (thisGame.scoreHeaders) { + for (var header of thisGame.scoreHeaders) { + header.sortable = true; + headers.push(header); + } + } + + return headers; +} + +export function formatScoreData(thisGame, data) { + return data; +} + export function formatScoreTable(thisGame, scores) { var formattedItems = []; for (var item of scores) { @@ -78,10 +97,21 @@ export function formatScoreTable(thisGame, scores) { item.data.clear_type = thisGame.medalTable[item.data?.clear_type]; } + if (item.data?.combo_type != undefined && thisGame.comboTable) { + item.data.combo_type = thisGame.comboTable[item.data?.combo_type]; + } + if (!item.data?.rank && thisGame.ratingTable) { item.data.rank = rankFromScore(thisGame.ratingTable, item.points); } + if (!item.data?.rank && thisGame.percTable) { + item.data.rank = rankFromScore( + thisGame.percTable, + item.data?.achievement_rate, + ); + } + if (item.data?.rank != undefined && thisGame.rankTable) { item.data.rank = thisGame.rankTable[item.data?.rank]; } @@ -118,6 +148,10 @@ export function formatScoreTable(thisGame, scores) { item.data.music_rate = item.data?.music_rate / 10; } + if (item.data?.achievement_rate) { + item.data.achievement_rate = `${item.data?.achievement_rate / 100}%`; + } + if (item.data?.excellent) { item.medal = "EX FC"; } else if (item.data?.fullcombo) { @@ -136,6 +170,10 @@ export function formatScoreTable(thisGame, scores) { item.data.combo = 0; } + if (item.data?.miss_count === -1) { + item.data.miss_count = 0; + } + if (item.points != undefined) { item.points = item.points .toString() diff --git a/src/views/Game/SongView.vue b/src/views/Game/SongView.vue index 26c9a38..704706a 100644 --- a/src/views/Game/SongView.vue +++ b/src/views/Game/SongView.vue @@ -15,7 +15,11 @@ import GameHeader from "@/components/Cards/GameHeader.vue"; import { APIGetTopScore } from "@/stores/api/music"; import { getGameInfo } from "@/constants"; import { formatDifficulty } from "@/constants/scoreDataFilters"; -import { formatScoreTable } from "@/constants/table/scores"; +import { + topScoreHeaders, + formatScoreData, + formatScoreTable, +} from "@/constants/table/scores"; const $route = useRoute(); const $router = useRouter(); var gameId = $route.params.game; @@ -35,25 +39,12 @@ if (!thisGame) { onMounted(async () => { try { const data = await APIGetTopScore(gameId, songId); - songData.value = data; + songData.value = formatScoreData(thisGame, data); } catch (error) { console.error("Failed to fetch score data:", error); } }); -const headers = [ - { text: "Player", value: "username", width: 120, sortable: true }, - { text: "Timestamp", value: "timestamp", width: 140, sortable: true }, - { text: "Grade", value: "data.rank", width: 80, sortable: true }, - { text: "Score", value: "points", width: 120, sortable: true }, -]; - -if (thisGame.scoreHeaders) { - for (var header of thisGame.scoreHeaders) { - headers.push(header); - } -} - const chartSelector = reactive({ currentChart: 0, }); @@ -163,7 +154,7 @@ const navigateToProfile = (item) => { >