From 0f4ca4876cd7e3ac7f05620a6d048f5cd7dac294 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Sat, 17 Feb 2024 12:38:18 +0100 Subject: [PATCH] added smooth game element movements for native BD engine --- src/game_bd/bd_caveengine.c | 15 +++++++++ src/game_bd/bd_elements.h | 3 +- src/game_bd/bd_gameplay.c | 50 +++++++++++++++++++++++++++++ src/game_bd/bd_gameplay.h | 5 +++ src/game_bd/bd_graphics.c | 64 ++++++++++++++++++++++++++++++++++++- src/game_bd/main_bd.c | 4 +++ 6 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/game_bd/bd_caveengine.c b/src/game_bd/bd_caveengine.c index bfb7dfbc..3019aeca 100644 --- a/src/game_bd/bd_caveengine.c +++ b/src/game_bd/bd_caveengine.c @@ -526,6 +526,19 @@ static inline boolean is_space_dir(const GdCave *cave, const int x, const int y, return (e == O_SPACE || e == O_LAVA); } +static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir) +{ + /* raw values without range correction */ + int raw_x = x + gd_dx[dir]; + int raw_y = y + gd_dy[dir]; + /* final values with range correction */ + int new_x = getx(cave, raw_x, raw_y); + int new_y = gety(cave, raw_x, raw_y); + int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir); + + game_bd.game->dir_buffer[new_y][new_x] = new_dir; +} + /* store an element at the given position */ static inline void store(GdCave *cave, const int x, const int y, const GdElement element) { @@ -551,6 +564,7 @@ static inline void store_sc(GdCave *cave, const int x, const int y, const GdElem static inline void store_dir(GdCave *cave, const int x, const int y, const GdDirection dir, const GdElement element) { + store_dir_buffer(cave, x, y, dir); store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED); } @@ -558,6 +572,7 @@ static inline void store_dir(GdCave *cave, const int x, const int y, static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y, const GdDirection dir, const GdElement element) { + store_dir_buffer(cave, x, y, dir); store(cave, x + gd_dx[dir], y + gd_dy[dir], element); } diff --git a/src/game_bd/bd_elements.h b/src/game_bd/bd_elements.h index 87584170..34a0615a 100644 --- a/src/game_bd/bd_elements.h +++ b/src/game_bd/bd_elements.h @@ -308,9 +308,10 @@ typedef enum _element SCANNED = 0x100, COVERED = 0x200, + SKIPPED = 0x400, /* binary AND this to elements to get rid of properties above. */ - O_MASK = ~(SCANNED | COVERED) + O_MASK = ~(SCANNED | COVERED | SKIPPED) } GdElement; typedef enum _sound diff --git a/src/game_bd/bd_gameplay.c b/src/game_bd/bd_gameplay.c index e367d9fb..ffedad2c 100644 --- a/src/game_bd/bd_gameplay.c +++ b/src/game_bd/bd_gameplay.c @@ -31,6 +31,10 @@ void gd_game_free(GdGame *game) if (game->element_buffer) gd_cave_map_free(game->element_buffer); + if (game->last_element_buffer) + gd_cave_map_free(game->last_element_buffer); + if (game->dir_buffer) + gd_cave_map_free(game->dir_buffer); if (game->gfx_buffer) gd_cave_map_free(game->gfx_buffer); @@ -140,6 +144,16 @@ static void load_cave(GdGame *game) gd_cave_map_free(game->element_buffer); game->element_buffer = NULL; + /* delete last element buffer */ + if (game->last_element_buffer) + gd_cave_map_free(game->last_element_buffer); + game->last_element_buffer = NULL; + + /* delete direction buffer */ + if (game->dir_buffer) + gd_cave_map_free(game->dir_buffer); + game->dir_buffer = NULL; + /* delete gfx buffer */ if (game->gfx_buffer) gd_cave_map_free(game->gfx_buffer); @@ -171,6 +185,20 @@ static void load_cave(GdGame *game) for (x = 0; x < game->cave->w; x++) game->element_buffer[y][x] = O_NONE; + /* create new last element buffer */ + game->last_element_buffer = gd_cave_map_new(game->cave, int); + + for (y = 0; y < game->cave->h; y++) + for (x = 0; x < game->cave->w; x++) + game->last_element_buffer[y][x] = O_NONE; + + /* create new direction buffer */ + game->dir_buffer = gd_cave_map_new(game->cave, int); + + for (y = 0; y < game->cave->h; y++) + for (x = 0; x < game->cave->w; x++) + game->dir_buffer[y][x] = GD_MV_STILL; + /* create new gfx buffer */ game->gfx_buffer = gd_cave_map_new(game->cave, int); @@ -455,6 +483,9 @@ static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean if (allow_iterate) game->milliseconds_game += millisecs_elapsed; + /* increment cycle (frame) counter for the current cave iteration */ + game->itercycle++; + if (game->milliseconds_game >= cavespeed) { GdPlayerState pl; @@ -462,6 +493,25 @@ static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean game->milliseconds_game -= cavespeed; pl = game->cave->player_state; + /* initialize buffers for last cave element and direction for next iteration */ + for (y = 0; y < game->cave->h; y++) + { + for (x = 0; x < game->cave->w; x++) + { + game->last_element_buffer[y][x] = game->element_buffer[y][x]; + game->dir_buffer[y][x] = GD_MV_STILL; + } + } + + /* store last maximum number of cycles (to force redraw if changed) */ + game->itermax_last = game->itermax; + + /* update maximum number of cycles (frame) per cave iteration */ + game->itermax = game->itercycle; + + /* reset cycle (frame) counter for the next cave iteration */ + game->itercycle = 0; + iterate_cave(game, game->player_move, game->player_fire); if (game->player_move == GD_MV_STILL) diff --git a/src/game_bd/bd_gameplay.h b/src/game_bd/bd_gameplay.h index 4fb6ece4..6cd016ec 100644 --- a/src/game_bd/bd_gameplay.h +++ b/src/game_bd/bd_gameplay.h @@ -92,7 +92,12 @@ typedef struct _gd_game int state_counter; /* counter used to control the game flow, rendering of caves */ int **element_buffer; + int **last_element_buffer; + int **dir_buffer; int **gfx_buffer; /* contains the indexes to the cells; created by *start_level, deleted by *stop_game */ + int itercycle; + int itermax; + int itermax_last; int animcycle; int milliseconds_game; int milliseconds_anim; diff --git a/src/game_bd/bd_graphics.c b/src/game_bd/bd_graphics.c index 2c0e1335..34b34b39 100644 --- a/src/game_bd/bd_graphics.c +++ b/src/game_bd/bd_graphics.c @@ -301,6 +301,10 @@ int gd_drawcave(Bitmap *dest, GdGame *game, boolean force_redraw) int scroll_y_aligned = scroll_y; int x, y, xd, yd; + /* force redraw if maximum number of cycles has changed (to redraw moving elements) */ + if (game->itermax != game->itermax_last) + redraw_all = TRUE; + if (!game->cave->gate_open_flash) { show_flash_count = 0; @@ -327,8 +331,15 @@ int gd_drawcave(Bitmap *dest, GdGame *game, boolean force_redraw) { for (x = game->cave->x1, xd = 0; x <= game->cave->x2; x++, xd++) { - if (redraw_all || game->gfx_buffer[y][x] & GD_REDRAW) + /* potential movement direction of game element */ + int dir = game->dir_buffer[y][x]; + + if (redraw_all || game->gfx_buffer[y][x] & GD_REDRAW || dir != GD_MV_STILL) { + /* skip redrawing already drawn element with movement */ + if (game->element_buffer[y][x] & SKIPPED) + continue; + /* if it needs to be redrawn */ SDL_Rect offset; @@ -345,6 +356,57 @@ int gd_drawcave(Bitmap *dest, GdGame *game, boolean force_redraw) struct GraphicInfo_BD *g = &graphic_info_bd_object[tile][frame]; int width = g->width * TILESIZE_VAR / TILESIZE; int height = g->height * TILESIZE_VAR / TILESIZE; + boolean use_smooth_movements = TRUE; + + /* if game element is just moving, draw movement animation between two tiles */ + if (use_smooth_movements && dir != GD_MV_STILL) + { + if (!(game->last_element_buffer[y][x] & SKIPPED)) + { + /* redraw previous game element on the cave field the new element is moving to */ + int tile_old = game->last_element_buffer[y][x] & ~SKIPPED; + struct GraphicInfo_BD *g_old = &graphic_info_bd_object[tile_old][frame]; + + blit_bitmap(g_old->bitmap, dest, g_old->src_x, g_old->src_y, width, height, + offset.x, offset.y); + } + + /* get cave field position the game element is moving from */ + int dx = (dir == GD_MV_LEFT ? +1 : dir == GD_MV_RIGHT ? -1 : 0); + int dy = (dir == GD_MV_UP ? +1 : dir == GD_MV_DOWN ? -1 : 0); + int old_x = game->cave->getx(game->cave, x + dx, y + dy); + int old_y = game->cave->gety(game->cave, x + dx, y + dy); + + if (old_x >= game->cave->x1 && + old_x <= game->cave->x2 && + old_y >= game->cave->y1 && + old_y <= game->cave->y2) + { + if (game->dir_buffer[old_y][old_x] == GD_MV_STILL) + { + /* redraw game element on the cave field the element is moving from */ + int tile_from = game->element_buffer[old_y][old_x]; + struct GraphicInfo_BD *g_from = &graphic_info_bd_object[tile_from][0]; + + blit_bitmap(g_from->bitmap, dest, g_from->src_x, g_from->src_y, width, height, + offset.x + dx * cell_size, offset.y + dy * cell_size); + + game->element_buffer[old_y][old_x] |= SKIPPED; + } + else + { + /* if old tile also moving (like pushing player), do not redraw it again */ + game->last_element_buffer[old_y][old_x] |= SKIPPED; + } + } + + /* get shifted position between cave fields the game element is moving from/to */ + int itercycle = MIN(MAX(0, game->itermax - game->itercycle - 1), game->itermax); + int shift = cell_size * itercycle / game->itermax; + + offset.x += dx * shift; + offset.y += dy * shift; + } blit_bitmap(g->bitmap, dest, g->src_x, g->src_y, width, height, offset.x, offset.y); diff --git a/src/game_bd/main_bd.c b/src/game_bd/main_bd.c index 93350523..d637acc8 100644 --- a/src/game_bd/main_bd.c +++ b/src/game_bd/main_bd.c @@ -269,6 +269,10 @@ void InitGameEngine_BD(void) game_bd.game = gd_game_new(native_bd_level.cave_nr, native_bd_level.level_nr); + game_bd.game->itercycle = 0; + game_bd.game->itermax = 8; // default; dynamically changed at runtime + game_bd.game->itermax_last = game_bd.game->itermax; + // default: start with completely covered playfield int next_state = GAME_INT_START_UNCOVER + 1; -- 2.34.1