diff --git a/src/game/game.ts b/src/game/game.ts index 1640495..8310e4c 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -10,6 +10,7 @@ export default class Game { private world; private ui; private prevWorldPos: Point | null = null; + private prevFrame: number = performance.now(); constructor(private canvas: HTMLCanvasElement, controls: HTMLElement) { window.addEventListener('resize', this.onResize); @@ -108,7 +109,11 @@ export default class Game { } private loop = () => { - this.world.update(); + const now = performance.now(); + const dt = now - this.prevFrame; + this.prevFrame = now; + + this.world.update(dt); this.graphics.clear(); diff --git a/src/game/renderer.ts b/src/game/renderer.ts index 182ffaa..4a77f8e 100644 --- a/src/game/renderer.ts +++ b/src/game/renderer.ts @@ -52,5 +52,9 @@ export const renderers: Renderers = { ctx.fillRect(0.8, 0.2, 0.2, 0.6); } } + if (tile.resource) { + ctx.fillStyle = 'blue'; + ctx.fillText(tile.resource.toString(2), 0.5, 0.65); + } } }; \ No newline at end of file diff --git a/src/game/utils.ts b/src/game/utils.ts index aaf2aeb..9ec1024 100644 --- a/src/game/utils.ts +++ b/src/game/utils.ts @@ -141,6 +141,13 @@ export const DIRECTION_VECTORS: Record = { [Direction.WEST]: [-1, 0], [Direction.EAST]: [1, 0], }; +export const NEXT_DIRECTION: Record = { + [Direction.NONE]: Direction.NONE, + [Direction.NORTH]: Direction.EAST, + [Direction.SOUTH]: Direction.WEST, + [Direction.WEST]: Direction.NORTH, + [Direction.EAST]: Direction.SOUTH, +}; export const ALL_DIRECTIONS = [Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST]; export const movePoint = (point: Point, direction: Direction): Point => [ point[0] + DIRECTION_VECTORS[direction][0], diff --git a/src/game/world.ts b/src/game/world.ts index e0af166..71f46cb 100644 --- a/src/game/world.ts +++ b/src/game/world.ts @@ -1,4 +1,4 @@ -import { ALL_DIRECTIONS, Direction, cyrb32, exp, getDirection, getOppositeDirection, movePoint, pointsEquals, trunc } from "./utils"; +import { ALL_DIRECTIONS, Direction, NEXT_DIRECTION, cyrb32, exp, getDirection, getOppositeDirection, movePoint, pointsEquals, trunc } from "./utils"; export enum TileType { DESTINATION, @@ -16,13 +16,15 @@ type Resource = number; interface Port { direction: PortDirection; - buffer?: Resource; } type Ports = Partial>; interface BaseTile { ports: Ports; + nextOutput?: Direction; + resource?: Resource; + timer?: number; } interface TileDestination extends BaseTile { @@ -47,8 +49,25 @@ interface TileConveyor extends BaseTile { export type Tile = TileDestination | TileSource | TileExtractor | TileConveyor; const id = (point: Point) => ((Math.floor(point[0]) & 0xFFFF) << 16) | Math.floor(point[1]) & 0xFFFF; +const deid = (pid: number): Point => [(pid >> 16) & 0xFFFF, pid & 0xFFFF]; -export const getPortDirections = (ports: Partial>) => Object.keys(ports).map(k => +k as Direction); +export const getPortDirections = (ports: Ports) => Object.keys(ports).map(k => +k as Direction); + +const findNextOutput = (ports: Ports, prevDirection?: Direction): Direction => { + const outputs = getPortDirections(ports).filter(d => d && ports[d]?.direction === PortDirection.OUTPUT); + if (outputs.length === 0) return Direction.NONE; + + if (prevDirection) { + let nextDirection = NEXT_DIRECTION[prevDirection]; + while (nextDirection !== prevDirection) { + if (outputs.includes(nextDirection)) break; + nextDirection = NEXT_DIRECTION[nextDirection] + } + return nextDirection; + } else { + return outputs[0]; + } +} export default class World { private world = new Map(); @@ -108,11 +127,11 @@ export default class World { if (existingTile?.type === TileType.SOURCE) { const ports: Ports = {}; for (const direction of ALL_DIRECTIONS) { - const oppositeDirection = getOppositeDirection(direction); - const neighbourPos = movePoint(position, direction); - const neighbour = this.getTile(neighbourPos); - if (neighbour && !neighbour.ports[oppositeDirection]) { - neighbour.ports[oppositeDirection] = { direction: PortDirection.INPUT }; + const [neighbour, oppositeDirection] = this.getNeighbour(position, direction); + if (neighbour) { + if (!neighbour.ports[oppositeDirection]) { + neighbour.ports[oppositeDirection] = { direction: PortDirection.INPUT }; + } ports[direction] = { direction: PortDirection.OUTPUT }; } } @@ -141,22 +160,16 @@ export default class World { const pid = id(position); const existingTile = this.world.get(pid); const type = existingTile?.type; - if (type === TileType.DESTINATION || type === TileType.SOURCE) { + if (type === TileType.DESTINATION) { return; } if (existingTile) { - for (const direction of getPortDirections(existingTile.ports)) { - const oppositeDirection = getOppositeDirection(direction); - const neighbourPos = movePoint(position, direction); - const neighbour = this.getTile(neighbourPos); + for (const direction of getPortDirections(existingTile.ports)) { + const [neighbour, oppositeDirection] = this.getNeighbour(position, direction); if (neighbour) { delete neighbour.ports[oppositeDirection]; } } - } - if (existingTile?.type === TileType.EXTRACTOR) { - this.world.set(pid, existingTile.source); - } else if (existingTile) { this.world.delete(pid); } } @@ -175,20 +188,65 @@ export default class World { } private genTile(position: Point): Tile | null { - const pid = id(position); const hash = cyrb32(this.seed, ...position); if ((hash & 0xFF) === 42) { const resource = (hash >> 12) & 0xF || 1; const newTile: Tile = { type: TileType.SOURCE, resource, ports: {} }; - this.world.set(pid, newTile); return newTile; } return null; } - update() { + private get tiles() { + return this.world.entries(); + } + private getNeighbour(position: Point, direction: Direction): [Tile | null, Direction] { + const neighbourPosition = movePoint(position, direction); + const neighbour = this.getTile(neighbourPosition); + const oppositeDirection = getOppositeDirection(direction); + + return [neighbour, oppositeDirection]; + } + + update(dt: number) { + for (const [pid, tile] of this.tiles) { + const position = deid(pid); + if (tile.timer && tile.timer > 0) { + tile.timer -= dt; + } + + if (!tile.timer || tile.timer <= 0) { + switch (tile.type) { + case TileType.EXTRACTOR: + case TileType.CONVEYOR: + if (tile.resource) { + tile.nextOutput = findNextOutput(tile.ports, tile.nextOutput); + if (tile.nextOutput) { + const [neighbour, oppositeDirection] = this.getNeighbour(position, tile.nextOutput); + + if (neighbour?.ports[oppositeDirection]?.direction === PortDirection.INPUT && !neighbour.resource) { + neighbour.resource = tile.resource; + neighbour.timer = 300; // TODO remove hardcode + tile.resource = undefined; + tile.timer = 500; + } else { + tile.timer = 100; + } + } + } + if (tile.type === TileType.EXTRACTOR && !tile.resource) { + tile.resource = tile.source.resource; + } + break; + case TileType.DESTINATION: + // TODO count gathered + tile.resource = undefined; + break; + } + } + } } } \ No newline at end of file