import { Color, TextDisplay } from '@common/display/text'; import Input from '@common/input'; import { Inventory } from '@common/rpg/components/inventory'; import { Item } from '@common/rpg/components/item'; import { Position } from '@common/rpg/components/position'; import { World } from '@common/rpg/core/world'; import { TextDisplaySystem } from '@common/rpg/systems/render/text'; import { nextFrame } from '@common/utils'; import './assets/style.css'; import { Direction, getOppositeDirection, MAP_HEIGHT, MAP_WIDTH, MAP_X, MAP_Y, ROOM_AREA_HEIGHT, ROOM_AREA_WIDTH, ROOM_AREA_X, ROOM_AREA_Y } from './const'; import { getPossibleRoomsCount, getRoom, getRoomsCount, getRoomsForLayer, getMapRoomChar, Room, Door } from './room'; import { createPlayer, Player } from './player'; export default async function main() { const world = new World(); const display = new TextDisplay(); world.addSystem(new TextDisplaySystem(display)); display.drawTextInBox(8, 11, 'Use arrow keys to move\nSHIFT to run\nSPACE to activate\nAWOO!', { fg: Color.CYAN }); let roomEntity = getRoom(world, 0, 0, 0); let room = roomEntity?.get(Room); if (!room) { throw new Error('No room found'); } room.onEnter(); const playerEntity = createPlayer(world, (room.x + room.width / 2) | 0, (room.y + room.height / 2) | 0); const player = playerEntity.get(Player); if (!player) { throw new Error('No player found'); } function drawRoom() { if (!room) return; display.drawBox(ROOM_AREA_X, ROOM_AREA_Y, ROOM_AREA_WIDTH - 2, ROOM_AREA_HEIGHT - 2, { fill: ' ' }); display.drawBox(room.x, room.y, room.width, room.height, { fill: ['.', Color.GRAY] }); } function drawMap() { display.drawBox(MAP_X, MAP_Y, MAP_WIDTH - 2, MAP_HEIGHT - 2, { fill: [' '], title: 'Map' }); const worldPos = roomEntity.get(Position, 'world'); const centerX = worldPos ? worldPos.state.x - MAP_X - MAP_WIDTH / 2 : 0; const centerY = worldPos ? worldPos.state.y - MAP_Y - MAP_HEIGHT / 2 : 0; const worldZ = worldPos ? worldPos.state.z : 0; for (const room of getRoomsForLayer(world, worldZ)) { const roomPos = room.get(Position, 'world'); const worldX = roomPos ? roomPos.state.x : 0; const worldY = roomPos ? roomPos.state.y : 0; const x = Math.round(worldX - centerX); const y = Math.round(worldY - centerY); if (x > MAP_X && x < MAP_X + MAP_WIDTH - 1 && y > MAP_Y && y < MAP_Y + MAP_HEIGHT - 1) { const char = getMapRoomChar(room); display.setChar(x, y, [char, room === roomEntity ? Color.YELLOW : Color.WHITE]); } } } function drawInfo() { const worldPos = roomEntity.get(Position, 'world'); const coords = worldPos ? `${worldPos.state.x},${worldPos.state.y},${worldPos.state.z}`.padStart(9) : '0,0,0'; const foundRooms = getRoomsCount(world); const totalRooms = getPossibleRoomsCount(world); const rooms = `${foundRooms}/${totalRooms}${foundRooms === totalRooms ? '' : '+'}`.padStart(coords.length - 2); const inv = playerEntity.get(Inventory); const foundItems = inv ? Object.values(inv.state.slots).length : 0; const totalItems = Array.from(world.query(Item)).length; const items = `${foundItems}/${totalItems}`.padStart(coords.length - 2); display.drawTextInBox(0, 0, `Pos: ${coords}\nRooms: ${rooms}\nItems: ${items}`, { fg: Color.YELLOW, title: 'Info' }); } let lastMove = Date.now(); function handleInput() { if (!room || !player) return; Input.updateKeys(); const isSpacePressed = Input.isPressed(Input.KeyCode.SPACE); if (Date.now() - lastMove < 75 && !Input.isHeld(Input.KeyCode.SHIFT) && !isSpacePressed) return; lastMove = Date.now(); let newX = player.x; let newY = player.y; let moved = isSpacePressed; if (Input.isHeld(Input.KeyCode.UP)) { newY--; moved = true; } if (Input.isHeld(Input.KeyCode.DOWN)) { newY++; moved = true; } if (Input.isHeld(Input.KeyCode.LEFT)) { newX--; moved = true; } if (Input.isHeld(Input.KeyCode.RIGHT)) { newX++; moved = true; } if (moved) { const activatedDoor = room.getActivatedDoor(newX, newY); if (activatedDoor) { const shouldTravel = ![Direction.UP, Direction.DOWN].includes(activatedDoor.direction) || isSpacePressed; if (shouldTravel) { room.onLeave(); roomEntity = getRoom(world, activatedDoor.worldX, activatedDoor.worldY, activatedDoor.worldZ); room = roomEntity.get(Room); if (!room) return; room.onEnter(); const oppositeDoor = room.getDoor(getOppositeDirection(activatedDoor.direction)); if (oppositeDoor) { switch (activatedDoor.direction) { case Direction.NORTH: newX = oppositeDoor.x + 1; newY = oppositeDoor.y - 1; break; case Direction.SOUTH: newX = oppositeDoor.x + 1; newY = oppositeDoor.y + 1; break; case Direction.EAST: newX = oppositeDoor.x + 1; newY = oppositeDoor.y + 1; break; case Direction.WEST: newX = oppositeDoor.x - 1; newY = oppositeDoor.y + 1; break; case Direction.UP: case Direction.DOWN: newX = oppositeDoor.x; newY = oppositeDoor.y; break; } } else { newX = room.x + room.width / 2; newY = room.y + room.height / 2; } } } else { if (newX < room.x + 1 || newX >= room.x + room.width + 1) { newX = player.x; } if (newY < room.y + 1 || newY >= room.y + room.height + 1) { newY = player.y; } } // const pickedItem = room.pickItem(newX, newY); // if (pickedItem) { // player.addItem(pickedItem); // } player.x = newX; player.y = newY; } } while (true) { const dt = await nextFrame(); handleInput(); drawRoom(); drawMap(); drawInfo(); world.update(dt); } }