Compare commits

...

3 Commits

Author SHA1 Message Date
En Yi e4b5695a15 Allow level end to be deleted 2024-08-19 14:24:33 +08:00
En Yi e37e89505a Experiment with timer-based transition 2024-08-19 14:24:22 +08:00
En Yi 019f39f84c 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
2024-08-19 11:55:48 +08:00
9 changed files with 116 additions and 42 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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,41 @@ 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;
CPlayerState_t* p_pstate = get_component(p_player, CPLAYERSTATE_T);
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
p_pstate->locked = true;
}
change_level_state(data, LEVEL_STATE_STARTING);
}
static void at_level_complete(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
data->sm.fractional += scene->delta_time;
if (data->sm.fractional > 1.0f)
{
data->sm.fractional -= 1.0f;
data->sm.counter++;
}
if (data->sm.counter >= 3)
{
do_action(scene, ACTION_NEXTLEVEL, true);
at_level_dead(scene);
}
}
void init_sandbox_scene(LevelScene_t* scene)
{
init_scene(&scene->scene, &level_do_action);
@ -1116,7 +1151,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 +1402,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);

View File

@ -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

View File

@ -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));
change_level_state(data, LEVEL_STATE_DEAD);
}
}
}
@ -1486,10 +1467,8 @@ void update_tilemap_system(Scene_t* scene)
{
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
if (!p_ent->m_alive) continue;
CTransform_t* p_ctransform = get_component(p_ent, CTRANSFORM_COMP_T);
if (p_ctransform == NULL) continue;
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
// Update tilemap position
for (size_t i = 0;i < p_tilecoord->n_tiles; ++i)
{
@ -2038,13 +2017,27 @@ void level_end_detection_system(Scene_t* scene)
)
)
{
do_action(scene, ACTION_NEXTLEVEL, true);
destroy_entity(scene, &lvl_scene->data.tilemap, p_other_ent);
change_level_state(&lvl_scene->data, 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);

View File

@ -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

View File

@ -256,6 +256,6 @@ Entity_t* create_level_end(EntityManager_t* ent_manager)
Entity_t* p_flag = add_entity(ent_manager, LEVEL_END_TAG);
if (p_flag == NULL) return NULL;
add_component(p_flag, CTRANSFORM_COMP_T);
add_component(p_flag, CTILECOORD_COMP_T);
return p_flag;
}

View File

@ -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,8 +73,17 @@ typedef struct LevelSceneData {
unsigned int current_level;
CoinCounter_t coins;
bool show_grid;
Vector2 player_spawn;
LevelSceneStateMachine_t sm;
}LevelSceneData_t;
static inline void change_level_state(LevelSceneData_t* data, LevelSceneState_t state)
{
data->sm.state = state;
data->sm.counter = 0;
data->sm.fractional = 0;
}
typedef struct LevelScene {
Scene_t scene;
LevelSceneData_t data;

View File

@ -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: