/** * Railtrack Pro - World Management * Handles grid system, track placement, and world state * @author Railtrack Pro Development Team */ class World { constructor(renderer) { this.renderer = renderer; this.gridSize = 100; this.gridUnit = 1; // Grid unit size this.tracks = []; this.selectedType = null; this.snapDistance = 0.1; this.isPlacementMode = false; this.initGrid(); } /** * Initialize grid system */ initGrid() { this.gridHelper = this.renderer.gridHelper; console.log('[World] Grid system initialized'); } /** * Snap position to nearest grid point */ snapToGrid(position) { const x = Math.round(position.x / this.gridUnit) * this.gridUnit; const z = Math.round(position.z / this.gridUnit) * this.gridUnit; return new THREE.Vector3(x, 0, z); } /** * Check if position is valid for track placement */ isValidPlacement(position) { // Check bounds if (Math.abs(position.x) > this.gridSize || Math.abs(position.z) > this.gridSize) { return false; } // Check collisions with existing tracks const positionSnap = this.snapToGrid(position); const collisionBuffer = this.snapDistance * 2; for (const track of this.tracks) { if (track.mesh === null) continue; const trackPos = track.mesh.position; const distance = positionSnap.distanceTo(trackPos); if (distance < collisionBuffer) { return false; } } return true; } /** * Add track piece to world */ addTrackPiece(type, position, rotation) { if (!this.isValidPlacement(position)) { console.warn('[World] Invalid placement position'); return null; } const track = new TrackPiece(type, position, rotation); track.create(this.renderer.scene); this.tracks.push(track); // Update stats Game.updateStats(this.tracks.length); console.log(`[World] Added ${type} track at ${position.x.toFixed(1)}, ${position.z.toFixed(1)}`); return track; } /** * Remove track piece from world */ removeTrackPiece(trackPiece) { const index = this.tracks.indexOf(trackPiece); if (index > -1) { this.tracks.splice(index, 1); this.renderer.removeTrackPiece(trackPiece.mesh); Game.updateStats(this.tracks.length); Game.updateSelectedInfo(null); console.log(`[World] Removed track piece`); return true; } return false; } /** * Get track piece at position */ getTrackAtPosition(position) { for (const track of this.tracks) { if (track.mesh === null) continue; const distance = position.distanceTo(track.mesh.position); if (distance < this.snapDistance) { return track; } } return null; } /** * Connect tracks at junctions */ connectTracks(track1, track2) { // Simple connection logic // In full implementation, check compatibility and adjust orientation console.log('[World] Connecting tracks'); } /** * Clear all tracks */ clear() { for (const track of this.tracks) { this.removeTrackPiece(track); } this.tracks = []; Game.updateStats(0); console.log('[World] All tracks cleared'); } /** * Get world statistics */ getStats() { const counts = { straight: 0, curved: 0, junction: 0, signal: 0 }; for (const track of this.tracks) { if (counts[track.type] !== undefined) { counts[track.type]++; } } return { total: this.tracks.length, ...counts }; } } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = World; }