1
0
Fork 0

Optimizations & world gen

This commit is contained in:
Pabloader 2024-06-26 10:27:01 +00:00
parent 56937c6d4c
commit b0bcae89ef
5 changed files with 73 additions and 14 deletions

View File

@ -18,6 +18,7 @@ export default class Game {
canvas.addEventListener('mousedown', this.onMouseDown); canvas.addEventListener('mousedown', this.onMouseDown);
canvas.addEventListener('mousemove', this.onMouseMove); canvas.addEventListener('mousemove', this.onMouseMove);
canvas.addEventListener('mouseup', this.onMouseUp); canvas.addEventListener('mouseup', this.onMouseUp);
document.addEventListener('keypress', this.onKeyPress);
document.addEventListener('contextmenu', prevent); document.addEventListener('contextmenu', prevent);
this.graphics = new Graphics(canvas); this.graphics = new Graphics(canvas);
@ -52,6 +53,7 @@ export default class Game {
this.mouseDown = event.button; this.mouseDown = event.button;
event.preventDefault(); event.preventDefault();
} }
private onMouseUp = (event: MouseEvent) => { private onMouseUp = (event: MouseEvent) => {
this.canvas.style.cursor = 'default'; this.canvas.style.cursor = 'default';
this.mouseDown = false; this.mouseDown = false;
@ -64,6 +66,7 @@ export default class Game {
} }
event.preventDefault(); event.preventDefault();
} }
private onMouseMove = (event: MouseEvent) => { private onMouseMove = (event: MouseEvent) => {
if (this.mouseDown === 1) { if (this.mouseDown === 1) {
this.canvas.style.cursor = 'grabbing'; this.canvas.style.cursor = 'grabbing';
@ -73,6 +76,15 @@ export default class Game {
event.preventDefault(); event.preventDefault();
} }
private onKeyPress = (event: KeyboardEvent) => {
const key = event.key.toLowerCase();
if (key === 'h') {
this.graphics.resetView();
} else {
console.log(`Pressed: ${key}`);
}
}
run() { run() {
this.running = true; this.running = true;
this.loop(); this.loop();
@ -80,11 +92,11 @@ export default class Game {
private loop = () => { private loop = () => {
this.graphics.clear(); this.graphics.clear();
this.graphics.drawGrid();
this.graphics.drawWorld(this.world); this.graphics.drawWorld(this.world);
this.graphics.drawHighlight(); this.graphics.drawHighlight();
this.graphics.drawGrid();
this.graphics.debug(); this.graphics.debug();

View File

@ -1,14 +1,18 @@
import { exp, trunc } from "./utils"; import { exp, trunc } from "./utils";
import type World from "./world"; import type World from "./world";
import { TileType } from "./world";
const initialTileSize = 32;
export default class Graphics { export default class Graphics {
private context: CanvasRenderingContext2D; private context: CanvasRenderingContext2D;
private tileSize = 32; private tileSize = initialTileSize;
private offset: Point = [0, 0]; private offset: Point = [0, 0];
private highlighted: Point = [0, 0]; private highlighted: Point = [0, 0];
constructor(private canvas: HTMLCanvasElement) { constructor(private canvas: HTMLCanvasElement) {
this.context = canvas.getContext('2d')!; this.context = canvas.getContext('2d')!;
this.resetView();
} }
get width() { get width() {
@ -35,6 +39,11 @@ export default class Graphics {
this.offset = exp`${this.offset} + ${amount}`; this.offset = exp`${this.offset} + ${amount}`;
} }
resetView() {
this.tileSize = initialTileSize;
this.offset = exp`(${this.size} - ${this.tileSize}) / ${2}`;
}
highlight(screenPoint: Point) { highlight(screenPoint: Point) {
this.highlighted = this.screenToWorld(screenPoint); this.highlighted = this.screenToWorld(screenPoint);
} }
@ -90,14 +99,19 @@ export default class Graphics {
} }
drawWorld(world: World) { 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; const [x0, y0, x1, y1] = this.visibleWorld;
for (let y = y0; y <= y1; y++) { for (let y = y0; y <= y1; y++) {
for (let x = x0; x <= x1; x++) { for (let x = x0; x <= x1; x++) {
const tile = world.getTile([x, y]); const tile = world.getTile([x, y]);
if (tile) { if (tile?.type === TileType.SOURCE) {
this.drawTile([x, y], ctx => { this.drawTile([x, y], ctx => {
ctx.fillStyle = 'green'; ctx.fillStyle = '#bbffff';
ctx.fillRect(0, 0, 1, 1); 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 { 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 { screenToWorld(point: Point): Point {

View File

@ -15,6 +15,9 @@ Bun.serve({
entrypoints: [path.resolve(import.meta.dir, 'index.ts')], entrypoints: [path.resolve(import.meta.dir, 'index.ts')],
sourcemap: 'inline', sourcemap: 'inline',
publicPath: '/build/', publicPath: '/build/',
define: {
global: 'window',
}
}); });
if (bundle.success && bundle.outputs.length === 1) { if (bundle.success && bundle.outputs.length === 1) {

View File

@ -90,4 +90,17 @@ export function trunc(input: Point): Point {
return op(input, 0, (x) => Math.floor(x)) as 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;

View File

@ -1,4 +1,4 @@
import { trunc } from "./utils"; import { cyrb32, sinHash } from "./utils";
export enum TileType { export enum TileType {
SOURCE, SOURCE,
@ -19,8 +19,9 @@ interface BaseTile {
inputs: Direction[]; inputs: Direction[];
} }
interface TileSource extends BaseTile { interface TileSource {
type: TileType.SOURCE; type: TileType.SOURCE;
resource: number;
} }
interface TileExtractor extends BaseTile { interface TileExtractor extends BaseTile {
@ -34,17 +35,17 @@ interface TileConveyor extends BaseTile {
type Tile = TileExtractor | TileSource | TileConveyor; 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 { export default class World {
private world = new Map<string, Tile>(); private world = new Map<number, Tile>();
constructor() { constructor(private seed: number = Math.random() * 2e9) {
} }
placeTile(position: Point, type: TileType) { placeTile(position: Point, type: TileType) {
// TODO select correct type // 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) { removeTile(position: Point) {
@ -52,7 +53,20 @@ export default class World {
this.world.delete(id(position)); this.world.delete(id(position));
} }
getTile(position: Point) { getTile(position: Point): Tile | null {
return this.world.get(id(position)); 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;
} }
} }