d3/server/components/world.ts

108 lines
2.8 KiB
TypeScript

import { assertEquals } from "https://deno.land/std@0.221.0/assert/mod.ts";
import { chunks, enumerate, log_from, spiral, Vec2 } from "../../common/utils.ts";
import { Engine, Entity, Query } from "../engine.ts";
import { CompDisplay } from "./display.ts";
const log = log_from(import.meta);
export class CompPos {
pos;
entity;
constructor(entity: Entity, position: Vec2) {
this.pos = position;
this.entity = entity;
}
move(displacement: Vec2) {
this.pos = this.pos.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;
if (collider_exists) return result;
this.pos = step;
result = true;
}
return result;
}
}
function* displacement_steps(position: Vec2, displacement: Vec2) {
let pos = position;
let left = displacement;
while (left.len() >= 1) {
if (left.x() >= 1) {
pos = pos.add(new Vec2(1, 0));
left = left.sub(new Vec2(1, 0));
}
if (left.x() <= -1) {
pos = pos.add(new Vec2(-1, 0));
left = left.sub(new Vec2(-1, 0));
}
if (left.y() >= 1) {
pos = pos.add(new Vec2(0, 1));
left = left.sub(new Vec2(0, 1));
}
if (left.y() <= -1) {
pos = pos.add(new Vec2(0, -1));
left = left.sub(new Vec2(0, -1));
}
yield pos;
}
}
Deno.test("test_displacement", () => {
assertEquals(
[...displacement_steps(new Vec2(1, 1), new Vec2(4, 6))],
[new Vec2(2, 2), new Vec2(3, 3), new Vec2(4, 4), new Vec2(5, 5), new Vec2(5, 6), new Vec2(5, 7)],
);
});
export function query_at(pos: Vec2) {
return Query.with(CompPos).filter(([c]) => c.pos.overlaps(pos));
}
export function query_in_rect(min: Vec2, max: Vec2) {
return Query.with(CompPos).filter(([c]) => c.pos.inside(min, max));
}
class CompStructure {}
export function sys_spawn_obstacle(pos: Vec2, display: string) {
return (engine: Engine) =>
engine.spawn((entity) =>
entity.insert(
new CompStructure(),
new CompDisplay(display),
new CompPos(entity, pos),
)
);
}
export async function sys_spawn_structure(file_path: string, origin: Vec2) {
const content = await Deno.readTextFile(file_path);
return (engine: Engine) => {
let count = 0;
for (const [y, line] of enumerate(content.split("\n"))) {
for (const [x, chars] of enumerate(chunks(line, 2))) {
const display = chars[0] + chars[1];
if (display === " ") continue;
engine.run(sys_spawn_obstacle(origin.add(new Vec2(x, y)), display));
count += 1;
}
}
log("Spawned", count, "tiles from", file_path);
};
}
export function sys_find_free_pos(target: Vec2) {
return (engine: Engine) => {
for (const pos of spiral(target)) {
const found = engine.one(query_at(pos));
if (found === null) return pos;
}
throw new Error("Unreachable.");
};
}