Files

229 lines
6.7 KiB
JavaScript

/**
* Railtrack Pro - Game Logic
* Main game controller, UI interactions, and game state management
* @author Railtrack Pro Development Team
*/
class Game {
constructor() {
this.renderer = null;
this.world = null;
this.selectedTrackType = 'straight';
this.stats = { total: 0, straight: 0, curved: 0, junction: 0, signal: 0 };
this.init();
}
/**
* Initialize game
*/
init() {
console.log('[Game] Initializing Railtrack Pro...');
// Initialize renderer
this.renderer = new Renderer();
// Initialize world
this.world = new World(this.renderer);
// Setup UI controls
this.setupUIControls();
// Initialize stats display
this.updateStats(0);
// Highlight first track type as active
this.setActiveTrackType('straight');
console.log('[Game] Initialization complete');
}
/**
* Setup UI controls and event listeners
*/
setupUIControls() {
const trackTypes = ['straight', 'curved', 'junction', 'signal'];
trackTypes.forEach(type => {
const button = document.getElementById(`btn-${type}`);
if (button) {
button.addEventListener('click', () => this.setActiveTrackType(type));
}
});
}
/**
* Set active track type for placement
*/
setActiveTrackType(type) {
this.selectedTrackType = type;
// Update button states
document.querySelectorAll('#controls button').forEach(btn => {
btn.classList.remove('active');
});
const activeButton = document.getElementById(`btn-${type}`);
if (activeButton) {
activeButton.classList.add('active');
}
console.log(`[Game] Selected track type: ${type}`);
}
/**
* Handle track click from renderer
*/
static onTrackClick(trackMesh) {
if (trackMesh.userData.piece) {
Game.removeTrack(trackMesh.userData.piece);
}
}
/**
* Handle mouse click on viewport for track placement
*/
static onViewportClick(event) {
// Only handle placement if a track type is selected
if (!Game.instance.selectedTrackType) {
console.log('[Game] No track type selected');
return;
}
// Raycast for placement
const mouse = new THREE.Vector2();
const rect = Game.instance.renderer.renderer.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
Game.instance.renderer.raycaster.setFromCamera(mouse, Game.instance.renderer.camera);
// Create plane for intersection
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const target = new THREE.Vector3();
const intersectPoint = new THREE.Vector3();
Game.instance.renderer.raycaster.ray.intersectPlane(plane, intersectPoint);
if (intersectPoint) {
// Snap to grid
const snappedPosition = Game.instance.world.snapToGrid(intersectPoint);
// Check if placement is valid
if (Game.instance.world.isValidPlacement(snappedPosition)) {
// Calculate rotation based on selected type
const rotation = new THREE.Vector3(0, Math.PI / 2, 0);
// Add track piece to world
Game.instance.world.addTrackPiece(
Game.instance.selectedTrackType,
snappedPosition,
rotation
);
} else {
console.log('[Game] Placement invalid - invalid position');
}
}
}
/**
* Remove track piece
*/
static removeTrack(trackPiece) {
if (Game.instance && Game.instance.world) {
Game.instance.world.removeTrackPiece(trackPiece);
}
}
/**
* Update stats display in info panel
*/
static updateStats(count) {
if (!Game.instance) return;
const stats = Game.instance.world.getStats();
Game.instance.stats = stats;
const statsElement = document.getElementById('stats');
if (statsElement) {
statsElement.innerHTML = `
<h3>📊 Track Statistics</h3>
<p><strong>Total:</strong> ${stats.total}</p>
<p>⬇️ Straight: ${stats.straight}</p>
<p>⬇️ Curved: ${stats.curved}</p>
<p>⬇️ Junction: ${stats.junction}</p>
<p>⬇️ Signal: ${stats.signal}</p>
`;
}
}
/**
* Update selected track info display
*/
static updateSelectedInfo(selectedMesh) {
if (!Game.instance) return;
const infoElement = document.getElementById('selected-info');
if (!selectedMesh) {
infoElement.innerHTML = `
<h4>🔍 Selection</h4>
<p>No track selected</p>
`;
return;
}
const track = selectedMesh.userData.piece;
if (!track) return;
const config = TrackConfig.types[track.type] || { name: 'Unknown' };
infoElement.innerHTML = `
<h4>📦 Track Info</h4>
<p><strong>Type:</strong> ${config.name}</p>
<p><strong>Position:</strong> ${track.position.x.toFixed(1)}, ${track.position.z.toFixed(1)}</p>
<p><strong>Rotation:</strong> ${(track.rotation.y * 180 / Math.PI).toFixed(0)}°</p>
<p><strong>Cost:</strong> $${config.cost || 0}</p>
`;
}
/**
* Get current game instance
*/
static getInstance() {
return this.instance;
}
/**
* Initialize game on DOM ready
*/
initGame() {
Game.instance = new Game();
// Add click handler to viewport
const viewport = document.getElementById('viewport');
if (viewport) {
viewport.addEventListener('click', (event) => {
// Prevent placement if clicking on info panel
if (event.target.closest('#info-panel')) return;
Game.onViewportClick(event);
});
}
console.log('[Game] Game instance created and ready');
}
}
// Global game instance
Game.instance = null;
// Initialize game when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
Game.initGame();
});
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = Game;
}