import { mts } from "../../common/mod.ts"; import { range } from "../../common/utils.ts"; import { log_from, v2, Vec2 } from "../../common/utils.ts"; import { CompDisplay } from "../components/display.ts"; import { sys_find_free_pos } from "../components/world.ts"; import { CompPos, query_in_rect } from "../components/world.ts"; import { Engine, Entity } from "../engine.ts"; import { ClientInterface } from "../network.ts"; const log = log_from(import.meta); export class Session { client; engine; entity; constructor(client: ClientInterface, engine: Engine, entity: Entity) { this.client = client; this.engine = engine; this.entity = entity; } static init(client: ClientInterface, engine: Engine) { const spawn_pos = engine.run(sys_find_free_pos(v2(0, 0))); const entity = engine.run(sys_spawn_player(spawn_pos)); const self = new Session(client, engine, entity); return self; } async start() { try { for await (const input of this.client.inputs.iter()) { if (input.kind !== "request_display") log("Received", input); if (input.kind === "ping") this.client.outputs.send({ kind: "ping_response", content: input.content }); if (input.kind === "request_display") this.send_display(input.content.width, input.content.height); if (input.kind === "input") this.handle_input(input); if (input.kind === "exit") this.engine.delete(this.entity.identifier); } } catch (error) { console.error("Session loop failed, ", error); this.engine.delete(this.entity.identifier); } } handle_input(input: mts.MsgInput) { if (input.content.control === "up") this.move(v2(0, 1)); if (input.content.control === "down") this.move(v2(0, -1)); if (input.content.control === "left") this.move(v2(-1, 0)); if (input.content.control === "right") this.move(v2(1, 0)); } send_display(width: number, height: number) { const raw = this.engine.run(sys_render_world(this.entity.get_force(CompPos).position, v2(width, height))); this.client.outputs.send({ kind: "display", content: { raw } }); } move(direction: Vec2) { const pos = this.entity.get_force(CompPos); pos.move_collide(this.engine, direction); } } export function sys_render_world(center: Vec2, size: Vec2) { return (engine: Engine) => { const radius = size.scale(0.5); const result = Array.from(range(0, size.y())).map(() => Array.from(range(0, size.x())).map(() => " ")); const min = center.sub(radius); const max = center.add(radius); for (const entity of engine.query_all(query_in_rect(min, max).with(CompDisplay))) { const local_pos = entity.get_force(CompPos).position.sub(min); result[local_pos.y()][local_pos.x()] = entity.get_force(CompDisplay).display; } return result.map((line) => line.join("")).toReversed().join("\n"); }; } export function sys_spawn_player(position: Vec2) { return (engine: Engine) => { return engine.spawn((entity) => entity.insert( new CompPlayer(), new CompPos(entity, position), new CompDisplay("`/"), ) ); }; } class CompPlayer { life; constructor() { this.life = 10; } }