diff --git a/common/utils.ts b/common/utils.ts index 71f6c4d..52c7e04 100644 --- a/common/utils.ts +++ b/common/utils.ts @@ -224,14 +224,6 @@ export class Vec2 { public clone() { return new Vec2(this.x(), this.y()); } - - public normalize() { - return new Vec2(clamp(this.x(), -1, 1), clamp(this.y(), -1, 1)); - } -} - -export function clamp(x: number, min: number, max: number) { - return Math.max(min, Math.min(x, max)); } export function v2(x: number, y: number) { diff --git a/server/components/display.ts b/server/components/display.ts index 4eebf70..8f6f350 100644 --- a/server/components/display.ts +++ b/server/components/display.ts @@ -16,8 +16,8 @@ export function sys_render_world(center: Vec2, size: Vec2) { 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).sub(v2(1, 1)); - for (const [pos, display] of engine.all(query_in_rect(min, max).with(CompDisplay))) { - const local_pos = pos.pos.sub(min); + for (const [pos, display] of engine.query_all(query_in_rect(min, max).with(CompDisplay))) { + const local_pos = pos.position.sub(min); result[local_pos.y()][local_pos.x()] = display.display; } return result.map((line) => line.join("")).toReversed().join("\n"); diff --git a/server/components/world.ts b/server/components/world.ts index 013c118..fb0857f 100644 --- a/server/components/world.ts +++ b/server/components/world.ts @@ -5,24 +5,24 @@ import { CompDisplay } from "./display.ts"; const log = log_from(import.meta); export class CompPos { - pos; + position; entity; constructor(entity: Entity, position: Vec2) { - this.pos = position; + this.position = position; this.entity = entity; } move(displacement: Vec2) { - this.pos = this.pos.add(displacement); + this.position = this.position.add(displacement); } move_collide(engine: Engine, displacement: Vec2) { let result = false; - for (const step of displacement_steps(this.pos, displacement)) { - const collider_exists = engine.one(query_at(step)) !== null; + for (const step of displacement_steps(this.position, displacement)) { + const collider_exists = engine.query_one(query_at(step)) !== null; if (collider_exists) return result; - this.pos = step; + this.position = step; result = true; } return result; @@ -61,11 +61,11 @@ Deno.test("test_displacement", () => { }); export function query_at(pos: Vec2) { - return Query.with(CompPos).filter(([c]) => c.pos.overlaps(pos)); + return Query.with(CompPos).filter(([c]) => c.position.overlaps(pos)); } export function query_in_rect(min: Vec2, max: Vec2) { - return Query.with(CompPos).filter(([c]) => c.pos.inside(min, max)); + return Query.with(CompPos).filter(([c]) => c.position.inside(min, max)); } class CompStructure {} @@ -100,7 +100,7 @@ export async function sys_spawn_structure(file_path: string, origin: Vec2) { export function sys_find_free_pos(target: Vec2) { return (engine: Engine) => { for (const pos of spiral(target)) { - const found = engine.one(query_at(pos)); + const found = engine.query_one(query_at(pos)); if (found === null) return pos; } throw new Error("Unreachable."); diff --git a/server/engine.ts b/server/engine.ts index 0768bc7..e3e9758 100644 --- a/server/engine.ts +++ b/server/engine.ts @@ -2,10 +2,12 @@ import { Awaitable, ClassMap, Constructible, first, log_from, Prototyped, wait } const log = log_from(import.meta); export class Entity { + identifier; components; engine; - constructor(engine: Engine) { + constructor(identifier: number, engine: Engine) { + this.identifier = identifier; this.engine = engine; this.components = new ClassMap(); } @@ -26,13 +28,6 @@ export class Entity { } } -export class CompId { - public readonly id; - constructor(id: number) { - this.id = id; - } -} - export class Engine { next_identifier; entities; @@ -48,8 +43,7 @@ export class Engine { spawn(op: (entity: Entity) => unknown) { const identifier = this.next_identifier++; - const entity = new Entity(this); - entity.insert(new CompId(identifier)); + const entity = new Entity(identifier, this); op(entity); this.entities.set(identifier, entity); return entity; @@ -60,19 +54,18 @@ export class Engine { } async global_system_loop(sys: (engine: Engine) => Awaitable, interval: number) { - // TODO : Wait for start. while (true) { await sys(this); await wait(interval); } } - *all(query: Query) { + *query_all(query: Query) { yield* query.run(this.entities.values()); } - one(query: Query) { - return first(this.all(query)); + query_one(query: Query) { + return first(this.query_all(query)); } get(identifier: number) { diff --git a/server/entities/enemy.ts b/server/entities/enemy.ts index 494826d..134b76d 100644 --- a/server/entities/enemy.ts +++ b/server/entities/enemy.ts @@ -1,37 +1,15 @@ -import { assert } from "https://deno.land/std@0.221.0/assert/assert.ts"; -import { v2 } from "../../common/utils.ts"; import { Vec2 } from "../../common/utils.ts"; import { CompDisplay } from "../components/display.ts"; -import { query_in_rect } from "../components/world.ts"; import { CompPos } from "../components/world.ts"; -import { CompId, Query } from "../engine.ts"; import { Engine } from "../engine.ts"; -import { CompPlayer } from "./player.ts"; export class CompEnemy { target; life; - constructor() { this.target = null as number | null; this.life = 10; } - - get_target_pos(engine: Engine) { - if (this.target === null) return null; - const result = engine.one(Query.with(CompId).filter(([c]) => c.id === this.target).with(CompPos)); - if (result === null) return null; - const [_, pos] = result; - return pos.pos; - } - - find_target_from(engine: Engine, pos: Vec2, range: number) { - const radius = v2(range, range); - const found = engine.one(Query.with(CompPlayer).with(CompId).and(query_in_rect(pos.sub(radius), pos.add(radius)))); - if (found === null) return this.target = null; - const [_, id, __] = found; - return this.target = id.id; - } } export function sys_spawn_enemy(pos: Vec2) { @@ -45,27 +23,3 @@ export function sys_spawn_enemy(pos: Vec2) { ); }; } - -export function sys_update_enemy() { - return (engine: Engine) => { - for (const [enemy, enemy_pos] of engine.all(Query.with(CompEnemy).with(CompPos))) { - if (enemy.target === null) enemy.find_target_from(engine, enemy_pos.pos, 3); - if (enemy.target === null) continue; - const pos = enemy.get_target_pos(engine); - assert(pos !== null); - const direction = pos.sub(enemy_pos.pos); - if (direction.len() <= 5) { - const displacement = direction.normalize(); - const moved = enemy_pos.move_collide(engine, displacement); - if (!moved) { - enemy_pos.move_collide(engine, v2(displacement.x(), 0)); - enemy_pos.move_collide(engine, v2(0, displacement.y())); - } - } else enemy.target = null; - } - }; -} - -export function enemy_plugin(engine: Engine) { - engine.global_system_loop(sys_update_enemy(), 500); -} diff --git a/server/entities/player.ts b/server/entities/player.ts index f57a6f8..d08879d 100644 --- a/server/entities/player.ts +++ b/server/entities/player.ts @@ -2,7 +2,7 @@ import { mts } from "../../common/mod.ts"; import { log_from, v2, Vec2 } from "../../common/utils.ts"; import { CompDisplay, sys_render_world } from "../components/display.ts"; import { CompPos, sys_find_free_pos } from "../components/world.ts"; -import { CompId, Engine, Entity } from "../engine.ts"; +import { Engine, Entity } from "../engine.ts"; import { ClientInterface } from "../network.ts"; const log = log_from(import.meta); @@ -32,12 +32,12 @@ export class Session { 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") break; + if (input.kind === "exit") this.engine.delete(this.entity.identifier); } } catch (error) { console.error("Session loop failed, ", error); + this.engine.delete(this.entity.identifier); } - this.engine.delete(this.entity.get_force(CompId).id); } handle_input(input: mts.MsgInput) { @@ -48,7 +48,7 @@ export class Session { } send_display(width: number, height: number) { - const raw = this.engine.run(sys_render_world(this.entity.get_force(CompPos).pos, v2(width, height))); + 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 } }); } @@ -70,7 +70,7 @@ export function sys_spawn_player(position: Vec2) { }; } -export class CompPlayer { +class CompPlayer { life; constructor() { this.life = 10; diff --git a/server/main.ts b/server/main.ts index 1a67098..b848ce7 100755 --- a/server/main.ts +++ b/server/main.ts @@ -6,15 +6,12 @@ import { Session } from "./entities/player.ts"; import { sys_spawn_structure } from "./components/world.ts"; import { Gateway } from "./network.ts"; import { sys_spawn_enemy } from "./entities/enemy.ts"; -import { enemy_plugin } from "./entities/enemy.ts"; const log = log_from(import.meta); async function main() { log("Starting."); const engine = new Engine(); - enemy_plugin(engine); engine.start(); - engine.run(await sys_spawn_structure("../data/structures/houses.txt", v2(2, 2))); engine.run(sys_spawn_enemy(v2(1, 1)));