diff --git a/maze.c b/maze.c index 17003b8..74d543e 100644 --- a/maze.c +++ b/maze.c @@ -10,7 +10,7 @@ #include "unigi/unigi.platform.sdl1/src/main.c" #include "ecs2.h" -#include "ecs2_comps.h" +// #include "ecs2_comps.h" #include "keys.h" #include @@ -99,9 +99,34 @@ int maze_connected(uint8_t *maze, int x, int y, int size, uint8_t move) { return 0; } +// Calculate which direction from the given position would have you +// facing down the longest hallway +uint8_t maze_longesthallway(uint8_t *maze, int size, int x, int y) { + uint8_t face = DIRNORTH; + uint8_t maxface = face; + uint16_t maxdist = 0; + while (face) { + uint16_t dist = 0; + int dx = x, dy = y; + struct vec2i move = dirtovec(face); + while (maze_connected(maze, dx, dy, size, face)) { + dx += move.x; + dy += move.y; + dist++; + } + if (dist > maxdist) { + maxface = face; + maxdist = dist; + } + face >>= 1; + } + return maxface; +} + // Generate a (square) maze. Utilize one bit of the maze (#2) to // indicate whether it is visited -void maze_generate(uint8_t *maze, int size) { +void maze_generate(uint8_t *maze, int size, struct vec2i *start, + struct vec2i *end) { const int mazesquare = (size) * (size); for (int i = 0; i < mazesquare; i++) { maze[i] = MAZENORTH | MAZEEAST; @@ -112,9 +137,12 @@ void maze_generate(uint8_t *maze, int size) { mazestack[i] = -1; } // Push current cell onto stack, mark as visited - int x = size / 2; - int y = size / 2; + start->x = rand() % size; + start->y = rand() % size; + int x = start->x; + int y = start->y; int mazetop = 0; + int maxmazetop = 0; STACKPUSH(mazestack, mazetop, x + y * size); maze[x + y * size] |= MAZEVISIT; uint8_t visitable[4]; @@ -122,6 +150,13 @@ void maze_generate(uint8_t *maze, int size) { // Now let's make a maze! while (mazetop) { + // The end of the maze is the furthest into the stack we go. This should + // somewhat maximize the complexity of start to finish? + if (mazetop > maxmazetop) { + end->x = x; + end->y = y; + maxmazetop = mazetop; + } mazetop--; visittop = 0; x = mazestack[mazetop] % size; @@ -162,6 +197,9 @@ void maze_generate(uint8_t *maze, int size) { } } + eprintf("Maze generate: %d,%d -> %d,%d maxdepth: %d\n", start->x, start->y, + end->x, end->y, maxmazetop); + free(mazestack); } @@ -184,14 +222,170 @@ void maze_wall_generate(uint8_t *maze, int size, haloo3d_obj *obj) { } } -// A general position within the maze, and a pointer to the maze itself. -// Can be used to traverse the maze +// Turning into a blob? IDK. Maybe certain things should be +// global state, which is this. typedef struct { - uint8_t *maze; - struct vec2i pos; // tile position within maze - int size; - uint8_t dir; // facing dir (see DIREAST/DIRSOUTH/etc); -} ecs_maze; + // float timedelta; + int fps; + uint8_t state; + uint8_t maze[MAZESIZE * MAZESIZE]; + // int size; + // A suggested start and end. The end is the actual + // end, where we put the ending indicator + struct vec2i start; + struct vec2i end; + // Some simple calcs for you to use + mfloat_t cellsize; +} worldstate; + +enum { + WSTATE_INIT = 0, + WSTATE_SPINUP = 1, + WSTATE_GAMEPLAY = 2, + WSTATE_GAMEOVER = 3, + WSTATE_SPINDOWN = 4 +}; + +// A component that stores a position and a facing direction +// as an angle +typedef struct { + struct vec3 pos; + struct vec2 rot; // x is yaw, y is pitch +} ecs_placement; + +// A component representing movement through the scene. +// typedef struct { +// struct vec3 posvel; +// mfloat_t yawvel; +// } ecs_movement; + +// A component which links back to a camera +typedef struct { + haloo3d_camera *camera; +} ecs_camera; + +// A component which allows automatic navigation +// of position through the use of timers. +typedef struct { + struct vec3 dest; + int timer; +} ecs_autonav; + +// A component which allows automatic rotation +// through the use of timers. +typedef struct { + struct vec2 dest; // x is yaw, y is pitch + int timer; +} ecs_autorotate; + +// A component which has pointers back to global world state. +typedef struct { + worldstate *state; +} ecs_worldstate; + +// A component which holds onto data specifically for world +// entities. You can think of it as private data so people +// can't poke at the worldstate data +typedef struct { + worldstate *state; + haloo3d_obj_instance *walls; + uint16_t timer; +} ecs_world; + +// void sys_move_placement(ecs_movement *m, ecs_placement *p) { +// vec3_add(p->pos.v, p->pos.v, m->posvel.v); +// p->yaw += m->yawvel; +// } + +void sys_autonav(ecs_autonav *nav, ecs_placement *p) { + if (nav->timer <= 0) { + p->pos = nav->dest; + return; + } + mfloat_t xdiff = nav->dest.x - p->pos.x; + mfloat_t ydiff = nav->dest.y - p->pos.y; + mfloat_t zdiff = nav->dest.z - p->pos.z; + p->pos.x += xdiff / nav->timer; + p->pos.y += ydiff / nav->timer; + p->pos.z += zdiff / nav->timer; + nav->timer--; +} + +void sys_autorotate(ecs_autorotate *arot, ecs_placement *p) { + if (arot->timer <= 0) { + p->rot = arot->dest; + return; + } + mfloat_t xdiff = arot->dest.x - p->rot.x; + mfloat_t ydiff = arot->dest.y - p->rot.y; + p->rot.x += xdiff / arot->timer; + p->rot.y += ydiff / arot->timer; + arot->timer--; +} + +void sys_camera(ecs_camera *cam, ecs_placement *p) { + cam->camera->pos = p->pos; + cam->camera->yaw = p->rot.x; + cam->camera->pitch = p->rot.y; +} + +void sys_world(ecs_world *w) { + float scaleleft; + const int spinspeed = w->state->fps * 4 / 5; + switch (w->state->state) { + case WSTATE_INIT: + maze_generate(w->state->maze, MAZESIZE, &w->state->start, &w->state->end); + maze_wall_generate(w->state->maze, MAZESIZE, w->walls->model); + eprintf("INIT MAZE COMPLETE, spinning up walls\n"); + w->state->state = WSTATE_SPINUP; + w->timer = spinspeed; + w->walls->scale.y = 0; + break; + case WSTATE_SPINUP: + // Bring walls up, timer down + scaleleft = 1 - w->walls->scale.y; + w->walls->scale.y += scaleleft / w->timer; + w->timer--; + if (w->timer == 0) { + eprintf("SPINUP COMPLETE, starting gameplay\n"); + w->walls->scale.y = 1; + // Start gameplay. We won't know when it's done + w->state->state = WSTATE_GAMEPLAY; + } + break; + case WSTATE_GAMEOVER: + w->timer = spinspeed; + eprintf("GAME OVER, spinning down\n"); + w->state->state = WSTATE_SPINDOWN; + break; + case WSTATE_SPINDOWN: + // Bring walls sown, timer down + scaleleft = w->walls->scale.y; + w->walls->scale.y -= scaleleft / w->timer; + w->timer--; + if (w->timer == 0) { + eprintf("SPINDOWN COMPLETE, reinitializing\n"); + w->walls->scale.y = 0; + // Start gameplay. We won't know when it's done + w->state->state = WSTATE_INIT; + } + break; + } +} + +// // A general position within the maze, and a pointer to the maze itself. +// // Can be used to traverse the maze +// typedef struct { +// uint8_t *maze; +// struct vec2i pos; // tile position within maze +// int size; +// uint8_t dir; // facing dir (see DIREAST/DIRSOUTH/etc); +// } ecs_maze; + +// // Use a timer and a destination to move simply through +// typedef struct { +// +// } ecs_mazemove; // State for tracking ai moving through the maze. typedef struct { @@ -200,17 +394,18 @@ typedef struct { uint32_t timer; } ecs_smartai; +// typedef struct { +// uint8_t *maze; +// // What state the game is in, such as game initialize, animation, running, +// etc uint8_t state; +// } ecs_world; + // everything in the maze is controlled by the CPU. As such, movement // is as simple as "go here by this time". No need to complicate the // components? +/* static void sys_ecs_smartai(ecs_smartai *smartai, ecs_maze *maze, ecs_moveto *mt, ecs_rotateto *rt) { - // haloo_ecs *ecs, hecs_eidt id, hecs_cidt said, hecs_cidt mzid, hecs_cidt - // mtid, hecs_cidt rtid) { - // ecs_smartai *smartai = HECS_ENTITYCOMPONENT(ecs_smartai *, id, said, ecs); - // ecs_maze *maze = HECS_ENTITYCOMPONENT(ecs_maze *, id, mzid, ecs); - // ecs_moveto *mt = HECS_ENTITYCOMPONENT(ecs_moveto *, id, mtid, ecs); - // ecs_rotateto *rt = HECS_ENTITYCOMPONENT(ecs_rotateto *, id, rtid, ecs); int actiontime = fps / 2; // Some states are triggered based on the timer if (smartai->timer > 0) { @@ -273,60 +468,91 @@ static void sys_ecs_smartai(ecs_smartai *smartai, ecs_maze *maze, } } } +*/ + +// static void sys_ecs_world(ecs_world *world, ecs_maze *maze, ecs_moveto *mt, +// ecs_rotateto *rt) {} // Setup ECS system for our game ECS_START(mecs) -ECS_COMPONENT(ecs_moveto); -ECS_COMPONENT(ecs_rotateto); -ECS_COMPONENT(ecs_smartai); +ECS_COMPONENT(ecs_worldstate); +ECS_COMPONENT(ecs_world); +ECS_COMPONENT(ecs_autonav); +ECS_COMPONENT(ecs_autorotate); +ECS_COMPONENT(ecs_placement); ECS_COMPONENT(ecs_camera); -ECS_COMPONENT(ecs_maze); +// ECS_COMPONENT(ecs_moveto); +// ECS_COMPONENT(ecs_rotateto); +// ECS_COMPONENT(ecs_smartai); +// ECS_COMPONENT(ecs_camera); +// ECS_COMPONENT(ecs_maze); ECS_END(mecs) // And then a copy of the components here... that sucksssss -ECS_CID(ecs_moveto, 0); -ECS_CID(ecs_rotateto, 1); -ECS_CID(ecs_smartai, 2); -ECS_CID(ecs_camera, 3); -ECS_CID(ecs_maze, 4); +ECS_CID(ecs_worldstate, 0); +ECS_CID(ecs_world, 1); +ECS_CID(ecs_autonav, 2); +ECS_CID(ecs_autorotate, 3); +ECS_CID(ecs_placement, 4); +ECS_CID(ecs_camera, 5); -ECS_SYSTEM1(mecs, sys_ecs_moveto, ecs_moveto); -ECS_SYSTEM1(mecs, sys_ecs_rotateto, ecs_rotateto); -ECS_SYSTEM2(mecs, sys_ecs_moveto_camera, ecs_moveto, ecs_camera); -ECS_SYSTEM2(mecs, sys_ecs_rotateto_camera, ecs_rotateto, ecs_camera); -ECS_SYSTEM4(mecs, sys_ecs_smartai, ecs_smartai, ecs_maze, ecs_moveto, - ecs_rotateto); +// ECS_CID(ecs_moveto, 0); +// ECS_CID(ecs_rotateto, 1); +// ECS_CID(ecs_smartai, 2); +// ECS_CID(ecs_camera, 3); +// ECS_CID(ecs_maze, 4); -// ECS_SYSTEM1(mecs, sys2_moveto, ecs_moveto, mt) { -// mt->pos.x = 1; -// } +ECS_SYSTEM1(mecs, sys_world, ecs_world); +ECS_SYSTEM2(mecs, sys_autonav, ecs_autonav, ecs_placement); +ECS_SYSTEM2(mecs, sys_autorotate, ecs_autorotate, ecs_placement); +ECS_SYSTEM2(mecs, sys_camera, ecs_camera, ecs_placement); +// ECS_SYSTEM1(mecs, sys_ecs_moveto, ecs_moveto); +// ECS_SYSTEM1(mecs, sys_ecs_rotateto, ecs_rotateto); +// ECS_SYSTEM2(mecs, sys_ecs_moveto_camera, ecs_moveto, ecs_camera); +// ECS_SYSTEM2(mecs, sys_ecs_rotateto_camera, ecs_rotateto, ecs_camera); +// ECS_SYSTEM4(mecs, sys_ecs_smartai, ecs_smartai, ecs_maze, ecs_moveto, +// ecs_rotateto); + +void init_floortexture(haloo3d_fb *floort) { + // uint16_t cols[4] = {0xFD93, 0xFB83, 0xFFFF}; //, 0xFDDD}; + uint16_t cols[1] = {0xFD93}; + haloo3d_fb_init_tex(floort, 64, 64); + haloo3d_apply_alternating(floort, cols, 1); + haloo3d_apply_noise(floort, NULL, 1.0 / 6); +} + +void init_ceilingtexture(haloo3d_fb *ceilt) { + uint16_t cols[1] = {0xFFFF}; //, 0xFDDD}; + // haloo3d_apply_alternating(floort, cols, 2); + haloo3d_fb_init_tex(ceilt, 64, 64); + haloo3d_apply_alternating(ceilt, cols, 1); + haloo3d_apply_noise(ceilt, NULL, 1.0 / 4); + haloo3d_apply_brick(ceilt, 16, 8, 0xFAAA); +} + +void init_walltexture(haloo3d_fb *wallt) { + haloo3d_fb_init_tex(wallt, 64, 64); + uint16_t wallcols[] = {0xFA22}; + haloo3d_apply_alternating(wallt, wallcols, 1); + haloo3d_apply_noise(wallt, NULL, 1.0 / 8); + haloo3d_apply_brick(wallt, 18, 13, 0xFEEE); + // haloo3d_apply_brick(wallt, 14, 8, 0xFD94); + haloo3d_apply_noise(wallt, NULL, 1.0 / 8); +} int main() { // int argc, char **argv) { haloo3d_easystore storage; haloo3d_easystore_init(&storage); - haloo3d_debugconsole dc; - haloo3d_debugconsole_init(&dc); - haloo3d_fb screen; haloo3d_fb_init(&screen, SWIDTH, SHEIGHT); haloo3d_easyrender render; haloo3d_easyrender_init(&render, WIDTH, HEIGHT); render.camera.pos.y = 0.5; - eprintf("Initialized renderer\n"); - - haloo3d_debugconsole_set(&dc, "render/fps.i", &fps); - haloo3d_debugconsole_set(&dc, "render/fov.f", &fov); - haloo3d_debugconsole_set(&dc, "render/trifunc.i", &render.trifunc); - haloo3d_debugconsole_set(&dc, "render/ditherstart.f", &ditherstart); - haloo3d_debugconsole_set(&dc, "render/ditherend.f", &ditherend); - haloo3d_debugconsole_set(&dc, "render/sky.u16x", &sky); - haloo3d_debugconsole_set(&dc, "camera/pos_y.f", &render.camera.pos.y); - haloo3d_debugconsole_set(&dc, "camera/pitch.f", &render.camera.pitch); - render.tprint.fb = &screen; + eprintf("Initialized renderer\n"); haloo3d_easytimer frametimer, sdltimer, filltimer; haloo3d_easytimer_init(&frametimer, AVGWEIGHT); @@ -340,36 +566,22 @@ int main() { // int argc, char **argv) { haloo3d_fb *floort = haloo3d_easystore_addtex(&storage, "floor"); haloo3d_fb *ceilt = haloo3d_easystore_addtex(&storage, "ceiling"); haloo3d_fb *wallt = haloo3d_easystore_addtex(&storage, "walls"); + haloo3d_gen_plane(flooro, MAZESIZE); haloo3d_gen_plane(ceilo, MAZESIZE); haloo3d_gen_grid(wallo, MAZESIZE, 0); + init_floortexture(floort); + init_ceilingtexture(ceilt); + init_walltexture(wallt); - // no generic maze generator, we just do it raw. Each cell has a byte which - // indicates if the wall to the NORTH (#0 bit) and the wall to the WEST (#1 - // bit) are solid. Because of this, we need one additional row and column - uint8_t maze[MAZESIZE * MAZESIZE]; - maze_generate(maze, MAZESIZE); - maze_wall_generate(maze, MAZESIZE, wallo); - - uint16_t cols[4] = {0xFD93, 0xFB83, 0xFFFF}; //, 0xFDDD}; - haloo3d_fb_init_tex(floort, 64, 64); - haloo3d_apply_alternating(floort, cols, 1); - haloo3d_apply_noise(floort, NULL, 1.0 / 6); - // haloo3d_apply_alternating(floort, cols, 2); - haloo3d_fb_init_tex(ceilt, 64, 64); - haloo3d_apply_alternating(ceilt, cols + 2, 1); - haloo3d_apply_noise(ceilt, NULL, 1.0 / 4); - haloo3d_apply_brick(ceilt, 16, 8, 0xFAAA); - - haloo3d_fb_init_tex(wallt, 64, 64); - uint16_t wallcols[] = {0xFA22}; - haloo3d_apply_alternating(wallt, wallcols, 1); - haloo3d_apply_noise(wallt, NULL, 1.0 / 8); - haloo3d_apply_brick(wallt, 18, 13, 0xFEEE); - // haloo3d_apply_brick(wallt, 14, 8, 0xFD94); - haloo3d_apply_noise(wallt, NULL, 1.0 / 8); eprintf("Initialized models and textures\n"); + worldstate wstate; + memset(&wstate, 0, sizeof(worldstate)); + wstate.fps = fps; + wstate.state = WSTATE_INIT; + wstate.cellsize = HSCALE; + // Lighting. Note that for performance, the lighting is always calculated // against the base model, and is thus not realistic if the object rotates in // the world. This can be fixed easily, since each object gets its own @@ -412,7 +624,6 @@ int main() { // int argc, char **argv) { render.totalverts); // Init unigi system - // sprintf(render.printbuf, "maze.exe - %s %s %s", argv[1], argv[2], argv[3]); unigi_graphics_init(); unigi_window_create(res, "maze.exe"); // render.printbuf); @@ -420,34 +631,53 @@ int main() { // int argc, char **argv) { // render.camera.pitch = MPI - 0.1; // 2.2; // ceili->pos.y = -10; + haloo3d_debugconsole dc; + haloo3d_debugconsole_init(&dc); + + haloo3d_debugconsole_set(&dc, "render/fps.i", &fps); + haloo3d_debugconsole_set(&dc, "render/fov.f", &fov); + haloo3d_debugconsole_set(&dc, "render/trifunc.i", &render.trifunc); + haloo3d_debugconsole_set(&dc, "render/ditherstart.f", &ditherstart); + haloo3d_debugconsole_set(&dc, "render/ditherend.f", &ditherend); + haloo3d_debugconsole_set(&dc, "render/sky.u16x", &sky); + haloo3d_debugconsole_set(&dc, "camera/pos_y.f", &render.camera.pos.y); + haloo3d_debugconsole_set(&dc, "camera/pitch.f", &render.camera.pitch); haloo3d_debugconsole_set(&dc, "obj/ceil/pos_y.f", &ceili->pos.y); - // Set up the various systems + // Set up ECS entities. For this game, we mostly have global entities. mecs ecs; mecs_init(&ecs); + ecs_eid worldid = mecs_newentity(&ecs, 0); + eprintf("World eid: %d\n", worldid); + ECS_SETCOMPONENT(&ecs, worldid, + ecs_world){.state = &wstate, .walls = walli, .timer = 0}; + ecs_eid playerid = mecs_newentity(&ecs, 0); - if (playerid == -1) { - dieerr("WHY IS PLAYERID -1???\n"); - } eprintf("Player eid: %d\n", playerid); + ECS_SETCOMPONENT(&ecs, playerid, ecs_worldstate){.state = &wstate}; + ECS_SETCOMPONENT(&ecs, playerid, ecs_camera){.camera = &render.camera}; + ECS_SETCOMPONENT(&ecs, playerid, ecs_autonav){.timer = 0}; + ECS_SETCOMPONENT(&ecs, playerid, ecs_autorotate){.timer = 0}; - // System is setup such that camera position matches maze index - render.camera.pos.x = 0.5 * HSCALE; - render.camera.pos.z = 0.5 * HSCALE; - struct vec2i playerstart = {.x = 0, .y = 0}; - struct vec2 playerrotation = {.x = render.camera.yaw, - .y = render.camera.pitch}; - ECS_SETCOMPONENT(&ecs, playerid, ecs_moveto){ - .pos = render.camera.pos, .dst = render.camera.pos, .timer = 0}; - ECS_SETCOMPONENT(&ecs, playerid, ecs_rotateto){ - .rot = playerrotation, .dstrot = playerrotation, .timer = 0}; - ECS_SETCOMPONENT(&ecs, playerid, ecs_maze){ - .maze = maze, .pos = playerstart, .size = MAZESIZE, .dir = DIRSOUTH}; - ECS_SETCOMPONENT(&ecs, playerid, ecs_camera) & render.camera; - ECS_SETCOMPONENT(&ecs, playerid, ecs_smartai){.state = 0, .timer = 0}; + // if (playerid == -1) { + // dieerr("WHY IS PLAYERID -1???\n"); + // } - eprintf("Player component mask: %lx\n", ecs.entities[playerid]); + // // System is setup such that camera position matches maze index + // render.camera.pos.x = 0.5 * HSCALE; + // render.camera.pos.z = 0.5 * HSCALE; + + // struct vec2i playerstart = {.x = 0, .y = 0}; + // struct vec2 playerrotation = {.x = render.camera.yaw, + // .y = render.camera.pitch}; + // ECS_SETCOMPONENT(&ecs, playerid, ecs_rotateto){ + // .rot = playerrotation, .dstrot = playerrotation, .timer = 0}; + // ECS_SETCOMPONENT(&ecs, playerid, ecs_maze){ + // .maze = maze, .pos = playerstart, .size = MAZESIZE, .dir = DIRSOUTH}; + // ECS_SETCOMPONENT(&ecs, playerid, ecs_camera) & render.camera; + // ECS_SETCOMPONENT(&ecs, playerid, ecs_smartai){.state = 0, .timer = 0}; + // eprintf("Player component mask: %lx\n", ecs.entities[playerid]); // ----------------------------------- // Actual rendering @@ -468,29 +698,39 @@ int main() { // int argc, char **argv) { // walli->up.x = sin(3 * render.camera.yaw); // walli->up.y = cos(4 * render.camera.yaw); - unigi_event_get(&event); - if (event.type == unigi_enum_event_input_keyboard) { - if (event.data.input_keyboard.down) { - switch (event.data.input_keyboard.button) { - case KEY_SPACE: - haloo3d_debugconsole_beginprompt(&dc); - break; - default: - exit(0); + do { + unigi_event_get(&event); + switch (event.type) { + case unigi_enum_event_input_keyboard: + if (event.data.input_keyboard.down) { + switch (event.data.input_keyboard.button) { + case KEY_SPACE: + haloo3d_debugconsole_beginprompt(&dc); + break; + default: + exit(0); + } } + break; } - } + } while (event.type != unigi_enum_event_none); // --------------------------- // Game logic? // --------------------------- for (int i = 0; i < ECS_MAXENTITIES; i++) { - sys_ecs_smartai_run(&ecs, i); - sys_ecs_moveto_run(&ecs, i); - sys_ecs_rotateto_run(&ecs, i); - sys_ecs_moveto_camera_run(&ecs, i); - sys_ecs_rotateto_camera_run(&ecs, i); + sys_world_run(&ecs, i); + + sys_autonav_run(&ecs, i); + sys_autorotate_run(&ecs, i); + sys_camera_run(&ecs, i); + // sys_ecs_world_run(&ecs, i); + // sys_ecs_smartai_run(&ecs, i); + // sys_ecs_moveto_run(&ecs, i); + // sys_ecs_rotateto_run(&ecs, i); + // sys_ecs_moveto_camera_run(&ecs, i); + // sys_ecs_rotateto_camera_run(&ecs, i); } totaldrawn = 0; @@ -543,7 +783,7 @@ int main() { // int argc, char **argv) { } // Just to get the compiler to STOP COMPLAINING about unused - mecs_deleteentity(&ecs, playerid); + mecs_deleteentity(&ecs, worldid); haloo3d_easystore_deleteallobj(&storage, haloo3d_obj_free); haloo3d_easystore_deletealltex(&storage, haloo3d_fb_free);