Optimizations & world gen
This commit is contained in:
parent
56937c6d4c
commit
b0bcae89ef
14
src/game.ts
14
src/game.ts
|
|
@ -18,6 +18,7 @@ export default class Game {
|
|||
canvas.addEventListener('mousedown', this.onMouseDown);
|
||||
canvas.addEventListener('mousemove', this.onMouseMove);
|
||||
canvas.addEventListener('mouseup', this.onMouseUp);
|
||||
document.addEventListener('keypress', this.onKeyPress);
|
||||
document.addEventListener('contextmenu', prevent);
|
||||
|
||||
this.graphics = new Graphics(canvas);
|
||||
|
|
@ -52,6 +53,7 @@ export default class Game {
|
|||
this.mouseDown = event.button;
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
private onMouseUp = (event: MouseEvent) => {
|
||||
this.canvas.style.cursor = 'default';
|
||||
this.mouseDown = false;
|
||||
|
|
@ -64,6 +66,7 @@ export default class Game {
|
|||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
private onMouseMove = (event: MouseEvent) => {
|
||||
if (this.mouseDown === 1) {
|
||||
this.canvas.style.cursor = 'grabbing';
|
||||
|
|
@ -73,6 +76,15 @@ export default class Game {
|
|||
event.preventDefault();
|
||||
}
|
||||
|
||||
private onKeyPress = (event: KeyboardEvent) => {
|
||||
const key = event.key.toLowerCase();
|
||||
if (key === 'h') {
|
||||
this.graphics.resetView();
|
||||
} else {
|
||||
console.log(`Pressed: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
run() {
|
||||
this.running = true;
|
||||
this.loop();
|
||||
|
|
@ -80,11 +92,11 @@ export default class Game {
|
|||
|
||||
private loop = () => {
|
||||
this.graphics.clear();
|
||||
this.graphics.drawGrid();
|
||||
|
||||
this.graphics.drawWorld(this.world);
|
||||
|
||||
this.graphics.drawHighlight();
|
||||
this.graphics.drawGrid();
|
||||
|
||||
this.graphics.debug();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
import { exp, trunc } from "./utils";
|
||||
import type World from "./world";
|
||||
import { TileType } from "./world";
|
||||
|
||||
const initialTileSize = 32;
|
||||
|
||||
export default class Graphics {
|
||||
private context: CanvasRenderingContext2D;
|
||||
private tileSize = 32;
|
||||
private tileSize = initialTileSize;
|
||||
private offset: Point = [0, 0];
|
||||
private highlighted: Point = [0, 0];
|
||||
|
||||
constructor(private canvas: HTMLCanvasElement) {
|
||||
this.context = canvas.getContext('2d')!;
|
||||
this.resetView();
|
||||
}
|
||||
|
||||
get width() {
|
||||
|
|
@ -35,6 +39,11 @@ export default class Graphics {
|
|||
this.offset = exp`${this.offset} + ${amount}`;
|
||||
}
|
||||
|
||||
resetView() {
|
||||
this.tileSize = initialTileSize;
|
||||
this.offset = exp`(${this.size} - ${this.tileSize}) / ${2}`;
|
||||
}
|
||||
|
||||
highlight(screenPoint: Point) {
|
||||
this.highlighted = this.screenToWorld(screenPoint);
|
||||
}
|
||||
|
|
@ -90,14 +99,19 @@ export default class Graphics {
|
|||
}
|
||||
|
||||
drawWorld(world: World) {
|
||||
this.context.font = 'bold 0.4px sans-serif';
|
||||
this.context.textRendering = 'optimizeSpeed';
|
||||
this.context.textAlign = 'center';
|
||||
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) {
|
||||
if (tile?.type === TileType.SOURCE) {
|
||||
this.drawTile([x, y], ctx => {
|
||||
ctx.fillStyle = 'green';
|
||||
ctx.fillStyle = '#bbffff';
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillText(tile.resource.toString(2), 0.5, 0.65);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -112,7 +126,10 @@ export default class Graphics {
|
|||
}
|
||||
|
||||
worldToScreen(point: Point): Point {
|
||||
return exp`${point} * ${this.tileSize} + ${this.offset}`;
|
||||
return [
|
||||
point[0] * this.tileSize + this.offset[0],
|
||||
point[1] * this.tileSize + this.offset[1],
|
||||
];
|
||||
}
|
||||
|
||||
screenToWorld(point: Point): Point {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ Bun.serve({
|
|||
entrypoints: [path.resolve(import.meta.dir, 'index.ts')],
|
||||
sourcemap: 'inline',
|
||||
publicPath: '/build/',
|
||||
define: {
|
||||
global: 'window',
|
||||
}
|
||||
});
|
||||
|
||||
if (bundle.success && bundle.outputs.length === 1) {
|
||||
|
|
|
|||
15
src/utils.ts
15
src/utils.ts
|
|
@ -90,4 +90,17 @@ export function trunc(input: Point): Point {
|
|||
return op(input, 0, (x) => Math.floor(x)) as Point;
|
||||
}
|
||||
|
||||
export const prevent = (e: Event) => (e.preventDefault(), false);
|
||||
export const prevent = (e: Event) => (e.preventDefault(), false);
|
||||
|
||||
export const cyrb32 = (seed: number, ...parts: number[]) => {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const ch = parts[i];
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
return h1;
|
||||
};
|
||||
export const sinHash = (...data: number[]) => data.reduce((hash, n) => Math.sin((hash * 123.12 + n) * 756.12), 0) / 2 + 0.5;
|
||||
30
src/world.ts
30
src/world.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { trunc } from "./utils";
|
||||
import { cyrb32, sinHash } from "./utils";
|
||||
|
||||
export enum TileType {
|
||||
SOURCE,
|
||||
|
|
@ -19,8 +19,9 @@ interface BaseTile {
|
|||
inputs: Direction[];
|
||||
}
|
||||
|
||||
interface TileSource extends BaseTile {
|
||||
interface TileSource {
|
||||
type: TileType.SOURCE;
|
||||
resource: number;
|
||||
}
|
||||
|
||||
interface TileExtractor extends BaseTile {
|
||||
|
|
@ -34,17 +35,17 @@ interface TileConveyor extends BaseTile {
|
|||
|
||||
type Tile = TileExtractor | TileSource | TileConveyor;
|
||||
|
||||
const id = (point: Point) => `${Math.trunc(point[0])}-${Math.trunc(point[1])}`;
|
||||
const id = (point: Point) => ((Math.floor(point[0]) & 0xFFFF) << 16) | Math.floor(point[1]) & 0xFFFF;
|
||||
|
||||
export default class World {
|
||||
private world = new Map<string, Tile>();
|
||||
constructor() {
|
||||
private world = new Map<number, Tile>();
|
||||
constructor(private seed: number = Math.random() * 2e9) {
|
||||
|
||||
}
|
||||
|
||||
placeTile(position: Point, type: TileType) {
|
||||
// TODO select correct type
|
||||
this.world.set(id(position), { type: TileType.SOURCE, inputs: [], outputs: [] });
|
||||
this.world.set(id(position), { type: TileType.SOURCE, resource: (Math.random() * 0xF) & 0xF });
|
||||
}
|
||||
|
||||
removeTile(position: Point) {
|
||||
|
|
@ -52,7 +53,20 @@ export default class World {
|
|||
this.world.delete(id(position));
|
||||
}
|
||||
|
||||
getTile(position: Point) {
|
||||
return this.world.get(id(position));
|
||||
getTile(position: Point): Tile | null {
|
||||
const pid = id(position);
|
||||
const tile = this.world.get(pid);
|
||||
if (tile) return tile;
|
||||
|
||||
const hash = cyrb32(this.seed, ...position);
|
||||
|
||||
if ((hash & 0x1FF) === 42) {
|
||||
const resource = (hash >> 12) & 0xF;
|
||||
const newTile: Tile = { type: TileType.SOURCE, resource };
|
||||
this.world.set(pid, newTile);
|
||||
return newTile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue