add auto run

This commit is contained in:
asrael 2025-11-20 20:55:45 -06:00
parent fa57baf212
commit 2555bec8eb
9 changed files with 142 additions and 110 deletions

View file

@ -135,8 +135,7 @@ pxl8.world_is_loaded = world.is_loaded
pxl8.world_generate = world.generate
pxl8.world_apply_textures = world.apply_textures
pxl8.procgen_tex = world.procgen_tex
pxl8.PROCGEN_CAVE = world.PROCGEN_CAVE
pxl8.PROCGEN_DUNGEON = world.PROCGEN_DUNGEON
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
pxl8.transition_create = transition.create

View file

@ -4,8 +4,7 @@ local core = require("pxl8.core")
local world = {}
world.PROCGEN_CAVE = C.PXL8_PROCGEN_CAVE
world.PROCGEN_DUNGEON = C.PXL8_PROCGEN_DUNGEON
world.PROCGEN_ROOMS = C.PXL8_PROCGEN_ROOMS
world.PROCGEN_TERRAIN = C.PXL8_PROCGEN_TERRAIN
function world.new()
@ -35,14 +34,14 @@ end
function world.generate(w, params)
local c_params = ffi.new("pxl8_procgen_params")
c_params.type = params.type or C.PXL8_PROCGEN_CAVE
c_params.type = params.type or C.PXL8_PROCGEN_ROOMS
c_params.width = params.width or 32
c_params.height = params.height or 32
c_params.depth = params.depth or 0
c_params.seed = params.seed or 0
c_params.density = params.density or 0.45
c_params.iterations = params.iterations or 4
c_params.type_params = nil
c_params.min_room_size = params.min_room_size or 5
c_params.max_room_size = params.max_room_size or 10
c_params.num_rooms = params.num_rooms or 8
return C.pxl8_world_generate(w, core.gfx, c_params)
end

View file

