From 99ab9bd22be2506c23d1e379f35583ddab4eadf6 Mon Sep 17 00:00:00 2001 From: zlago Date: Fri, 4 Oct 2024 21:38:14 +0200 Subject: map transitions --- src/entity.h | 11 +++ src/funsinit.c | 3 + src/loader.c | 2 +- src/loader.h | 7 +- src/main.c | 290 +++++++++++++++++++++++++++++++++++++++++---------------- src/main.h | 19 +++- src/player.c | 12 +-- src/warp.c | 62 ++++++++++++ 8 files changed, 313 insertions(+), 93 deletions(-) create mode 100644 src/warp.c (limited to 'src') diff --git a/src/entity.h b/src/entity.h index 354fbc1..fe76feb 100644 --- a/src/entity.h +++ b/src/entity.h @@ -9,6 +9,7 @@ struct entity { int (*update)(struct entity *self); int (*hurt)(struct entity *self, int damage); int (*draw)(struct entity *self, int camx, int camy); + void (*free)(struct entity *self); int x, y; // unsigned results in a bunch of weird edge cases struct velocity { signed x, y; @@ -29,3 +30,13 @@ struct entity { void *texture; void *ext; }; + +struct warp { + int (*update)(struct warp *); + unsigned x, y; + unsigned w, h; + unsigned tox, toy; + struct hitbox hitbox; + unsigned timer; + void *ext; +}; diff --git a/src/funsinit.c b/src/funsinit.c index 5d15cc4..25ba630 100644 --- a/src/funsinit.c +++ b/src/funsinit.c @@ -2,6 +2,9 @@ #include "loader.h" #include "main.h" +void *warp_new(struct entities *entities); +int warp_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); void funs_init(void) { + res_push_fun(warp_new, warp_property, "warp"); } diff --git a/src/loader.c b/src/loader.c index f132b7a..2382246 100644 --- a/src/loader.c +++ b/src/loader.c @@ -226,7 +226,7 @@ void res_push_collision(struct blob *blob, name_T name) { collisions = res_push_blob(blob, name, collisions, blob_free_func); } -void res_push_fun(struct entity *(*newfun)(void), int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name) { +void res_push_fun(void *(*newfun)(struct entities *entities), int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value), name_T name) { int found; struct fun_item new = {.funs = {.newfun = newfun, .setfun = setfun}, .name = name}; struct fun_item *dst = bsearchinsertposition(&new, funs.items, funs.count, sizeof (struct fun_item), res_compare_funs, &found); diff --git a/src/loader.h b/src/loader.h index 61f9e3c..b16fd28 100644 --- a/src/loader.h +++ b/src/loader.h @@ -2,6 +2,7 @@ #include #include "entity.h" +#include "main.h" typedef char * name_T; @@ -11,8 +12,8 @@ struct blob { }; struct funs { - struct entity *(*newfun)(void); - int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value); + void *(*newfun)(struct entities *entities); + int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value); }; void res_init(void); @@ -30,7 +31,7 @@ void res_free_collision(void); struct blob res_get_collision(name_T const name); void res_free_fun(void); -void res_push_fun(struct entity *(*newfun)(void), int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name); +void res_push_fun(void *(*newfun)(struct entities *entities), int (*setfun)(void *const restrict self, char const *const restrict key, char const *const restrict value), name_T name); struct funs res_get_fun(name_T const name); int loadResources(char *filename); diff --git a/src/main.c b/src/main.c index 2d3e89d..c48deef 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,8 @@ SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; +enum game_state game_state = STATE_PLAYING; +char *game_next_level; #define WINDOW_WIDTH 160 #define WINDOW_HEIGHT 90 @@ -34,13 +36,134 @@ SDL_Scancode keybinds[] = { SDL_SCANCODE_S, }; -struct entity player[1] = {0}; +struct entities entities = {{{0}}, {{0}}, 0, {{0}}, 0}, next_entities = {{{0}}, {{0}}, 0, {{0}}, 0}; -struct entity *player_new(void); -int player_property(struct entity *const restrict entity, char const *const restrict property, char const *const restrict value); +void *player_new(struct entities *entities); +int player_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); void funs_init(void); +void entities_free(struct entities *entities) { + for (int i = 0, e = 0; i < 64 && e < entities->enemies; i++) { + if (entities->enemy[i].state) { + e++; + if (entities->enemy[i].free != NULL) { + entities->enemy[i].free(entities->enemy + i); + } + } + } + entities->enemies = 0; + #if 0 + for (int i = 0; i < entities.warps; i++) { + entities.warp[i].update(entities.warp + i); + } + #endif + entities->warps = 0; +} + +int entities_load(struct entities *entities, char *data, size_t size, size_t input_bytes) { + entities->enemies = 0; + entities->warps = 0; + data += input_bytes; + size -= input_bytes; + while (size) { + size_t len; + if ((len = strnlen(data, size)) == size) { + return 1; + } + char *name = data; + struct funs fun = res_get_fun(name); + data += len + 1; + size -= len + 1; + if (fun.newfun) { + struct entity *entity = fun.newfun(&next_entities); + while (1) { + if ((len = strnlen(data, size)) == size) { + return 1; + } else if (len == 0) { + data++; + size--; + break; + } + char *key = data; + data += len + 1; + size -= len + 1; + if ((len = strnlen(data, size)) == size) { + return 1; + } + char *value = data; + data += len + 1; + size -= len + 1; + if (fun.setfun(entity, key, value)) { + puts(key); puts(value); + return 1; + } + } + } else { + fprintf(stderr, "warn: unknown entity %s\n", name); + while (1) { + if ((len = strnlen(data, size)) == size) { + return 1; + } else if (len == 0) { + data++; + size--; + break; + } + data += len + 1; + size -= len + 1; + if ((len = strnlen(data, size)) == size) { + return 1; + } + data += len + 1; + size -= len + 1; + } + } + } + return 0; +} + +int game_update(void) { + if (entities.player[0].update(entities.player)) { + return 1; + } + + for (int i = 0, e = 0; i < 64 && e < entities.enemies; i++) { + if (entities.enemy[i].state) { + e++; + entities.enemy[i].update(entities.enemy + i); + } + } + + for (int i = 0; i < entities.warps; i++) { + entities.warp[i].update(entities.warp + i); + } + + return 0; +} + +void game_render(SDL_Texture *framebuffer, int x, int y) { + SDL_SetRenderTarget(renderer, framebuffer); + tilemap_background(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT); + entities.player[0].draw(entities.player, x, y); + for (int i = 0, e = 0; i < 64 && e < entities.enemies; i++) { + if (entities.enemy[i].state) { + e++; + entities.enemy[i].draw(entities.enemy + i, x, y); + } + } + tilemap_foreground(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT); + //SDL_RenderCopy(renderer, res_get_texture("meow").data, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90}); +} + +void game_render_flush(SDL_Texture *framebuffer) { + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); + // then we wait for the next video frame + SDL_RenderPresent(renderer); +} + int main(int const argc, char *const *const argv) { struct option opts[] = { {"help", 0, NULL, 'h'}, @@ -176,60 +299,19 @@ int main(int const argc, char *const *const argv) { optind++; } } - struct blob map = res_get_map("untitled"); - tilemap = tilemap_load(map.data, map.size); - char *a = (char *) map.data + tilemap->input_bytes; - map.size -= tilemap->input_bytes; - while (map.size) { - size_t len; - if ((len = strnlen(a, map.size)) == map.size) { - return 1; // hack - } - char *name = a; - puts(name); - struct funs fun = res_get_fun(name); - a += len + 1; - map.size -= len + 1; - if (fun.newfun) { - struct entity *entity = fun.newfun(); - while (1) { - if ((len = strnlen(a, map.size)) == map.size) { - return 1; // hack - } else if (len == 0) { - a++; - map.size--; - break; - } - char *key = a; - a += len + 1; - map.size -= len + 1; - if ((len = strnlen(a, map.size)) == map.size) { - return 1; // hack - } - char *value = a; - a += len + 1; - map.size -= len + 1; - if (fun.setfun(entity, key, value)) { - return 1; // hack - } - } - } else { - while (1) { // hack - if ((len = strnlen(a, map.size)) == map.size) { // hack - return 1; // hack - } else if (len == 0) { - a++; - map.size--; - break; - } - a += len + 1; - map.size -= len + 1; - } - } + + struct blob blob = res_get_map("untitled"); + next_tilemap = tilemap_load(blob.data, blob.size); + if (next_tilemap == NULL) { + fputs("failed to load initial tilemap\n", stderr); + goto end; } - //memmem(map.data + tilemap->byte_count, map.size - tilemap->byte_count, (char []) {0, 0}, 2); - printf("load_tilemap %p\n", (void *) tilemap); - SDL_SetRenderTarget(renderer, NULL); + if (entities_load(&entities, blob.data, blob.size, next_tilemap->input_bytes)) { + tilemap_free(next_tilemap); + fputs("failed to load initial tilemap\n", stderr); + goto end; + } + tilemap = next_tilemap; } /* unsigned error; // identical variable is declared higher up */ @@ -251,10 +333,11 @@ int main(int const argc, char *const *const argv) { SDL_ShowWindow(window); - int x = 0, y = 0; - player_new(); - player_property(player, "x", "40"); - player_property(player, "y", "64"); + int x = 0, y = 0, fade = 0; + player_new(&next_entities); + player_property(next_entities.player, "x", "40"); + player_property(next_entities.player, "y", "64"); + memcpy(&entities, &next_entities, sizeof (entities)); while (1) { SDL_Event evt; while (SDL_PollEvent(&evt)) { @@ -352,29 +435,72 @@ int main(int const argc, char *const *const argv) { } } - //SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90}); - if (player[0].update(player)) { - break; // TODO: game over state + switch (game_state) { + case STATE_PLAYING: + //SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90}); + if (game_update()) { + goto end; // TODO: game over state + } + + x = (entities.player[0].x / 16) - (WINDOW_WIDTH / 2); + y = (entities.player[0].y / 16) - (WINDOW_HEIGHT / 2); + if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;} + if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;} + + game_render(framebuffer, x, y); + game_render_flush(framebuffer); + break; + + case STATE_FADE_IN: + fade += 15; + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, fade); + SDL_RenderFillRect(renderer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); + SDL_RenderPresent(renderer); + if (fade == 255) { + struct blob blob = res_get_map(game_next_level); + next_tilemap = tilemap_load(blob.data, blob.size); + if (next_tilemap != NULL) { + if (entities_load(&next_entities, blob.data, blob.size, next_tilemap->input_bytes)) { + tilemap_free(next_tilemap); + } else { + tilemap_free(tilemap); + tilemap = next_tilemap; + entities_free(&entities); + memcpy(&entities, &next_entities, sizeof (entities)); + } + } + game_state = STATE_FADE_OUT; + + x = (entities.player[0].x / 16) - (WINDOW_WIDTH / 2); + y = (entities.player[0].y / 16) - (WINDOW_HEIGHT / 2); + if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;} + if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;} + + game_render(framebuffer, x, y); + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + } + break; + + case STATE_FADE_OUT: + fade -= 15; + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, fade); + SDL_RenderFillRect(renderer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); + SDL_RenderPresent(renderer); + if (fade == 0) { + game_state = STATE_PLAYING; + } + break; } - - //x += input_right(input_now) - input_left(input_now); - //y += input_down(input_now) - input_up(input_now); - x = (player[0].x / 16) - (WINDOW_WIDTH / 2); - y = (player[0].y / 16) - (WINDOW_HEIGHT / 2); - if (x < 0) {x = 0;} else if (x + WINDOW_WIDTH > tilemap->width * 8) {x = tilemap->width * 8 - WINDOW_WIDTH;} - if (y < 0) {y = 0;} else if (y + WINDOW_HEIGHT > tilemap->height * 8) {y = tilemap->height * 8 - WINDOW_HEIGHT;} - - SDL_SetRenderTarget(renderer, framebuffer); - tilemap_background(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT); - player[0].draw(player, x, y); - tilemap_foreground(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT); - //SDL_RenderCopy(renderer, res_get_texture("meow").data, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90}); - SDL_SetRenderTarget(renderer, NULL); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, framebuffer, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, &(SDL_Rect) {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}); - // then we wait for the next video frame - SDL_RenderPresent(renderer); } end: diff --git a/src/main.h b/src/main.h index ad0951f..d743ce7 100644 --- a/src/main.h +++ b/src/main.h @@ -7,4 +7,21 @@ extern SDL_Renderer *renderer; extern unsigned input_now; -extern struct entity player[1]; +extern struct entities { + struct entity player[1]; + struct entity enemy[64]; + unsigned enemies; + struct warp warp[16]; + unsigned warps; +} entities, next_entities; + +extern enum game_state { + STATE_PLAYING, + STATE_FADE_IN, + STATE_FADE_OUT, +} game_state; + +extern char *game_next_level; + +void entities_free(struct entities *entities); +int entities_load(struct entities *entities, char *data, size_t size, size_t input_bytes); diff --git a/src/player.c b/src/player.c index b23b2f0..0f0025e 100644 --- a/src/player.c +++ b/src/player.c @@ -114,7 +114,7 @@ static int move(struct entity *self) { return on_ground; } -void anim(struct entity *self, unsigned anim) { +static void anim(struct entity *self, unsigned anim) { self->anim = player_anims[anim]; } @@ -210,8 +210,8 @@ static int player_draw(struct entity *self, int camX, int camY) { return 0; } -struct entity *player_new(void) { - player[0] = (struct entity) { +struct entity *player_new(struct entities *entities) { + entities->player[0] = (struct entity) { .update = player_update, .hurt = player_hurt, .draw = player_draw, @@ -228,9 +228,9 @@ struct entity *player_new(void) { .texture = NULL, .ext = NULL, }; - anim(player, PLAYER_A_IDLE); - player[0].texture = res_get_texture("fywi").data; - return player + 0; + anim(entities->player, PLAYER_A_IDLE); + entities->player[0].texture = res_get_texture("fywi").data; + return entities->player + 0; } int player_property(struct entity *const restrict self, char const *const restrict property, char const *const restrict value) { diff --git a/src/warp.c b/src/warp.c new file mode 100644 index 0000000..f36b716 --- /dev/null +++ b/src/warp.c @@ -0,0 +1,62 @@ +#include "main.h" +#include "entity.h" +#include "loader.h" +#include "tilemap.h" + +static int warp_update(struct warp *self) { + if ( + self->hitbox.left < entities.player[0].hitbox.right && + self->hitbox.right > entities.player[0].hitbox.left && + self->hitbox.top < entities.player[0].hitbox.bottom && + self->hitbox.bottom > entities.player[0].hitbox.top + ) { + if (self->ext == NULL) { + return 0; + } + game_next_level = self->ext; + memcpy(next_entities.player, entities.player, sizeof (entities.player)); + next_entities.player[0].x = self->tox * 16; + next_entities.player[0].y = self->toy * 16; + game_state = STATE_FADE_IN; + self->ext = NULL; + return 0; + } + return 0; +} + +struct warp *warp_new(struct entities *entities) { + entities->warp[entities->warps] = (struct warp) { + .update = warp_update, + .x = 0, .y = 0, + .w = 0, .h = 0, + .tox = 0, .toy = 0, + .hitbox = { + 0, 0, 0, 0, + }, + .timer = 0, + .ext = NULL, + }; + return entities->warp + entities->warps++; +} + +int warp_property(struct warp *const restrict self, char const *const restrict property, char const *const restrict value) { + if (strcmp(property, "x") == 0) { + self->x = atoi(value); + } else if (strcmp(property, "y") == 0) { + self->y = atoi(value); + } else if (strcmp(property, "width") == 0) { + self->w = atoi(value); + } else if (strcmp(property, "height") == 0) { + self->h = atoi(value); + } else if (strcmp(property, "tox") == 0) { + self->tox = atoi(value); + } else if (strcmp(property, "toy") == 0) { + self->toy = atoi(value); + } else if (strcmp(property, "map") == 0) { + self->ext = value; + } else { + return 1; + } + self->hitbox = (struct hitbox) {.left = self->x, .top = self->y, .right = self->x + self->w, .bottom = self->y + self->h}; + return 0; +} -- cgit 1.4.1-2-gfad0