182 lines
7.0 KiB
TypeScript
182 lines
7.0 KiB
TypeScript
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);
|
|
}
|
|
}
|