Block placement
This commit is contained in:
parent
0f7304851e
commit
56937c6d4c
23
src/game.ts
23
src/game.ts
|
|
@ -1,10 +1,12 @@
|
|||
import Graphics from "./graphics";
|
||||
import { prevent } from "./utils";
|
||||
import World, { TileType } from "./world";
|
||||
|
||||
export default class Game {
|
||||
private running = false;
|
||||
private mouseDown = false;
|
||||
private mouseDown: false | number = false;
|
||||
private graphics;
|
||||
private world;
|
||||
|
||||
constructor(private canvas: HTMLCanvasElement) {
|
||||
window.addEventListener('resize', this.onResize);
|
||||
|
|
@ -19,6 +21,7 @@ export default class Game {
|
|||
document.addEventListener('contextmenu', prevent);
|
||||
|
||||
this.graphics = new Graphics(canvas);
|
||||
this.world = new World();
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
|
@ -46,17 +49,27 @@ export default class Game {
|
|||
}
|
||||
|
||||
private onMouseDown = (event: MouseEvent) => {
|
||||
this.mouseDown = true;
|
||||
this.mouseDown = event.button;
|
||||
event.preventDefault();
|
||||
}
|
||||
private onMouseUp = (event: MouseEvent) => {
|
||||
this.canvas.style.cursor = 'default';
|
||||
this.mouseDown = false;
|
||||
|
||||
const pos = this.graphics.screenToWorld([event.clientX, event.clientY]);
|
||||
if (event.button === 0) {
|
||||
this.world.placeTile(pos, TileType.CONVEYOR);
|
||||
} else if (event.button === 2) {
|
||||
this.world.removeTile(pos);
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
private onMouseMove = (event: MouseEvent) => {
|
||||
if (this.mouseDown) {
|
||||
if (this.mouseDown === 1) {
|
||||
this.canvas.style.cursor = 'grabbing';
|
||||
this.graphics.pan([event.movementX, event.movementY]);
|
||||
}
|
||||
this.graphics.highlight([event.clientX, event.clientY])
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +82,10 @@ export default class Game {
|
|||
this.graphics.clear();
|
||||
this.graphics.drawGrid();
|
||||
|
||||
this.graphics.drawWorld(this.world);
|
||||
|
||||
this.graphics.drawHighlight();
|
||||
|
||||
this.graphics.debug();
|
||||
|
||||
if (this.running) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { exp } from "./utils";
|
||||
import { exp, trunc } from "./utils";
|
||||
import type World from "./world";
|
||||
|
||||
export default class Graphics {
|
||||
private context: CanvasRenderingContext2D;
|
||||
private tileSize = 32;
|
||||
private offset: Point = [0, 0];
|
||||
private highlighted: Point = [0, 0];
|
||||
|
||||
constructor(private canvas: HTMLCanvasElement) {
|
||||
this.context = canvas.getContext('2d')!;
|
||||
|
|
@ -33,6 +35,10 @@ export default class Graphics {
|
|||
this.offset = exp`${this.offset} + ${amount}`;
|
||||
}
|
||||
|
||||
highlight(screenPoint: Point) {
|
||||
this.highlighted = this.screenToWorld(screenPoint);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.context.clearRect(0, 0, this.width, this.height);
|
||||
}
|
||||
|
|
@ -49,8 +55,8 @@ export default class Graphics {
|
|||
this.context.beginPath();
|
||||
this.context.strokeStyle = 'gray';
|
||||
let [x0, y0, x1, y1] = this.visibleWorld;
|
||||
[x0, y0] = this.worldToScreen([Math.floor(x0), Math.floor(y0)]);
|
||||
[x1, y1] = this.worldToScreen([Math.ceil(x1), Math.ceil(y1)]);
|
||||
[x0, y0] = this.worldToScreen([x0, y0]);
|
||||
[x1, y1] = this.worldToScreen([x1, y1]);
|
||||
|
||||
for (let x = x0; x < x1; x += this.tileSize) {
|
||||
this.context.moveTo(x, 0);
|
||||
|
|
@ -63,11 +69,46 @@ export default class Graphics {
|
|||
this.context.stroke();
|
||||
}
|
||||
|
||||
get visibleWorld(): Rect {
|
||||
const topLeft = this.screenToWorld([0, 0]);
|
||||
const bottomRight = this.screenToWorld([this.width, this.height]);
|
||||
drawHighlight() {
|
||||
this.drawTile(this.highlighted, ctx => {
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
});
|
||||
}
|
||||
|
||||
return [...topLeft, ...bottomRight];
|
||||
drawTile(position: Point, renderer: (ctx: CanvasRenderingContext2D) => void) {
|
||||
this.context.save();
|
||||
|
||||
// TODO skip drawing if outside screen
|
||||
const screenPosition = this.worldToScreen(trunc(position));
|
||||
this.context.translate(screenPosition[0], screenPosition[1]);
|
||||
this.context.scale(this.tileSize, this.tileSize);
|
||||
|
||||
renderer(this.context);
|
||||
|
||||
this.context.restore();
|
||||
}
|
||||
|
||||
drawWorld(world: World) {
|
||||
const [x0, y0, x1, y1] = this.visibleWorld;
|
||||
for (let y = y0; y <= y1; y++) {
|
||||
for (let x = x0; x <= x1; x++) {
|
||||
const tile = world.getTile([x, y]);
|
||||
if (tile) {
|
||||
this.drawTile([x, y], ctx => {
|
||||
ctx.fillStyle = 'green';
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get visibleWorld(): Rect {
|
||||
const [x0, y0] = this.screenToWorld([0, 0]);
|
||||
const [x1, y1] = this.screenToWorld([this.width, this.height]);
|
||||
|
||||
return [Math.floor(x0), Math.floor(y0), Math.ceil(x1), Math.ceil(y1)];
|
||||
}
|
||||
|
||||
worldToScreen(point: Point): Point {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const operations: Record<string, [Operation, number]> = {
|
|||
|
||||
export function exp(strings: TemplateStringsArray, ...args: number[]): number;
|
||||
export function exp(strings: TemplateStringsArray, ...args: Operand[]): Point;
|
||||
export function exp(strings: TemplateStringsArray, ...args: Operand[]): Point | number {
|
||||
export function exp(strings: TemplateStringsArray, ...args: Operand[]): Operand {
|
||||
const input: (string | Operand)[] = [];
|
||||
const output: (Operation | Operand)[] = [];
|
||||
const stack: string[] = [];
|
||||
|
|
@ -86,5 +86,8 @@ export function exp(strings: TemplateStringsArray, ...args: Operand[]): Point |
|
|||
|
||||
return calcStack[0];
|
||||
}
|
||||
export function trunc(input: Point): Point {
|
||||
return op(input, 0, (x) => Math.floor(x)) as Point;
|
||||
}
|
||||
|
||||
export const prevent = (e: Event) => (e.preventDefault(), false);
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import { trunc } from "./utils";
|
||||
|
||||
export enum TileType {
|
||||
SOURCE,
|
||||
EXTRACTOR,
|
||||
CONVEYOR,
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
NONE,
|
||||
NORTH,
|
||||
EAST,
|
||||
SOUTH,
|
||||
WEST,
|
||||
}
|
||||
|
||||
interface BaseTile {
|
||||
outputs: Direction[];
|
||||
inputs: Direction[];
|
||||
}
|
||||
|
||||
interface TileSource extends BaseTile {
|
||||
type: TileType.SOURCE;
|
||||
}
|
||||
|
||||
interface TileExtractor extends BaseTile {
|
||||
type: TileType.EXTRACTOR;
|
||||
source: TileSource;
|
||||
}
|
||||
|
||||
interface TileConveyor extends BaseTile {
|
||||
type: TileType.CONVEYOR;
|
||||
}
|
||||
|
||||
type Tile = TileExtractor | TileSource | TileConveyor;
|
||||
|
||||
const id = (point: Point) => `${Math.trunc(point[0])}-${Math.trunc(point[1])}`;
|
||||
|
||||
export default class World {
|
||||
private world = new Map<string, Tile>();
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
placeTile(position: Point, type: TileType) {
|
||||
// TODO select correct type
|
||||
this.world.set(id(position), { type: TileType.SOURCE, inputs: [], outputs: [] });
|
||||
}
|
||||
|
||||
removeTile(position: Point) {
|
||||
// TODO restore correct type if needed
|
||||
this.world.delete(id(position));
|
||||
}
|
||||
|
||||
getTile(position: Point) {
|
||||
return this.world.get(id(position));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue