From a080b5ee0a33ffd030c7636bc62792bc781be805 Mon Sep 17 00:00:00 2001 From: En Yi Date: Thu, 20 Jul 2023 21:23:43 +0800 Subject: [PATCH] Add water checks in BFS Also refactor the BFS logic to generalise the next tiles to go after the tile checks --- scenes/engine/EC/EC.h | 1 + scenes/engine/collisions.h | 2 +- scenes/water_flow.c | 70 ++++++++++++++++++++++---------------- water_test.c | 36 ++++++++++++++++---- 4 files changed, 73 insertions(+), 36 deletions(-) diff --git a/scenes/engine/EC/EC.h b/scenes/engine/EC/EC.h index 5f54755..73f3e14 100644 --- a/scenes/engine/EC/EC.h +++ b/scenes/engine/EC/EC.h @@ -150,6 +150,7 @@ typedef enum _WaterRunnerState typedef struct _CWaterRunner { BFSTileMap_t bfs_tilemap; + int32_t start_height; int32_t current_tile; int32_t target_tile; struct sc_queue_32 bfs_queue; diff --git a/scenes/engine/collisions.h b/scenes/engine/collisions.h index 52bb20c..7d03577 100644 --- a/scenes/engine/collisions.h +++ b/scenes/engine/collisions.h @@ -15,7 +15,6 @@ typedef struct Tile { SolidType_t solid; uint8_t def; unsigned int water_level; - unsigned int max_water_level; struct sc_map_64v entities_set; Vector2 offset; Vector2 size; @@ -28,6 +27,7 @@ typedef struct TileGrid unsigned int height; unsigned int n_tiles; unsigned int tile_size; + unsigned int max_water_level; Tile_t* tiles; }TileGrid_t; diff --git a/scenes/water_flow.c b/scenes/water_flow.c index 47e0aea..609924b 100644 --- a/scenes/water_flow.c +++ b/scenes/water_flow.c @@ -26,6 +26,7 @@ Entity_t* create_water_runner(EntityManager_t* ent_manager, int32_t width, int32 p_crunner->visited = calloc(total, sizeof(bool)); p_crunner->current_tile = start_tile; + p_crunner->start_height = start_tile / width; p_crunner->target_tile = total; CTransform_t* p_ct = add_component(p_filler, CTRANSFORM_COMP_T); @@ -99,6 +100,9 @@ void update_water_runner_system(Scene_t* scene) } memset(p_crunner->visited, 0, p_crunner->bfs_tilemap.len * sizeof(bool)); int32_t lowest_tile = p_crunner->current_tile; + p_crunner->visited[p_crunner->current_tile] = true; + p_crunner->bfs_tilemap.tilemap[p_crunner->current_tile].reachable = true; + sc_queue_add_last(&p_crunner->bfs_queue, p_crunner->current_tile); while (!sc_queue_empty(&p_crunner->bfs_queue)) { @@ -112,52 +116,60 @@ void update_water_runner_system(Scene_t* scene) lowest_tile = curr_idx; } - p_crunner->visited[curr_idx] = true; + //p_crunner->visited[curr_idx] = true; p_crunner->bfs_tilemap.tilemap[curr_idx].reachable = true; // Possible optimisation to avoid repeated BFS, dunno how possible - unsigned int next = curr_idx + p_crunner->bfs_tilemap.width; - if (next >= p_crunner->bfs_tilemap.len) continue; + bool to_go[4] = {false, false, false, false}; + Tile_t* curr_tile = tilemap.tiles + curr_idx; + unsigned int next = curr_idx + p_crunner->bfs_tilemap.width; Tile_t* next_tile = tilemap.tiles + next; - if ( - next_tile->solid != SOLID - && next_tile->water_level < next_tile->max_water_level - && !p_crunner->visited[next] - ) + if (next < p_crunner->bfs_tilemap.len) { - sc_queue_add_last(&p_crunner->bfs_queue, next); - p_crunner->bfs_tilemap.tilemap[next].from = curr_idx; + to_go[0] = next_tile->solid != SOLID; } - else + + if ( + next_tile->solid == SOLID + || next_tile->water_level == tilemap.max_water_level + || curr_tile->water_level == tilemap.max_water_level + ) { next = curr_idx - 1; if (next % p_crunner->bfs_tilemap.width != 0) { next_tile = tilemap.tiles + next; - if ( - next_tile->solid != SOLID - && next_tile->water_level < next_tile->max_water_level - && !p_crunner->visited[next] - ) - { - sc_queue_add_last(&p_crunner->bfs_queue, next); - p_crunner->bfs_tilemap.tilemap[next].from = curr_idx; - } + to_go[1] = next_tile->solid != SOLID; } next = curr_idx + 1; if (next % p_crunner->bfs_tilemap.width != 0) { next_tile = tilemap.tiles + next; - if ( - next_tile->solid != SOLID - && next_tile->water_level < next_tile->max_water_level - && !p_crunner->visited[next] - ) - { - sc_queue_add_last(&p_crunner->bfs_queue, next); - p_crunner->bfs_tilemap.tilemap[next].from = curr_idx; - } + to_go[2] = next_tile->solid != SOLID; + } + } + + if (curr_tile->water_level == tilemap.max_water_level) + { + next = curr_idx - p_crunner->bfs_tilemap.width; + if (next >= 0 && next / p_crunner->bfs_tilemap.width >= p_crunner->start_height) + { + next_tile = tilemap.tiles + next; + to_go[3] = next_tile->solid != SOLID; + } + } + + const int8_t offsets[4] = {p_crunner->bfs_tilemap.width, -1, 1, -p_crunner->bfs_tilemap.width}; + for (uint8_t i = 0; i < 4; ++i) + { + next = curr_idx + offsets[i]; + if (to_go[i] && !p_crunner->visited[next]) + { + sc_queue_add_last(&p_crunner->bfs_queue, next); + p_crunner->bfs_tilemap.tilemap[next].from = curr_idx; + p_crunner->visited[next] = true; + p_crunner->bfs_tilemap.tilemap[next].reachable = true; } } } diff --git a/water_test.c b/water_test.c index 48e6fff..f22ee64 100644 --- a/water_test.c +++ b/water_test.c @@ -24,6 +24,8 @@ static GameEngine_t engine = .assets = {0} }; +static bool water_toggle = false; + static void level_scene_render_func(Scene_t* scene) { LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); @@ -146,7 +148,7 @@ static void level_scene_render_func(Scene_t* scene) draw_rec.y = 0; draw_rec.height *= -1; BeginDrawing(); - ClearBackground(LIGHTGRAY); + ClearBackground( water_toggle? ColorAlpha(BLUE, 0.2) : LIGHTGRAY); DrawTextureRec( data->game_viewport.texture, draw_rec, @@ -210,10 +212,28 @@ static void toggle_block_system(Scene_t* scene) if (tile_idx == last_tile_idx) return; if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - TileType_t new_type = EMPTY_TILE; - new_type = (tilemap.tiles[tile_idx].tile_type == SOLID_TILE)? EMPTY_TILE : SOLID_TILE; - if (new_type == SOLID_TILE) tilemap.tiles[tile_idx].water_level = 0; - change_a_tile(&tilemap, tile_idx, new_type); + if (!water_toggle) + { + TileType_t new_type = EMPTY_TILE; + new_type = (tilemap.tiles[tile_idx].tile_type == SOLID_TILE)? EMPTY_TILE : SOLID_TILE; + if (new_type == SOLID_TILE) tilemap.tiles[tile_idx].water_level = 0; + change_a_tile(&tilemap, tile_idx, new_type); + } + else + { + if (tilemap.tiles[tile_idx].tile_type != SOLID_TILE) + { + if (tilemap.tiles[tile_idx].water_level > 0) + { + tilemap.tiles[tile_idx].water_level = 0; + } + else + { + tilemap.tiles[tile_idx].water_level = tilemap.max_water_level; + } + } + + } last_tile_idx = tile_idx; CWaterRunner_t* p_crunner; sc_map_foreach_value(&scene->ent_manager.component_map[CWATERRUNNER_T], p_crunner) @@ -284,6 +304,9 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) case ACTION_RIGHT: p_playerstate->player_dir.x = (pressed)? 1 : 0; break; + case ACTION_METAL_TOGGLE: + if (!pressed) water_toggle = !water_toggle; + break; default: break; } @@ -331,6 +354,7 @@ int main(void) scene.data.tilemap.width = DEFAULT_MAP_WIDTH; scene.data.tilemap.height = DEFAULT_MAP_HEIGHT; scene.data.tilemap.tile_size = TILE_SIZE; + scene.data.tilemap.max_water_level = 1; scene.data.tilemap.n_tiles = scene.data.tilemap.width * scene.data.tilemap.height; assert(scene.data.tilemap.n_tiles <= MAX_N_TILES); scene.data.tilemap.tiles = all_tiles; @@ -340,7 +364,6 @@ int main(void) all_tiles[i].solid = NOT_SOLID; all_tiles[i].tile_type = EMPTY_TILE; all_tiles[i].moveable = true; - all_tiles[i].max_water_level = 1; sc_map_init_64v(&all_tiles[i].entities_set, 16, 0); all_tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE}; } @@ -373,6 +396,7 @@ int main(void) sc_map_put_64(&scene.scene.action_map, KEY_DOWN, ACTION_DOWN); sc_map_put_64(&scene.scene.action_map, KEY_LEFT, ACTION_LEFT); sc_map_put_64(&scene.scene.action_map, KEY_RIGHT, ACTION_RIGHT); + sc_map_put_64(&scene.scene.action_map, KEY_P, ACTION_METAL_TOGGLE); while(true)