diff options
| -rw-r--r-- | src/app/game/game.utils.ts | 4 | ||||
| -rw-r--r-- | src/app/game/page.styles.ts | 14 | ||||
| -rw-r--r-- | src/app/game/page.tsx | 34 |
3 files changed, 40 insertions, 12 deletions
diff --git a/src/app/game/game.utils.ts b/src/app/game/game.utils.ts index 6177d2c..4c99c55 100644 --- a/src/app/game/game.utils.ts +++ b/src/app/game/game.utils.ts @@ -42,6 +42,10 @@ export function parseLrcLines(lrcText: string): GameLine[] { return result; } +export function calculateCPSNeeded(text: string, seconds: number): number { + return text.length / seconds; +} + export function formatTime(ms: number): string { const s = Math.max(0, Math.floor(ms / 1000)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; diff --git a/src/app/game/page.styles.ts b/src/app/game/page.styles.ts index e8a2678..da45d8a 100644 --- a/src/app/game/page.styles.ts +++ b/src/app/game/page.styles.ts @@ -149,6 +149,7 @@ export const UpcomingText = styled.p` font-size: 20px; color: rgba(255, 255, 255, 0.30); font-weight: 400; + font-family: "Inter", "Segoe UI", "Helvetica Neue", Arial, sans-serif; min-height: 28px; white-space: nowrap; overflow: hidden; @@ -164,11 +165,18 @@ export const CurrentWrap = styled.div` gap: 10px; `; +export const LineTimingRow = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +`; + export const LineTimingMeta = styled.div` display: flex; align-items: center; - justify-content: flex-end; - font-size: 11px; + justify-content: flex-start; + font-size: 13px; letter-spacing: 1px; text-transform: uppercase; color: rgba(255, 255, 255, 0.45); @@ -219,7 +227,7 @@ export const CharBox = styled.span<{ justify-content: center; font-size: 36px; font-weight: 700; - font-family: monospace; + font-family: "Inter", "Segoe UI", "Helvetica Neue", Arial, sans-serif; padding: 0 3px; border-radius: 4px; transition: all 0.08s ease; diff --git a/src/app/game/page.tsx b/src/app/game/page.tsx index 7e6e9c3..aa953a7 100644 --- a/src/app/game/page.tsx +++ b/src/app/game/page.tsx @@ -30,6 +30,7 @@ import { CurrentWrap, LineTimingMeta, LineTimingValue, + LineTimingRow, LineTimingBar, LineTimingFill, CharRow, @@ -66,7 +67,7 @@ import { HomeBtn, } from "./page.styles"; import { gReducer, initialGState } from "./game.stat"; -import { formatTime, parseLrcLines } from "./game.utils"; +import { formatTime, parseLrcLines, calculateCPSNeeded } from "./game.utils"; type GamePhase = "idle" | "countdown" | "playing" | "paused" | "finished"; @@ -82,6 +83,7 @@ function GameInner() { const [currentMs, setCurrentMs] = useState(0); const [lineTimingPct, setLineTimingPct] = useState(0); const [lineRemainingMs, setLineRemainingMs] = useState(0); + const [currentLineTime, setCurrentLineTime] = useState(0); const [duration, setDuration] = useState(0); const [progressPct, setProgressPct] = useState(0); const [gameDurationMs, setGameDurationMs] = useState(0); @@ -169,6 +171,7 @@ function GameInner() { lineAnimRef.current = { startMs: 0, endMs: 0, startPerf: 0 }; setLineTimingPct(0); setLineRemainingMs(0); + setCurrentLineTime(-1); return; } const start = gameLines[idx].millisecond; @@ -179,7 +182,9 @@ function GameInner() { startPerf: performance.now(), }; setLineTimingPct(0); - setLineRemainingMs(Math.max(0, end - start)); + const currentLineTime = end - start; + setLineRemainingMs(Math.max(0, currentLineTime)); + setCurrentLineTime(Math.max(currentLineTime, currentLineTime)); }, [g.displayedLineIdx, gameLines]); useEffect(() => { @@ -561,12 +566,23 @@ function GameInner() { </UpcomingText> </UpcomingWrap> <CurrentWrap style={{ position: "relative" }}> - <LineTimingMeta> - Time left:{" "} - <LineTimingValue> - {Math.max(0, lineRemainingMs / 1000).toFixed(1)}s - </LineTimingValue> - </LineTimingMeta> + <LineTimingRow> + <LineTimingMeta> + Time left:{" "} + <LineTimingValue> + {Math.max(0, lineRemainingMs / 1000).toFixed(1)}s + </LineTimingValue> + </LineTimingMeta> + <LineTimingMeta> + Estimated CPS:{" "} + <LineTimingValue> + {calculateCPSNeeded( + gameLines[g.displayedLineIdx].content, + currentLineTime / 1000 + ).toFixed(1)} + </LineTimingValue> + </LineTimingMeta> + </LineTimingRow> <LineTimingBar> <LineTimingFill $pct={lineTimingPct} /> </LineTimingBar> @@ -625,7 +641,7 @@ function GameInner() { </CharRow> {clearShowing && <ClearToast>CLEAR!</ClearToast>} <CompletedLineFade> - {g.lineCompleted ? "Cleared - waiting for next line..." : ""} + {g.lineCompleted ? "Cleared - waiting for next line..." : gameLines[g.displayedLineIdx].content} </CompletedLineFade> </CurrentWrap> </> |