@ -116,7 +116,7 @@ static i32 pxl8_key_code(const char* key_name) {
{"1", 30}, {"2", 31}, {"3", 32}, {"4", 33}, {"5", 34},
{"6", 35}, {"7", 36}, {"8", 37}, {"9", 38}, {"0", 39},
{"return", 40}, {"escape", 41}, {"backspace", 42}, {"tab", 43}, {"space", 44},
{"-", 45}, {"=", 46},
{"-", 45}, {"=", 46}, {"`", 53},
{"left", 80}, {"right", 79}, {"up", 82}, {"down", 81},
{"f1", 58}, {"f2", 59}, {"f3", 60}, {"f4", 61}, {"f5", 62}, {"f6", 63},
{"f7", 64}, {"f8", 65}, {"f9", 66}, {"f10", 67}, {"f11", 68}, {"f12", 69},

View file

@ -5,11 +5,11 @@
#include "pxl8_macros.h"
typedef struct cave_grid {
typedef struct room_grid {
u8* cells;
i32 width;
i32 height;
} cave_grid;
} room_grid;
static u32 prng_state = 0;
@ -24,11 +24,7 @@ static u32 prng_next(void) {
return prng_state;
}
static f32 prng_float(void) {
return (f32)prng_next() / (f32)0xFFFFFFFF;
}
static bool cave_grid_init(cave_grid* grid, i32 width, i32 height) {
static bool room_grid_init(room_grid* grid, i32 width, i32 height) {
grid->width = width;
grid->height = height;
grid->cells = calloc(width * height, sizeof(u8));
@ -36,33 +32,20 @@ static bool cave_grid_init(cave_grid* grid, i32 width, i32 height) {
return grid->cells != NULL;
}
static u8 cave_grid_get(const cave_grid* grid, i32 x, i32 y) {
static u8 room_grid_get(const room_grid* grid, i32 x, i32 y) {
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
return 1;
}
return grid->cells[y * grid->width + x];
}
static void cave_grid_set(cave_grid* grid, i32 x, i32 y, u8 value) {
static void room_grid_set(room_grid* grid, i32 x, i32 y, u8 value) {
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
return;
}
grid->cells[y * grid->width + x] = value;
}
static i32 cave_grid_count_neighbors(const cave_grid* grid, i32 x, i32 y) {
i32 count = 0;
for (i32 dy = -1; dy <= 1; dy++) {
for (i32 dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue;
if (cave_grid_get(grid, x + dx, y + dy)) {
count++;
}
}
}
return count;
}
static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 vert_idx) {
face->aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
face->aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
@ -78,43 +61,26 @@ static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex*
}
}
static void cave_grid_initialize(cave_grid* grid, f32 density) {
static void room_grid_fill(room_grid* grid, u8 value) {
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
u8 value = (prng_float() < density) ? 1 : 0;
cave_grid_set(grid, x, y, value);
room_grid_set(grid, x, y, value);
}
}
}
static void cave_grid_smooth(cave_grid* grid) {
cave_grid temp;
if (!cave_grid_init(&temp, grid->width, grid->height)) return;
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
i32 neighbors = cave_grid_count_neighbors(grid, x, y);
u8 value = (neighbors > 4) ? 1 : 0;
cave_grid_set(&temp, x, y, value);
}
}
memcpy(grid->cells, temp.cells, grid->width * grid->height);
free(temp.cells);
}
static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
i32 vertex_count = 0;
i32 face_count = 0;
i32 floor_ceiling_count = 0;
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (cave_grid_get(grid, x, y) == 0) {
if (cave_grid_get(grid, x - 1, y) == 1) face_count++;
if (cave_grid_get(grid, x + 1, y) == 1) face_count++;
if (cave_grid_get(grid, x, y - 1) == 1) face_count++;
if (cave_grid_get(grid, x, y + 1) == 1) face_count++;
if (room_grid_get(grid, x, y) == 0) {
if (room_grid_get(grid, x - 1, y) == 1) face_count++;
if (room_grid_get(grid, x + 1, y) == 1) face_count++;
if (room_grid_get(grid, x, y - 1) == 1) face_count++;
if (room_grid_get(grid, x, y + 1) == 1) face_count++;
floor_ceiling_count++;
}
}
@ -148,11 +114,11 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (cave_grid_get(grid, x, y) == 0) {
if (room_grid_get(grid, x, y) == 0) {
f32 fx = (f32)x * cell_size;
f32 fy = (f32)y * cell_size;
if (cave_grid_get(grid, x - 1, y) == 1) {
if (room_grid_get(grid, x - 1, y) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
@ -179,7 +145,7 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
face_idx++;
}
if (cave_grid_get(grid, x + 1, y) == 1) {
if (room_grid_get(grid, x + 1, y) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx + cell_size, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
@ -206,7 +172,7 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
face_idx++;
}
if (cave_grid_get(grid, x, y - 1) == 1) {
if (room_grid_get(grid, x, y - 1) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
@ -233,7 +199,7 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
face_idx++;
}
if (cave_grid_get(grid, x, y + 1) == 1) {
if (room_grid_get(grid, x, y + 1) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy + cell_size};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
@ -265,7 +231,7 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (cave_grid_get(grid, x, y) == 0) {
if (room_grid_get(grid, x, y) == 0) {
f32 fx = (f32)x * cell_size;
f32 fy = (f32)y * cell_size;
@ -349,21 +315,96 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
return PXL8_OK;
}
static pxl8_result procgen_cave(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
static bool bounds_intersects(const pxl8_bounds* a, const pxl8_bounds* b) {
return !(a->x + a->w <= b->x || b->x + b->w <= a->x ||
a->y + a->h <= b->y || b->y + b->h <= a->y);
}
static void carve_corridor_h(room_grid* grid, i32 x1, i32 x2, i32 y) {
i32 start = (x1 < x2) ? x1 : x2;
i32 end = (x1 > x2) ? x1 : x2;
for (i32 x = start; x <= end; x++) {
room_grid_set(grid, x, y, 0);
room_grid_set(grid, x, y - 1, 0);
room_grid_set(grid, x, y + 1, 0);
}
}
static void carve_corridor_v(room_grid* grid, i32 y1, i32 y2, i32 x) {
i32 start = (y1 < y2) ? y1 : y2;
i32 end = (y1 > y2) ? y1 : y2;
for (i32 y = start; y <= end; y++) {
room_grid_set(grid, x, y, 0);
room_grid_set(grid, x - 1, y, 0);
room_grid_set(grid, x + 1, y, 0);
}
}
static pxl8_result procgen_rooms(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
pxl8_debug("procgen_rooms called: %dx%d, seed=%u, min=%d, max=%d, num=%d",
params->width, params->height, params->seed,
params->min_room_size, params->max_room_size, params->num_rooms);
prng_seed(params->seed);
cave_grid grid;
if (!cave_grid_init(&grid, params->width, params->height)) {
room_grid grid;
if (!room_grid_init(&grid, params->width, params->height)) {
pxl8_error("Failed to allocate room grid");
return PXL8_ERROR_OUT_OF_MEMORY;
}
cave_grid_initialize(&grid, params->density);
room_grid_fill(&grid, 1);
for (i32 i = 0; i < params->iterations; i++) {
cave_grid_smooth(&grid);
pxl8_bounds rooms[256];
i32 room_count = 0;
i32 max_attempts = params->num_rooms * 10;
for (i32 attempt = 0; attempt < max_attempts && room_count < params->num_rooms && room_count < 256; attempt++) {
i32 w = params->min_room_size + (prng_next() % (params->max_room_size - params->min_room_size + 1));
i32 h = params->min_room_size + (prng_next() % (params->max_room_size - params->min_room_size + 1));
i32 x = 1 + (prng_next() % (params->width - w - 2));
i32 y = 1 + (prng_next() % (params->height - h - 2));
pxl8_bounds new_room = {x, y, w, h};
bool overlaps = false;
for (i32 i = 0; i < room_count; i++) {
if (bounds_intersects(&new_room, &rooms[i])) {
overlaps = true;
break;
}
}
if (!overlaps) {
for (i32 ry = y; ry < y + h; ry++) {
for (i32 rx = x; rx < x + w; rx++) {
room_grid_set(&grid, rx, ry, 0);
}
}
if (room_count > 0) {
i32 new_cx = x + w / 2;
i32 new_cy = y + h / 2;
i32 prev_cx = rooms[room_count - 1].x + rooms[room_count - 1].w / 2;
i32 prev_cy = rooms[room_count - 1].y + rooms[room_count - 1].h / 2;
if (prng_next() % 2 == 0) {
carve_corridor_h(&grid, prev_cx, new_cx, prev_cy);
carve_corridor_v(&grid, prev_cy, new_cy, new_cx);
} else {
carve_corridor_v(&grid, prev_cy, new_cy, prev_cx);
carve_corridor_h(&grid, prev_cx, new_cx, new_cy);
}
}
rooms[room_count++] = new_room;
}
}
pxl8_result result = cave_to_bsp(bsp, &grid);
pxl8_debug("Room generation: %dx%d grid -> %d rooms created",
params->width, params->height, room_count);
pxl8_result result = grid_to_bsp(bsp, &grid);
free(grid.cells);
return result;
@ -375,12 +416,8 @@ pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
}
switch (params->type) {
case PXL8_PROCGEN_CAVE:
return procgen_cave(bsp, params);
case PXL8_PROCGEN_DUNGEON:
pxl8_error("Dungeon generation not yet implemented");
return PXL8_ERROR_NOT_INITIALIZED;
case PXL8_PROCGEN_ROOMS:
return procgen_rooms(bsp, params);
case PXL8_PROCGEN_TERRAIN:
pxl8_error("Terrain generation not yet implemented");

View file

@ -4,8 +4,7 @@
#include "pxl8_types.h"
typedef enum pxl8_procgen_type {
PXL8_PROCGEN_CAVE,
PXL8_PROCGEN_DUNGEON,
PXL8_PROCGEN_ROOMS,
PXL8_PROCGEN_TERRAIN
} pxl8_procgen_type;
@ -17,22 +16,10 @@ typedef struct pxl8_procgen_params {
i32 depth;
u32 seed;
f32 density;
i32 iterations;
void* type_params;
} pxl8_procgen_params;
typedef struct pxl8_procgen_cave_params {
i32 min_cave_size;
} pxl8_procgen_cave_params;
typedef struct pxl8_procgen_dungeon_params {
i32 room_count;
i32 min_room_size;
i32 max_room_size;
i32 corridor_width;
} pxl8_procgen_dungeon_params;
i32 num_rooms;
} pxl8_procgen_params;
typedef struct pxl8_procgen_tex_params {
char name[16];

View file

@ -255,9 +255,8 @@ static const char* pxl8_ffi_cdefs =
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n"
"\n"
"typedef enum pxl8_procgen_type {\n"
" PXL8_PROCGEN_CAVE = 0,\n"
" PXL8_PROCGEN_DUNGEON = 1,\n"
" PXL8_PROCGEN_TERRAIN = 2\n"
" PXL8_PROCGEN_ROOMS = 0,\n"
" PXL8_PROCGEN_TERRAIN = 1\n"
"} pxl8_procgen_type;\n"
"\n"
"typedef struct pxl8_procgen_params {\n"
@ -266,9 +265,9 @@ static const char* pxl8_ffi_cdefs =
" int height;\n"
" int depth;\n"
" unsigned int seed;\n"
" float density;\n"
" int iterations;\n"
" void* type_params;\n"
" int min_room_size;\n"
" int max_room_size;\n"
" int num_rooms;\n"
"} pxl8_procgen_params;\n"
"\n"
"typedef struct pxl8_procgen_tex_params {\n"

View file

@ -38,7 +38,12 @@ void pxl8_world_destroy(pxl8_world* world) {
}
pxl8_result pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_procgen_params* params) {
if (!world || !gfx || !params) return PXL8_ERROR_INVALID_ARGUMENT;
pxl8_debug("pxl8_world_generate called");
if (!world || !gfx || !params) {
pxl8_error("Invalid arguments to pxl8_world_generate");
return PXL8_ERROR_INVALID_ARGUMENT;
}
if (world->loaded) {
pxl8_bsp_destroy(&world->bsp);
@ -49,11 +54,12 @@ pxl8_result pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_pro
pxl8_result result = pxl8_procgen(&world->bsp, params);
if (result != PXL8_OK) {
pxl8_error("Failed to generate world");
pxl8_error("Failed to generate world: %d", result);
pxl8_bsp_destroy(&world->bsp);
return result;
}
pxl8_debug("World generation succeeded, setting loaded=true");
world->loaded = true;
return PXL8_OK;
}