UNPKG

asciitorium

Version:
191 lines (190 loc) 6.58 kB
import { AssetManager } from './AssetManager.js'; import { SoundManager } from './SoundManager.js'; export class GridMovement { constructor(options) { this.previousPosition = null; // Use the provided map asset and player state this.mapState = options.mapAsset; this.playerState = options.player; this.previousPosition = { x: options.player.value.x, y: options.player.value.y }; } // Read-only access methods getPlayerState() { return this.playerState; } getPlayer() { return this.playerState.value; } getMapState() { return this.mapState; } getMapData() { return this.mapState.value?.mapData ?? []; } getLegend() { return this.mapState.value?.legend ?? {}; } isReady() { return this.mapState.value !== null; } // Legend interpretation methods getLegendEntry(char) { return this.getLegend()[char]; } getCharAt(x, y) { const mapData = this.getMapData(); if (y < 0 || y >= mapData.length) return undefined; if (x < 0 || x >= mapData[y].length) return undefined; return mapData[y][x]; } isSolidChar(char) { const entry = this.getLegend()[char]; return entry?.solid ?? false; } isSolid(x, y) { const char = this.getCharAt(x, y); if (char === undefined) return true; // Out of bounds = solid return this.isSolidChar(char); } // Movement methods moveForward() { const player = this.getPlayer(); const { dx, dy } = this.getDirectionVector(player.direction); return this.movePlayerBy(dx, dy, player.direction); } moveBackward() { const player = this.getPlayer(); const oppositeDir = this.getOppositeDirection(player.direction); const { dx, dy } = this.getDirectionVector(oppositeDir); // Move backward but keep facing the same direction return this.movePlayerBy(dx, dy, player.direction); } turnLeft() { const player = this.getPlayer(); const newDirection = this.getNewDirection(player.direction, 'left'); this.playerState.value = { ...player, direction: newDirection, }; } turnRight() { const player = this.getPlayer(); const newDirection = this.getNewDirection(player.direction, 'right'); this.playerState.value = { ...player, direction: newDirection, }; } // Private helper methods getDirectionVector(direction) { switch (direction) { case 'north': return { dx: 0, dy: -1 }; case 'south': return { dx: 0, dy: 1 }; case 'east': return { dx: 2, dy: 0 }; // 2 units for east/west to match map aspect ratio case 'west': return { dx: -2, dy: 0 }; } } getOppositeDirection(direction) { switch (direction) { case 'north': return 'south'; case 'south': return 'north'; case 'east': return 'west'; case 'west': return 'east'; } } getNewDirection(current, turn) { const directions = ['north', 'east', 'south', 'west']; const currentIndex = directions.indexOf(current); const offset = turn === 'left' ? -1 : 1; const newIndex = (currentIndex + offset + 4) % 4; return directions[newIndex]; } movePlayerBy(dx, dy, direction) { const player = this.getPlayer(); const mapData = this.getMapData(); const mapHeight = mapData.length; const mapWidth = mapHeight > 0 ? mapData[0].length : 0; if (mapWidth === 0 || mapHeight === 0) return false; const newX = Math.max(0, Math.min(mapWidth - 1, player.x + dx)); const newY = Math.max(0, Math.min(mapHeight - 1, player.y + dy)); // Check intermediate positions for horizontal movement (2 steps) if (Math.abs(dx) === 2) { const stepX = dx > 0 ? 1 : -1; const midX = player.x + stepX; if (this.isSolid(midX, player.y)) { return false; // Blocked } } // Check final destination if (this.isSolid(newX, newY)) { return false; // Blocked } // Trigger onExit sound for the old tile before moving if (this.previousPosition) { this.checkAndPlayExitSound(this.previousPosition.x, this.previousPosition.y); } // Move successful this.playerState.value = { x: newX, y: newY, direction, }; // Update previous position this.previousPosition = { x: newX, y: newY }; // Trigger onEnter sound for the new tile this.checkAndPlayTileSound(newX, newY); return true; } async checkAndPlayTileSound(x, y) { const char = this.getCharAt(x, y); if (!char) return; const legendEntry = this.getLegendEntry(char); if (!legendEntry) return; try { // Load the material asset to check for sound metadata const materialAsset = await AssetManager.getMaterial(legendEntry.material); // Check for onEnterSound in the material asset if (materialAsset.onEnterSound) { SoundManager.playSound(materialAsset.onEnterSound); } } catch (error) { // Silently ignore errors loading materials or playing sounds console.debug('Could not check tile sound:', error); } } async checkAndPlayExitSound(x, y) { const char = this.getCharAt(x, y); if (!char) return; const legendEntry = this.getLegendEntry(char); if (!legendEntry) return; try { // Load the material asset to check for sound metadata const materialAsset = await AssetManager.getMaterial(legendEntry.material); // Check for onExitSound in the material asset if (materialAsset.onExitSound) { SoundManager.playSound(materialAsset.onExitSound); } } catch (error) { // Silently ignore errors loading materials or playing sounds console.debug('Could not check exit sound:', error); } } }