From 019f39f84c7ce3a0ff6d7c2945e3d66024ce0e61 Mon Sep 17 00:00:00 2001 From: En Yi Date: Mon, 19 Aug 2024 11:55:48 +0800 Subject: [PATCH] Implement basic level state management Internal Changelog: - Remove spawn position for entity - Not used. If spawning is needed, use a spawning entity - Put in state machine callback function - Player spawning system is part of callback function - Remove player spawning system - Incorporate state transition in systems --- engine/EC.h | 1 - engine/entManager.c | 1 - scenes/editor_scene.c | 47 ++++++++++++++++++++++++++++++++---------- scenes/game_scene.c | 22 ++++++++++++++++++-- scenes/game_systems.c | 34 ++++++++++++------------------ scenes/game_systems.h | 2 +- scenes/scene_impl.h | 18 ++++++++++++++++ scenes/scene_systems.c | 3 ++- 8 files changed, 90 insertions(+), 38 deletions(-) diff --git a/engine/EC.h b/engine/EC.h index 6ea2ac0..60b28ea 100644 --- a/engine/EC.h +++ b/engine/EC.h @@ -253,7 +253,6 @@ static inline void set_bbox(CBBox_t* p_bbox, unsigned int x, unsigned int y) } struct Entity { - Vector2 spawn_pos; Vector2 position; unsigned long m_id; unsigned int m_tag; diff --git a/engine/entManager.c b/engine/entManager.c index 31a25e1..89a6d61 100644 --- a/engine/entManager.c +++ b/engine/entManager.c @@ -115,7 +115,6 @@ Entity_t *add_entity(EntityManager_t* p_manager, unsigned int tag) Entity_t* p_ent = new_entity_from_mempool(&e_idx); if (p_ent == NULL) return NULL; - p_ent->spawn_pos = (Vector2){0, 0}; p_ent->m_tag = tag; sc_queue_add_last(&p_manager->to_add, e_idx); p_ent->manager = p_manager; diff --git a/scenes/editor_scene.c b/scenes/editor_scene.c index 90c2657..3132207 100644 --- a/scenes/editor_scene.c +++ b/scenes/editor_scene.c @@ -220,6 +220,12 @@ static void render_editor_game_scene(Scene_t* scene) ); BeginMode2D(data->camera.cam); + + if (point_in_AABB(data->player_spawn, (Rectangle){min.x * tilemap.tile_size, min.y * tilemap.tile_size, data->game_rec.width, data->game_rec.height})) + { + DrawCircleV(data->player_spawn, 6, PURPLE); + } + for (int tile_y = min.y; tile_y < max.y; tile_y++) { for (int tile_x = min.x; tile_x < max.x; tile_x++) @@ -248,12 +254,6 @@ static void render_editor_game_scene(Scene_t* scene) { CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T); - // Draw the spawn point - if (p_ent->m_tag == PLAYER_ENT_TAG) - { - DrawCircleV(p_ent->spawn_pos, 6, PURPLE); - } - // Entity culling Vector2 box_size = {0}; if (p_bbox != NULL) box_size = p_bbox->size; @@ -887,6 +887,9 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) p_playerstate->locked = !p_playerstate->locked; } break; + case ACTION_SET_SPAWNPOINT: + data->player_spawn = p_player->position; + break; default: break; } @@ -1062,9 +1065,6 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) data->show_grid = !data->show_grid; } break; - case ACTION_SET_SPAWNPOINT: - p_player->spawn_pos = p_player->position; - break; case ACTION_TOGGLE_TIMESLOW: if (!pressed) { @@ -1103,6 +1103,27 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed) } } +static void at_level_start(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + data->sm.state = LEVEL_STATE_RUNNING; +} + +static void at_level_dead(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + Entity_t* p_player = create_player(&scene->ent_manager); + p_player->position = data->player_spawn; + data->sm.state = LEVEL_STATE_STARTING; +} + +static void at_level_complete(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + do_action(scene, ACTION_NEXTLEVEL, true); + data->sm.state = LEVEL_STATE_STARTING; +} + void init_sandbox_scene(LevelScene_t* scene) { init_scene(&scene->scene, &level_do_action); @@ -1116,7 +1137,11 @@ void init_sandbox_scene(LevelScene_t* scene) &scene->data, MAX_N_TILES, all_tiles, (Rectangle){25, 25, VIEWABLE_EDITOR_MAP_WIDTH*TILE_SIZE, VIEWABLE_EDITOR_MAP_HEIGHT*TILE_SIZE} ); - scene->data.show_grid = true; + scene->data.sm.state_functions[LEVEL_STATE_STARTING] = at_level_start; + scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL; + scene->data.sm.state_functions[LEVEL_STATE_DEAD] = at_level_dead; + scene->data.sm.state_functions[LEVEL_STATE_COMPLETE] = at_level_complete; + scene->data.tile_sprites[ONEWAY_TILE] = get_sprite(&scene->scene.engine->assets, "tl_owp"); scene->data.tile_sprites[LADDER] = get_sprite(&scene->scene.engine->assets, "tl_ldr"); scene->data.tile_sprites[SPIKES] = get_sprite(&scene->scene.engine->assets, "d_spikes"); @@ -1363,8 +1388,8 @@ void init_sandbox_scene(LevelScene_t* scene) sc_array_add(&scene->scene.systems, &player_dir_reset_system); sc_array_add(&scene->scene.systems, &update_water_runner_system); sc_array_add(&scene->scene.systems, &check_player_dead_system); - sc_array_add(&scene->scene.systems, &player_respawn_system); sc_array_add(&scene->scene.systems, &level_end_detection_system); + sc_array_add(&scene->scene.systems, &level_state_management_system); sc_array_add(&scene->scene.systems, &render_editor_game_scene); sc_array_add(&scene->scene.systems, &level_scene_render_func); diff --git a/scenes/game_scene.c b/scenes/game_scene.c index c1f2fa1..8ae65e6 100644 --- a/scenes/game_scene.c +++ b/scenes/game_scene.c @@ -416,6 +416,19 @@ static void render_regular_game_scene(Scene_t* scene) EndTextureMode(); } +static void at_level_start(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + data->sm.state = LEVEL_STATE_RUNNING; +} + +static void at_level_complete(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + do_action(scene, ACTION_NEXTLEVEL, true); + data->sm.state = LEVEL_STATE_STARTING; +} + void init_game_scene(LevelScene_t* scene) { init_scene(&scene->scene, &level_do_action); @@ -433,6 +446,10 @@ void init_game_scene(LevelScene_t* scene) VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE } ); + scene->data.sm.state_functions[LEVEL_STATE_STARTING] = at_level_start; + scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL; + scene->data.sm.state_functions[LEVEL_STATE_DEAD] = NULL; + scene->data.sm.state_functions[LEVEL_STATE_COMPLETE] = at_level_complete; scene->scene.bg_colour = LIGHTGRAY; add_scene_layer( @@ -480,9 +497,10 @@ void init_game_scene(LevelScene_t* scene) sc_array_add(&scene->scene.systems, &sprite_animation_system); sc_array_add(&scene->scene.systems, &camera_update_system); sc_array_add(&scene->scene.systems, &player_dir_reset_system); - sc_array_add(&scene->scene.systems, &check_player_dead_system); - //sc_array_add(&scene->scene.systems, &player_respawn_system); sc_array_add(&scene->scene.systems, &update_water_runner_system); + sc_array_add(&scene->scene.systems, &check_player_dead_system); + sc_array_add(&scene->scene.systems, &level_end_detection_system); + sc_array_add(&scene->scene.systems, &level_state_management_system); sc_array_add(&scene->scene.systems, &render_regular_game_scene); sc_array_add(&scene->scene.systems, &level_scene_render_func); // This avoid graphical glitch, not essential diff --git a/scenes/game_systems.c b/scenes/game_systems.c index 07125d8..c19553c 100644 --- a/scenes/game_systems.c +++ b/scenes/game_systems.c @@ -231,7 +231,6 @@ void destroy_entity(Scene_t* scene, TileGrid_t* tilemap, Entity_t* p_ent) void check_player_dead_system(Scene_t* scene) { LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); - TileGrid_t tilemap = data->tilemap; Entity_t* p_player; // Cannot create player while looping though the players // So have to create outside of the loop @@ -245,25 +244,7 @@ void check_player_dead_system(Scene_t* scene) ent->position = p_player->position; } destroy_entity(scene, &data->tilemap, p_player); - } - } -} - -void player_respawn_system(Scene_t* scene) -{ - Entity_t* p_player; - // Cannot create player while looping though the players - // So have to create outside of the loop - sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_player) - { - if (!p_player->m_alive) - { - - Entity_t* new_player = create_player(&scene->ent_manager); - CTransform_t* p_ct = get_component(new_player, CTRANSFORM_COMP_T); - new_player->position = p_player->spawn_pos; - memset(&p_ct->velocity, 0, sizeof(p_ct->velocity)); - memset(&p_ct->accel, 0, sizeof(p_ct->accel)); + data->sm.state = LEVEL_STATE_DEAD; } } } @@ -2038,13 +2019,24 @@ void level_end_detection_system(Scene_t* scene) ) ) { - do_action(scene, ACTION_NEXTLEVEL, true); + lvl_scene->data.sm.state = LEVEL_STATE_COMPLETE; + //do_action(scene, ACTION_NEXTLEVEL, true); } } } } +void level_state_management_system(Scene_t* scene) +{ + LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); + + if (data->sm.state_functions[data->sm.state] != NULL) + { + data->sm.state_functions[data->sm.state](scene); + } +} + static inline bool is_point_in_water(Vector2 pos, TileGrid_t tilemap) { unsigned int tile_idx = get_tile_idx(pos.x, pos.y, tilemap); diff --git a/scenes/game_systems.h b/scenes/game_systems.h index c752fbe..f0cfd61 100644 --- a/scenes/game_systems.h +++ b/scenes/game_systems.h @@ -22,11 +22,11 @@ void boulder_destroy_wooden_tile_system(Scene_t* scene); void camera_update_system(Scene_t* scene); void container_destroy_system(Scene_t* scene); void player_dir_reset_system(Scene_t* scene); -void player_respawn_system(Scene_t* scene); void check_player_dead_system(Scene_t* scene); void lifetimer_update_system(Scene_t* scene); void airtimer_update_system(Scene_t* scene); void spike_collision_system(Scene_t* scene); void level_end_detection_system(Scene_t* scene); +void level_state_management_system(Scene_t* scene); #endif // __GAME_SYSTEMS_H diff --git a/scenes/scene_impl.h b/scenes/scene_impl.h index f6be934..b0a4fb4 100644 --- a/scenes/scene_impl.h +++ b/scenes/scene_impl.h @@ -45,6 +45,22 @@ typedef struct LevelCamera { float range_limit; }LevelCamera_t; +typedef enum LevelSceneState { + LEVEL_STATE_STARTING = 0, + LEVEL_STATE_RUNNING, + LEVEL_STATE_DEAD, + LEVEL_STATE_COMPLETE, +} LevelSceneState_t; + +typedef struct LevelSceneStateMachine { + LevelSceneState_t state; + system_func_t state_functions[4]; + + // Engine has no timeline support, so make a pseudo timeline implementation + float fractional; + uint32_t counter; +} LevelSceneStateMachine_t; + typedef struct LevelSceneData { TileGrid_t tilemap; // TODO: game_rec is actually obsolete since this is in the scene game layer @@ -57,6 +73,8 @@ typedef struct LevelSceneData { unsigned int current_level; CoinCounter_t coins; bool show_grid; + Vector2 player_spawn; + LevelSceneStateMachine_t sm; }LevelSceneData_t; typedef struct LevelScene { diff --git a/scenes/scene_systems.c b/scenes/scene_systems.c index daed2c9..59d6aad 100644 --- a/scenes/scene_systems.c +++ b/scenes/scene_systems.c @@ -41,6 +41,8 @@ void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* t } memset(&data->coins, 0, sizeof(data->coins)); data->show_grid = false; + + memset(&data->sm, 0, sizeof(data->sm)); } void term_level_scene_data(LevelSceneData_t* data) @@ -145,7 +147,6 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num) Entity_t* ent = create_player(&scene->scene.ent_manager); ent->position.x = (i % scene->data.tilemap.width) * scene->data.tilemap.tile_size; ent->position.y = (i / scene->data.tilemap.width) * scene->data.tilemap.tile_size; - ent->spawn_pos = ent->position; } break; case 23: