1
0
Fork 0

Block placement

This commit is contained in:
Pabloader 2024-06-25 23:18:08 +00:00
parent 0f7304851e
commit 56937c6d4c
4 changed files with 130 additions and 11 deletions

View File

@ -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) {

View File

@ -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 {

View File

@ -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);

58
src/world.ts Normal file
View File

@ -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));
}
}