Add piano-mode help viewer bound to question mark

This commit is contained in:
Jage9
2026-02-23 01:08:50 -05:00
parent 4022a66163
commit 446202288f
5 changed files with 91 additions and 9 deletions

View File

@@ -49,6 +49,7 @@ import {
getDirection,
getNearestItem,
getNearestPeer,
type GameMode,
type WorldItem,
} from './state/gameState';
import {
@@ -188,6 +189,18 @@ type HelpData = {
sections: HelpSection[];
};
/** Builds linearized help-view lines from sectioned help content. */
function buildHelpLines(help: HelpData): string[] {
const lines: string[] = [];
for (const section of help.sections) {
lines.push(section.title);
for (const item of section.items) {
lines.push(`${item.keys}: ${item.description}`);
}
}
return lines;
}
const APP_VERSION = String(window.CHGRID_WEB_VERSION ?? '').trim();
const DISPLAY_TIME_ZONE = resolveDisplayTimeZone();
dom.appVersion.textContent = APP_VERSION
@@ -230,8 +243,11 @@ let internalClipboardText = '';
let replaceTextOnNextType = false;
let pendingEscapeDisconnect = false;
let micGainLoopbackRestoreState: boolean | null = null;
let mainHelpViewerLines: string[] = [];
let pianoHelpViewerLines: string[] = [];
let helpViewerLines: string[] = [];
let helpViewerIndex = 0;
let helpViewerReturnMode: GameMode = 'normal';
let heartbeatTimerId: number | null = null;
let heartbeatNextPingId = -1;
let heartbeatAwaitingPong = false;
@@ -311,6 +327,7 @@ loadAudioLayerState();
loadMicInputGain();
loadMasterVolume();
void loadHelp();
void loadPianoHelp();
void loadChangelog();
/** Fetches a required DOM element and casts it to the requested element type. */
@@ -369,7 +386,7 @@ function setUpdatesExpanded(expanded: boolean): void {
/** Renders help sections into the footer help container and builds linearized viewer lines. */
function renderHelp(help: HelpData): void {
const lines: string[] = [];
const lines = buildHelpLines(help);
dom.instructions.innerHTML = '';
const heading = document.createElement('h2');
heading.textContent = 'Help';
@@ -386,9 +403,9 @@ function renderHelp(help: HelpData): void {
line.appendChild(keys);
line.append(` ${item.description}`);
dom.instructions.appendChild(line);
lines.push(`${item.keys}: ${item.description}`);
}
}
mainHelpViewerLines = lines;
helpViewerLines = lines;
helpViewerIndex = 0;
}
@@ -410,6 +427,23 @@ async function loadHelp(): Promise<void> {
}
}
/** Loads piano-mode help content from `piano.json` for in-mode help viewing. */
async function loadPianoHelp(): Promise<void> {
try {
const response = await fetch(withBase('piano.json'), { cache: 'no-store' });
if (!response.ok) {
return;
}
const help = (await response.json()) as HelpData;
if (!Array.isArray(help.sections) || help.sections.length === 0) {
return;
}
pianoHelpViewerLines = buildHelpLines(help);
} catch {
// Keep piano help unavailable if loading fails.
}
}
/** Renders changelog sections into the collapsible updates panel. */
function renderChangelog(changelog: ChangelogData): void {
dom.updatesPanel.innerHTML = '';
@@ -887,7 +921,7 @@ async function startPianoUseMode(itemId: string): Promise<void> {
activePianoMonophonicKey = null;
state.mode = 'pianoUse';
await audio.ensureContext();
updateStatus(`using ${item.title}, press escape to stop.`);
updateStatus(`using ${item.title}, press question mark for help.`);
audio.sfxUiBlip();
}
@@ -1112,12 +1146,14 @@ function stopRemotePianoNotesForSource(senderId: string, itemId: string): void {
}
/** Enters help-view mode and announces the first help line. */
function openHelpViewer(): void {
if (helpViewerLines.length === 0) {
function openHelpViewer(lines: string[], returnMode: GameMode = 'normal'): void {
if (lines.length === 0) {
updateStatus('Help unavailable.');
audio.sfxUiCancel();
return;
}
helpViewerLines = lines;
helpViewerReturnMode = returnMode;
state.mode = 'helpView';
helpViewerIndex = 0;
updateStatus(helpViewerLines[helpViewerIndex]);
@@ -2236,7 +2272,7 @@ function handleNormalModeInput(code: string, shiftKey: boolean): void {
return;
}
case 'openHelp':
openHelpViewer();
openHelpViewer(mainHelpViewerLines);
return;
case 'openChat':
state.mode = 'chat';
@@ -2305,7 +2341,7 @@ function handleHelpViewModeInput(code: string): void {
return;
}
if (code === 'Escape') {
state.mode = 'normal';
state.mode = helpViewerReturnMode;
updateStatus('Closed help.');
audio.sfxUiCancel();
}
@@ -2406,6 +2442,10 @@ function handlePianoUseModeInput(code: string): void {
stopPianoUseMode(true);
return;
}
if (code === 'Slash') {
openHelpViewer(pianoHelpViewerLines, 'pianoUse');
return;
}
const itemId = activePianoItemId;
if (!itemId) {
state.mode = 'normal';