Skip to content

Commit

Permalink
some renderer changes & add death tile icon
Browse files Browse the repository at this point in the history
  • Loading branch information
Vulae committed May 4, 2024
1 parent 4669460 commit 0b4d4af
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 32 deletions.
7 changes: 3 additions & 4 deletions src/lib/game/Chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class Chunk {

export class GeneratedChunk extends Chunk {
public readonly tiles: ValidTile[];
public deaths: { x: number, y: number, diedAt: Date }[] = [];

public constructor(world: World, chunkX: number, chunkY: number, tiles: ValidTile[]) {
super(world, chunkX, chunkY);
Expand All @@ -75,10 +76,6 @@ export class GeneratedChunk extends Chunk {
}



public deaths: { x: number, y: number, diedAt: Date }[] = [];



public encodeTiles(): ArrayBuffer {
const io = new BitIO(2048);
Expand Down Expand Up @@ -107,12 +104,14 @@ export class GeneratedChunk extends Chunk {

public save(): b.ParserType<typeof F_CHUNK> {
return {
deaths: this.deaths,
tiles: this.encodeTiles()
};
}

public static load(world: World, chunkX: number, chunkY: number, savedChunk: b.ParserType<typeof F_CHUNK>): GeneratedChunk {
const chunk = GeneratedChunk.decodeTiles(world, chunkX, chunkY, savedChunk.tiles);
chunk.deaths = savedChunk.deaths;
return chunk;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib/game/Save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export function clear(saveSlot: string): void {


export const F_CHUNK = b.object({
deaths: b.array(b.object({
x: b.number('u8'),
y: b.number('u8'),
diedAt: b.date()
})),
tiles: b.binary()
});

Expand Down
28 changes: 28 additions & 0 deletions src/lib/game/Viewport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

import type { GeneratedChunk } from "./Chunk";
import { CHUNK_SIZE } from "./Constants";
import type { World } from "./World";
import type { ValidTile } from "./tile/Tile";



Expand Down Expand Up @@ -97,6 +100,31 @@ export class Viewport {
ctx.translate(-this.cameraX, -this.cameraY);
}

public forEachTileInViewport(callbackfn: (tile: ValidTile) => void, margin: number = 0): void {
const bounds = this.cameraBounds(margin);
for(let x = bounds.minX; x < bounds.maxX; x++) {
for(let y = bounds.minY; y < bounds.maxY; y++) {
const tile = this.world.getTile(x, y);
callbackfn(tile);
}
}
}

public forEachChunkInViewport(callbackfn: (chunk: GeneratedChunk) => void, margin: number = 0): void {
const bounds = this.cameraBounds(margin);
bounds.minX = Math.floor(bounds.minX / CHUNK_SIZE);
bounds.minY = Math.floor(bounds.minY / CHUNK_SIZE);
bounds.maxX = Math.ceil(bounds.maxX / CHUNK_SIZE);
bounds.maxY = Math.ceil(bounds.maxY / CHUNK_SIZE);
for(let x = bounds.minX; x < bounds.maxX; x++) {
for(let y = bounds.minY; y < bounds.maxY; y++) {
const chunk = this.world.getChunk(x, y);
if(!chunk.isGenerated()) continue;
callbackfn(chunk);
}
}
}

}


27 changes: 17 additions & 10 deletions src/lib/game/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,25 @@ export class World extends EventDispatcher<{
public readonly tileSeed: number;
public readonly biomeSeed: number;

protected _createdAt: Date = new Date();
public get createdAt(): Date { return this._createdAt; }

protected _deaths: number = 0;
public get deaths(): number { return this._deaths; }
public createdAt: Date = new Date();
public deaths: number = 0;

constructor(seed: number) {
super();
this.seed = (seed & 0xFFFFFFFF) >>> 0;
const rng = splitmix32(this.seed, false);
this.tileSeed = rng();
this.biomeSeed = rng();
this.addEventListener('die', () => this._deaths++);
this.addEventListener('die', ({ data: { x, y } }) => {
this.deaths++;

const deathChunk = this.getGeneratedChunk(Math.floor(x / CHUNK_SIZE), Math.floor(y / CHUNK_SIZE));
deathChunk.deaths.push({
x: x - (deathChunk.chunkX * CHUNK_SIZE),
y: y - (deathChunk.chunkY * CHUNK_SIZE),
diedAt: new Date()
});
});
}

private chunks: {[key: ChunkCoordinate]: GeneratedChunk} = {};
Expand Down Expand Up @@ -93,6 +99,7 @@ export class World extends EventDispatcher<{

public flag(x: number, y: number): void {
const tile = this.getTile(x, y);
if(tile.isDeathTile()) return;
const prevNumFlags = tile.numFlags();
tile.flag();
const currentNumFlags = tile.numFlags();
Expand Down Expand Up @@ -211,15 +218,15 @@ export class World extends EventDispatcher<{

public static load(save: b.ParserType<typeof F_SAVE>): World {
const world = new World(save.seed);
world._createdAt = save.createdAt;
world._deaths = save.numDeaths;
world.createdAt = save.createdAt;
world.deaths = save.numDeaths;

for(const _chunkCoord in save.chunks) {
const chunkCoord = _chunkCoord as ChunkCoordinate;
const [ _, chunkXstr, chunkYstr ] = chunkCoord.match(/^(-?\d+),(-?\d+)$/)!;
const [ chunkX, chunkY ] = [ parseInt(chunkXstr), parseInt(chunkYstr) ];
const chunk = save.chunks[chunkCoord];
world.chunks[chunkCoord] = GeneratedChunk.decodeTiles(world, chunkX, chunkY, chunk.tiles);
const savedChunk = save.chunks[chunkCoord];
world.chunks[chunkCoord] = GeneratedChunk.load(world, chunkX, chunkY, savedChunk);
}

return world;
Expand Down
30 changes: 13 additions & 17 deletions src/lib/game/WorldRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

import { CHUNK_SIZE } from "./Constants";
import type { Viewport } from "./Viewport";
import type { World } from "./World";
import type { Theme } from "./theme/Theme";
Expand Down Expand Up @@ -32,23 +33,18 @@ export class WorldRenderer {
this.ctx.imageSmoothingEnabled = false;
this.viewport.transformCtx(this.ctx);

const bounds = this.viewport.cameraBounds(1);
for(let x = bounds.minX; x < bounds.maxX; x++) {
for(let y = bounds.minY; y < bounds.maxY; y++) {
this.ctx.save();
this.ctx.translate(x, y);
// We need to oversize each tile to prevent gaps between tiles due to fp precision loss.
// TODO: Dynamically set this based on cameraZoom
// this.ctx.translate(0.5, 0.5);
// this.ctx.scale(1.01, 1.01);
// this.ctx.translate(-0.5, -0.5);

const tile = this.world.getTile(x, y);
this.theme.drawTile(this.ctx, tile);

this.ctx.restore();
}
}
// Render tiles
this.viewport.forEachTileInViewport(tile => {
this.theme.drawTile(this.ctx, tile);
}, 0);

// Render death icons
this.viewport.forEachChunkInViewport(chunk => {
chunk.deaths.forEach(death => {
this.theme.drawDeathIcon(this.ctx, chunk.chunkX * CHUNK_SIZE + death.x, chunk.chunkY * CHUNK_SIZE + death.y);
});
}, 0);

this.ctx.imageSmoothingEnabled = true;
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/game/theme/Theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export abstract class Theme {
public abstract init(): Promise<void>;
public abstract drawTile(ctx: CanvasRenderingContext2D, tile: ValidTile): void;
public abstract drawParticle(ctx: CanvasRenderingContext2D, particle: ValidParticle): void;
public abstract drawDeathIcon(ctx: CanvasRenderingContext2D, tileX: number, tileY: number): void;

public abstract readonly soundEffects: {[key in SoundEffect]: { src: string, variation: number }};
public volume: number = 1;
Expand Down
8 changes: 7 additions & 1 deletion src/lib/game/theme/retro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class ThemeRetro extends Theme {

private tileset = new TextureAtlas('/infinite-minesweeper/retro/tileset.png', {
null: [ 0, 0, 16, 16 ],
skull: [ 0, 16, 16, 16 ],
bomb: [ 16, 0, 16, 16 ],
explosion1: [ 16, 16, 16, 16 ],
explosion2: [ 16, 32, 16, 16 ],
Expand Down Expand Up @@ -169,13 +170,15 @@ export class ThemeRetro extends Theme {
}

public drawTile(ctx: CanvasRenderingContext2D, tile: ValidTile): void {
ctx.save();
ctx.translate(tile.x, tile.y);
this.drawForcedTile(ctx, tile, false);
ctx.restore();
}



public drawParticle(ctx: CanvasRenderingContext2D, particle: ValidParticle): void {

ctx.save();

switch(particle.type) {
Expand Down Expand Up @@ -211,7 +214,10 @@ export class ThemeRetro extends Theme {

ctx.globalAlpha = 1;
ctx.restore();
}

public drawDeathIcon(ctx: CanvasRenderingContext2D, tileX: number, tileY: number): void {
this.tileset.draw(ctx, 'skull', tileX, tileY, 1, 1);
}


Expand Down
11 changes: 11 additions & 0 deletions src/lib/game/tile/Tile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import type { BitIO } from "$lib/BitIO";
import { CHUNK_SIZE } from "../Constants";
import type { World } from "../World";
import type { BlueberryTile } from "./Blueberry";
import type { ChocolateTile } from "./Chocolate";
Expand Down Expand Up @@ -69,6 +70,16 @@ export abstract class Tile {
*/
public abstract reveal(): boolean;

/**
* If player has died on this tile.
*/
public isDeathTile(): boolean {
const chunk = this.world.getGeneratedChunk(Math.floor(this.x / CHUNK_SIZE), Math.floor(this.y / CHUNK_SIZE));
const chunkPosX = this.x - chunk.chunkX * CHUNK_SIZE;
const chunkPosY = this.y - chunk.chunkY * CHUNK_SIZE;
return chunk.deaths.some(death => death.x == chunkPosX && death.y == chunkPosY);
}



public abstract save(io: BitIO): void;
Expand Down
Binary file modified static/retro/tileset.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0b4d4af

Please sign in to comment.