feat: add experimental game engine & demo game, fix ACPI events

This commit is contained in:
2026-03-18 14:26:50 +01:00
parent ffffb6a0c5
commit b5d2cc515c
42 changed files with 2237 additions and 12 deletions
+134
View File
@@ -0,0 +1,134 @@
/*
* tilemap.h
* MontaukOS 2D Game Engine - Tile Map
* Grid-based terrain with per-tile rendering and collision
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <montauk/heap.h>
#include <montauk/string.h>
#include "engine/sprite.h"
namespace engine {
static constexpr int MAX_TILE_TYPES = 16;
struct TileType {
Spritesheet* sheet; // tile image (may be a single-tile spritesheet)
int src_x, src_y; // source position within the sheet
int src_w, src_h; // source size (typically tile_size x tile_size)
bool solid; // blocks movement
};
struct Tilemap {
int* data = nullptr;
int map_w = 0;
int map_h = 0;
int tile_size = 16; // native tile size in pixels
TileType types[MAX_TILE_TYPES];
int type_count = 0;
bool alloc(int w, int h, int ts = 16) {
map_w = w;
map_h = h;
tile_size = ts;
data = (int*)montauk::malloc(w * h * sizeof(int));
if (!data) return false;
montauk::memset(data, 0, w * h * sizeof(int));
return true;
}
void free_map() {
if (data) { montauk::mfree(data); data = nullptr; }
}
// Register a tile type. Returns the tile ID.
int add_type(Spritesheet* sheet, int sx, int sy, int sw, int sh, bool solid) {
if (type_count >= MAX_TILE_TYPES) return -1;
int id = type_count++;
types[id].sheet = sheet;
types[id].src_x = sx;
types[id].src_y = sy;
types[id].src_w = sw;
types[id].src_h = sh;
types[id].solid = solid;
return id;
}
void set(int x, int y, int tile_id) {
if (x >= 0 && x < map_w && y >= 0 && y < map_h)
data[y * map_w + x] = tile_id;
}
int get(int x, int y) const {
if (x < 0 || x >= map_w || y < 0 || y >= map_h) return -1;
return data[y * map_w + x];
}
bool is_solid(int x, int y) const {
int id = get(x, y);
if (id < 0 || id >= type_count) return true; // out of bounds = solid
return types[id].solid;
}
// Check if a world-pixel rectangle collides with any solid tile.
// World coordinates are in native (unscaled) pixels.
bool collides(int wx, int wy, int ww, int wh) const {
// Negative coordinates are always solid (out of bounds)
if (wx < 0 || wy < 0) return true;
int tx0 = wx / tile_size;
int ty0 = wy / tile_size;
int tx1 = (wx + ww - 1) / tile_size;
int ty1 = (wy + wh - 1) / tile_size;
for (int ty = ty0; ty <= ty1; ty++)
for (int tx = tx0; tx <= tx1; tx++)
if (is_solid(tx, ty))
return true;
return false;
}
// Draw visible tiles to the pixel buffer.
// cam_x/cam_y: camera position in world pixels (native scale).
// scale: rendering scale factor.
void draw(uint32_t* dst, int dst_w, int dst_h,
int cam_x, int cam_y, int scale) const {
if (!data) return;
int ts = tile_size * scale;
// Determine visible tile range
int tx0 = cam_x / tile_size;
int ty0 = cam_y / tile_size;
int tx1 = tx0 + dst_w / ts + 2;
int ty1 = ty0 + dst_h / ts + 2;
if (tx0 < 0) tx0 = 0;
if (ty0 < 0) ty0 = 0;
if (tx1 > map_w) tx1 = map_w;
if (ty1 > map_h) ty1 = map_h;
for (int ty = ty0; ty < ty1; ty++) {
for (int tx = tx0; tx < tx1; tx++) {
int id = data[ty * map_w + tx];
if (id < 0 || id >= type_count) continue;
const TileType& tt = types[id];
if (!tt.sheet) continue;
int sx = tx * ts - cam_x * scale;
int sy = ty * ts - cam_y * scale;
tt.sheet->draw_region(dst, dst_w, dst_h,
tt.src_x, tt.src_y,
tt.src_w, tt.src_h,
sx, sy, scale);
}
}
}
};
} // namespace engine