Initial commit: Retro 90s flight simulator MVP
Loading screen, main menu, 3D flight sim with CRT post-processing, procedural terrain, airport with buildings, low-poly aircraft, flight physics, HUD instruments, and sound. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
export class UI {
|
||||
constructor() {
|
||||
this.loadingScreen = document.getElementById('loading-screen');
|
||||
this.mainMenu = document.getElementById('main-menu');
|
||||
this.pauseMenu = document.getElementById('pause-menu');
|
||||
this.controlsScreen = document.getElementById('controls-screen');
|
||||
this.quitScreen = document.getElementById('quit-screen');
|
||||
this.progressBar = document.getElementById('progress-bar');
|
||||
this.loadingMessage = document.getElementById('loading-message');
|
||||
this.menuItems = document.querySelectorAll('#menu-items .menu-item');
|
||||
this.pauseItems = document.querySelectorAll('#pause-items .menu-item');
|
||||
this.currentMenuItem = 0;
|
||||
this.currentPauseItem = 0;
|
||||
this.onNewFlight = null;
|
||||
this.onResume = null;
|
||||
this.onMainMenu = null;
|
||||
this.onQuit = null;
|
||||
this.menuActive = false;
|
||||
this.controlsActive = false;
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new Promise(r => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
async showLoading(onStep) {
|
||||
const steps = [
|
||||
{ label: 'GENERATING TERRAIN...', duration: 600 },
|
||||
{ label: 'BUILDING AIRPORT...', duration: 500 },
|
||||
{ label: 'LOADING AIRCRAFT...', duration: 400 },
|
||||
{ label: 'CALIBRATING INSTRUMENTS...', duration: 400 },
|
||||
{ label: 'READY', duration: 300 },
|
||||
];
|
||||
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const step = steps[i];
|
||||
this.loadingMessage.textContent = step.label;
|
||||
this.progressBar.style.width = `${((i + 1) / steps.length) * 100}%`;
|
||||
if (onStep) onStep(i);
|
||||
await this.delay(step.duration);
|
||||
}
|
||||
|
||||
this.loadingScreen.style.display = 'none';
|
||||
this.showMainMenu();
|
||||
}
|
||||
|
||||
showMainMenu() {
|
||||
this.mainMenu.style.display = 'flex';
|
||||
this.currentMenuItem = 0;
|
||||
this.updateMenuHighlight();
|
||||
this.menuActive = true;
|
||||
}
|
||||
|
||||
hideMainMenu() {
|
||||
this.mainMenu.style.display = 'none';
|
||||
this.menuActive = false;
|
||||
}
|
||||
|
||||
handleMenuInput(key) {
|
||||
if (!this.menuActive) return false;
|
||||
if (key === 'ArrowUp' || key === 'ArrowDown') {
|
||||
const items = this.menuItems;
|
||||
if (key === 'ArrowUp') {
|
||||
this.currentMenuItem = (this.currentMenuItem - 1 + items.length) % items.length;
|
||||
} else {
|
||||
this.currentMenuItem = (this.currentMenuItem + 1) % items.length;
|
||||
}
|
||||
this.updateMenuHighlight();
|
||||
return true;
|
||||
} else if (key === 'Enter' || key === ' ') {
|
||||
const action = this.menuItems[this.currentMenuItem].dataset.action;
|
||||
if (action === 'new-flight' && this.onNewFlight) this.onNewFlight();
|
||||
else if (action === 'controls') this.showControls();
|
||||
else if (action === 'quit' && this.onQuit) this.onQuit();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
updateMenuHighlight() {
|
||||
this.menuItems.forEach((item, i) => {
|
||||
item.classList.toggle('selected', i === this.currentMenuItem);
|
||||
});
|
||||
}
|
||||
|
||||
showControls() {
|
||||
this.controlsScreen.style.display = 'flex';
|
||||
this.controlsActive = true;
|
||||
}
|
||||
|
||||
hideControls() {
|
||||
this.controlsScreen.style.display = 'none';
|
||||
this.controlsActive = false;
|
||||
}
|
||||
|
||||
handleControlsInput() {
|
||||
if (!this.controlsActive) return false;
|
||||
this.hideControls();
|
||||
return true;
|
||||
}
|
||||
|
||||
showPauseMenu() {
|
||||
this.pauseMenu.style.display = 'flex';
|
||||
this.currentPauseItem = 0;
|
||||
this.updatePauseHighlight();
|
||||
}
|
||||
|
||||
hidePauseMenu() {
|
||||
this.pauseMenu.style.display = 'none';
|
||||
}
|
||||
|
||||
handlePauseInput(key) {
|
||||
if (this.pauseMenu.style.display === 'none') return false;
|
||||
if (key === 'ArrowUp' || key === 'ArrowDown') {
|
||||
const items = this.pauseItems;
|
||||
if (key === 'ArrowUp') {
|
||||
this.currentPauseItem = (this.currentPauseItem - 1 + items.length) % items.length;
|
||||
} else {
|
||||
this.currentPauseItem = (this.currentPauseItem + 1) % items.length;
|
||||
}
|
||||
this.updatePauseHighlight();
|
||||
return true;
|
||||
} else if (key === 'Enter' || key === ' ') {
|
||||
const action = this.pauseItems[this.currentPauseItem].dataset.action;
|
||||
if (action === 'resume' && this.onResume) this.onResume();
|
||||
else if (action === 'main-menu' && this.onMainMenu) this.onMainMenu();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
updatePauseHighlight() {
|
||||
this.pauseItems.forEach((item, i) => {
|
||||
item.classList.toggle('selected', i === this.currentPauseItem);
|
||||
});
|
||||
}
|
||||
|
||||
showQuitScreen() {
|
||||
this.quitScreen.style.display = 'flex';
|
||||
this.mainMenu.style.display = 'none';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user