From e5d2a3e7e2e8f512c2389548acd02d888a1d6ace Mon Sep 17 00:00:00 2001 From: paulaboks Date: Fri, 6 Mar 2026 15:46:42 -0300 Subject: [PATCH] Change tilemap to dimension and simple generation --- assets/sprites/textures/dirt.png | Bin 0 -> 144 bytes assets/sprites/textures/grass.png | Bin 0 -> 144 bytes assets/sprites/textures/rock.png | Bin 0 -> 168 bytes assets/sprites/textures/sand.png | Bin 0 -> 144 bytes assets/sprites/textures/stone.png | Bin 0 -> 144 bytes assets/sprites/textures/tree.png | Bin 0 -> 261 bytes assets/sprites/textures/water.png | Bin 0 -> 119 bytes client/client_world.ts | 2 - client/components/camera.ts | 13 ++++ client/components/dimension.ts | 20 ++++++ client/components/tilemap.ts | 37 ---------- client/game.ts | 19 ++--- client/generation.ts | 47 ++++++++++++ client/systems/render_system.ts | 10 +-- client/systems/rendering/dimension.ts | 37 ++++++++++ client/systems/rendering/tilemap.ts | 30 -------- client/systems/tile_editor_system.ts | 99 -------------------------- common/constants.ts | 3 + deno.json | 1 + deno.lock | 5 ++ 20 files changed, 137 insertions(+), 186 deletions(-) create mode 100644 assets/sprites/textures/dirt.png create mode 100644 assets/sprites/textures/grass.png create mode 100644 assets/sprites/textures/rock.png create mode 100644 assets/sprites/textures/sand.png create mode 100644 assets/sprites/textures/stone.png create mode 100644 assets/sprites/textures/tree.png create mode 100644 assets/sprites/textures/water.png create mode 100644 client/components/dimension.ts delete mode 100644 client/components/tilemap.ts create mode 100644 client/generation.ts create mode 100644 client/systems/rendering/dimension.ts delete mode 100644 client/systems/rendering/tilemap.ts delete mode 100644 client/systems/tile_editor_system.ts diff --git a/assets/sprites/textures/dirt.png b/assets/sprites/textures/dirt.png new file mode 100644 index 0000000000000000000000000000000000000000..f4515b39c9272800be4cba2d2acbe21941d37e10 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`;hrvzAr`&KcP`2Nn%~HDz@;%;x|u9ucH-&*qh{m|jUX$k`rYs*rf_$$Ha4 rlTOx6JPu`|ZYGPEojALa^cfhObP0l+XkK9BM6g literal 0 HcmV?d00001 diff --git a/assets/sprites/textures/rock.png b/assets/sprites/textures/rock.png new file mode 100644 index 0000000000000000000000000000000000000000..b075359bde46c9bdcb95b28c9406022777735160 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%``JOJ0Ar`&K2@;K7)E&%gjHu{ z<{x;@zuw@-i=+zP2ciPXFPOw)wk2Ke@$GqJaG-$E@Z7Rh+$+)M@ z%x6$%XJ?a-GL0!tdVXAWLy~~9kFjF!4W3;Ww#=N!8vE$=^z}UT`~RdQfWWiF z;}=exJ=}XJz2zp~ | undefined; +} + +export class Dimension extends Component { + image: HTMLImageElement; + tiles: Tile[]; + + constructor(tiles: Tile[]) { + super(); + this.image = AssetManager.instance.get("bworld:textures"); + this.tiles = tiles; + } +} diff --git a/client/components/tilemap.ts b/client/components/tilemap.ts deleted file mode 100644 index 3826007..0000000 --- a/client/components/tilemap.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component } from "$/common/ecs/mod.ts"; - -interface Tile { - x: number; - y: number; - index: number; -} - -export class Tilemap extends Component { - image: HTMLImageElement; - tile_size: number; - rows: number; - columns: number; - tiles: Tile[]; - scale: number; - margin: number; - - selected_tile = 0; - editing = false; - - constructor( - image: HTMLImageElement, - tile_size: number, - tiles: Tile[], - scale: number = 1, - margin: number = 0, - ) { - super(); - this.image = image; - this.tile_size = tile_size; - this.columns = Math.floor((image.width + margin) / (tile_size + margin)); - this.rows = Math.floor((image.height + margin) / (tile_size + margin)); - this.tiles = tiles; - this.scale = scale; - this.margin = margin; - } -} diff --git a/client/game.ts b/client/game.ts index 0559fbe..c403413 100644 --- a/client/game.ts +++ b/client/game.ts @@ -1,28 +1,21 @@ import { Entity } from "$/common/ecs/mod.ts"; import { Position } from "$/common/components/position.ts"; import { ClientWorld } from "./client_world.ts"; -import { Tilemap } from "./components/tilemap.ts"; -import { AssetManager } from "./assets.ts"; +import { Dimension } from "./components/dimension.ts"; import { create_player } from "./player.ts"; import { UIButton } from "./components/ui_components.ts"; import { open_about } from "./about.ts"; import { create_crop_entity, Crop } from "./components/crop.ts"; +import { generate } from "./generation.ts"; export function start_game(world: ClientWorld) { world.state = "game"; world.clear_entities(); - const tilemap = new Entity("backgroundtiles"); - tilemap.add( - new Tilemap( - AssetManager.instance.get("bworld:roguelike"), - 16, - [], - 2, - 1, - ), - ); - world.add_entity(tilemap); + const dimension = new Entity("dimension"); + dimension.add(new Dimension([])); + world.add_entity(dimension); + generate(dimension.get(Dimension)!, 100, 100); create_player(world); diff --git a/client/generation.ts b/client/generation.ts new file mode 100644 index 0000000..db90d1b --- /dev/null +++ b/client/generation.ts @@ -0,0 +1,47 @@ +import { Alea, create_noise_2d } from "@paulaboks/rng"; + +import { Dimension } from "./components/dimension.ts"; + +export function generate(dimension: Dimension, width: number, height: number, seed = "seed") { + const height_noise = create_noise_2d(new Alea(seed + "_height")); + const tree_noise = create_noise_2d(new Alea(seed + "_trees")); + + const scale = 0.05; + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + // multi-octave terrain + const nx = x * scale; + const ny = y * scale; + + let h = height_noise(nx, ny) * 1 + height_noise(nx * 2, ny * 2) * 0.5 + height_noise(nx * 4, ny * 4) * 0.25; + + h /= 1.75; // normalize + + let id = "bworld:grass"; + + if (h < -0.25) { + id = "bworld:water"; + } else if (h < -0.15) { + id = "bworld:sand"; + } else { + id = "bworld:grass"; + } + + dimension.tiles.push({ x, y, id }); + + // tree placement + if (id === "bworld:grass") { + const t = tree_noise(x * 0.12, y * 0.12); + + if (t > 0.65) { + dimension.tiles.push({ + x, + y, + id: "bworld:tree", + }); + } + } + } + } +} diff --git a/client/systems/render_system.ts b/client/systems/render_system.ts index f650e17..93f04a0 100644 --- a/client/systems/render_system.ts +++ b/client/systems/render_system.ts @@ -2,12 +2,12 @@ import { System } from "$/common/ecs/mod.ts"; import { World } from "$/common/ecs/world.ts"; import { Position } from "$/common/components/position.ts"; import { AnimatedSprite, Sprite } from "$/client/components/sprite.ts"; -import { Tilemap } from "$/client/components/tilemap.ts"; +import { Dimension } from "../components/dimension.ts"; import { PlayerInventory } from "$/client/components/inventory.ts"; import { Camera } from "$/client/components/camera.ts"; import { render_animated_sprite, render_sprite } from "./rendering/sprites.ts"; -import { render_tilemap } from "./rendering/tilemap.ts"; +import { render_dimension } from "./rendering/dimension.ts"; import { render_player_inventory } from "./rendering/player.ts"; export class RenderSystem extends System { @@ -56,10 +56,10 @@ export class RenderSystem extends System { render_animated_sprite(this.ctx, animated_sprite, position); } - const tilemap = entity.get(Tilemap); + const dimension = entity.get(Dimension); - if (tilemap) { - render_tilemap(this.ctx, tilemap); + if (dimension) { + render_dimension(this.ctx, dimension, camera); } // Ui diff --git a/client/systems/rendering/dimension.ts b/client/systems/rendering/dimension.ts new file mode 100644 index 0000000..8b1f54e --- /dev/null +++ b/client/systems/rendering/dimension.ts @@ -0,0 +1,37 @@ +import { get_sprite_region } from "$/common/utils.ts"; +import { Dimension } from "$/client/components/dimension.ts"; +import { TEXTURE_SIZE, TILE_SIZE } from "$/common/constants.ts"; +import { Camera, world_to_screen } from "$/client/components/camera.ts"; + +export function render_dimension(ctx: CanvasRenderingContext2D, tilemap: Dimension, camera: Camera) { + ctx.save(); + + for (const tile of tilemap.tiles) { + const region = get_sprite_region(tile.id); + + const x = tile.x * TILE_SIZE; + const y = tile.y * TILE_SIZE; + + const screen_position = world_to_screen(x, y, camera, ctx.canvas.width, ctx.canvas.height); + if ( + screen_position.x + TILE_SIZE < 0 || screen_position.x > ctx.canvas.width || + screen_position.y + TILE_SIZE < 0 || screen_position.y > ctx.canvas.height + ) { + continue; + } + + ctx.drawImage( + tilemap.image, + region.x * TEXTURE_SIZE, + region.y * TEXTURE_SIZE, + TEXTURE_SIZE, + TEXTURE_SIZE, + x, + y, + TILE_SIZE, + TILE_SIZE, + ); + } + + ctx.restore(); +} diff --git a/client/systems/rendering/tilemap.ts b/client/systems/rendering/tilemap.ts deleted file mode 100644 index 6770f09..0000000 --- a/client/systems/rendering/tilemap.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Tilemap } from "$/client/components/tilemap.ts"; - -export function render_tilemap(ctx: CanvasRenderingContext2D, tilemap: Tilemap) { - ctx.save(); - ctx.translate( - -Math.floor(ctx.canvas.width / 2), - -Math.floor(ctx.canvas.height / 2), - ); - - for (const tile of tilemap.tiles) { - const tx = tile.index % tilemap.columns; - const ty = Math.floor(tile.index / tilemap.columns); - const sx = tx * tilemap.tile_size + (tx * tilemap.margin); - const sy = ty * tilemap.tile_size + (ty * tilemap.margin); - - ctx.drawImage( - tilemap.image, - sx, - sy, - tilemap.tile_size, - tilemap.tile_size, - tile.x * tilemap.tile_size * tilemap.scale, - tile.y * tilemap.tile_size * tilemap.scale, - tilemap.tile_size * tilemap.scale, - tilemap.tile_size * tilemap.scale, - ); - } - - ctx.restore(); -} diff --git a/client/systems/tile_editor_system.ts b/client/systems/tile_editor_system.ts deleted file mode 100644 index f51f354..0000000 --- a/client/systems/tile_editor_system.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { System } from "$/common/ecs/mod.ts"; -import { point_inside_rec } from "$/common/utils.ts"; -import { ClientWorld } from "$/client/client_world.ts"; -import { Camera } from "$/client/components/camera.ts"; -import { Tilemap } from "$/client/components/tilemap.ts"; -import { DebugUI } from "$/client/debug_ui.ts"; -import { InputManager } from "$/client/input_manager.ts"; - -export class TileEditorSystem extends System { - constructor() { - super(); - } - - update(world: ClientWorld, _delta: number): void { - if (!world.debugging) { - return; - } - - DebugUI.ctx.save(); - DebugUI.ctx.setTransform(1, 0, 0, 1, 0, 0); - - const ctx = world.ctx; - - const camera_entity = world.get_entities().values().find((e) => e.get(Camera)?.active); - const camera = camera_entity?.get(Camera); - if (!camera) { - return; - } - - for (const entity of world.get_entities()) { - const tilemap = entity.get(Tilemap); - - if (!tilemap) { - continue; - } - - const scaled_size = tilemap.tile_size * tilemap.scale; - const mouse = InputManager.get_mouse_position(); - - DebugUI.begin("Tile Editor", 500, 10, 300); - DebugUI.progress_bar(0.5); - - if (DebugUI.button("Save")) { - navigator.clipboard.writeText(JSON.stringify(tilemap)); - } - - for (let i = 0; i < tilemap.rows; i += 1) { - for (let j = 0; j < tilemap.columns; j += 1) { - const x = DebugUI.cursor_x + j * scaled_size; - const y = DebugUI.cursor_y; - - const hovered = point_inside_rec(mouse.x, mouse.y, x, y, scaled_size, scaled_size); - - if (DebugUI.is_inside_windows(mouse.x, mouse.y) && hovered && InputManager.is_mouse_pressed(0)) { - InputManager.consume_mouse(0); - tilemap.selected_tile = i * tilemap.columns + j; - } - - ctx.drawImage( - tilemap.image, - j * tilemap.tile_size + (j * tilemap.margin), - i * tilemap.tile_size + (i * tilemap.margin), - tilemap.tile_size, - tilemap.tile_size, - x, - y, - scaled_size, - scaled_size, - ); - } - DebugUI.advance(scaled_size); - } - - DebugUI.end(); - - if (tilemap.editing) { - const world_x = mouse.x + camera.x; - const world_y = mouse.y + camera.y; - - const tx = Math.floor(world_x / (tilemap.tile_size * tilemap.scale)); - const ty = Math.floor(world_y / (tilemap.tile_size * tilemap.scale)); - if (InputManager.is_mouse_pressed(0)) { - const maybe_tile = tilemap.tiles.find((tile) => tile.x === tx && tile.y === ty); - if (maybe_tile?.index !== tilemap.selected_tile) { - tilemap.tiles.push({ x: tx, y: ty, index: tilemap.selected_tile }); - } - } else if (InputManager.is_mouse_pressed(1)) { - InputManager.consume_mouse(1); - const index = tilemap.tiles.findLastIndex((tile) => tile.x === tx && tile.y === ty); - if (index !== -1) { - tilemap.tiles.splice(index, 1); - } - } - } - } - - DebugUI.ctx.restore(); - } -} diff --git a/common/constants.ts b/common/constants.ts index d0c8846..ac7aadb 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -1,3 +1,6 @@ +export const TEXTURE_SIZE = 16; +export const TILE_SIZE = 32; + export const SLOT_SIZE = 18 * 3; export interface SpriteRegion { diff --git a/deno.json b/deno.json index 55f8078..5147f5e 100644 --- a/deno.json +++ b/deno.json @@ -16,6 +16,7 @@ "imports": { "$/": "./", "@gfx/canvas-wasm": "jsr:@gfx/canvas-wasm@^0.4.2", + "@paulaboks/rng": "jsr:@paulaboks/rng@^0.0.2", "@std/fs": "jsr:@std/fs@^1.0.23", "marked": "npm:marked@^17.0.3" } diff --git a/deno.lock b/deno.lock index f10b053..85b5ff8 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "5", "specifiers": { "jsr:@gfx/canvas-wasm@~0.4.2": "0.4.2", + "jsr:@paulaboks/rng@^0.0.2": "0.0.2", "jsr:@std/encoding@1.0.5": "1.0.5", "jsr:@std/fs@^1.0.23": "1.0.23", "jsr:@std/internal@^1.0.12": "1.0.12", @@ -15,6 +16,9 @@ "jsr:@std/encoding" ] }, + "@paulaboks/rng@0.0.2": { + "integrity": "9b4833c80f09f2b6455f3d477977caaab0aac1b04a89970166a2c5169ab4687d" + }, "@std/encoding@1.0.5": { "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" }, @@ -44,6 +48,7 @@ "workspace": { "dependencies": [ "jsr:@gfx/canvas-wasm@~0.4.2", + "jsr:@paulaboks/rng@^0.0.2", "jsr:@std/fs@^1.0.23", "npm:marked@^17.0.3" ]