diff --git a/assets/scenes/scene_1.symtres b/assets/scenes/scene_1.symtres index f452fcb..6919499 100755 --- a/assets/scenes/scene_1.symtres +++ b/assets/scenes/scene_1.symtres @@ -12,6 +12,7 @@ Scene_Config debug_draw_enabled : false debug_draw_mode : 0 ambient_light : 0.100 0.100 0.100 + next_scene : scene_2 } Player diff --git a/src/common/version.h b/src/common/version.h index 93a6b4b..f5fe498 100755 --- a/src/common/version.h +++ b/src/common/version.h @@ -4,7 +4,7 @@ /* Auto generated version file. DO NOT MODIFY */ #define SYMMETRY_VERSION_MAJOR 0 #define SYMMETRY_VERSION_MINOR 1 -#define SYMMETRY_VERSION_REVISION 352 +#define SYMMETRY_VERSION_REVISION 353 #define SYMMETRY_VERSION_BRANCH "dev" #endif \ No newline at end of file diff --git a/src/game/enemy.c b/src/game/enemy.c index 448cc5a..b960280 100644 --- a/src/game/enemy.c +++ b/src/game/enemy.c @@ -11,10 +11,13 @@ #include "../system/platform.h" #include "debug_vars.h" #include "im_render.h" +#include "player.h" #include #include #include +#include + static void enemy_on_scene_loaded(struct Event* event, void* enemy_ptr); static void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_state, float fixed_dt); @@ -381,6 +384,12 @@ void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, { enemy->Turret.time_elapsed_since_attack = 0.f; sound_source_play(game_state->sound, enemy->weapon_sound); + int hit_chance = 4; + int roll = rand() % 10; + if(roll <= hit_chance) + { + player_apply_damage(&scene->player, enemy); + } } else { diff --git a/src/game/event.c b/src/game/event.c index 2762e41..15b006a 100644 --- a/src/game/event.c +++ b/src/game/event.c @@ -343,6 +343,8 @@ const char* event_name_get(int event_type) case EVT_TRIGGER: return "Trigger Activated"; case EVT_INPUT_MAP_PRESSED: return "Input Map Pressed"; case EVT_INPUT_MAP_RELEASED: return "Input Map Released"; + case EVT_PLAYER_DIED: return "Player Died"; + case EVT_SCENE_CLEARED: return "Scene Cleared"; case EVT_MAX: return "Max Number of Events"; default: return "Invalid event_type"; } diff --git a/src/game/event.h b/src/game/event.h index 5a5861a..b008396 100755 --- a/src/game/event.h +++ b/src/game/event.h @@ -29,6 +29,8 @@ enum Event_Types EVT_TRIGGER, EVT_INPUT_MAP_PRESSED, EVT_INPUT_MAP_RELEASED, + EVT_PLAYER_DIED, + EVT_SCENE_CLEARED, EVT_MAX }; @@ -101,6 +103,17 @@ struct Trigger_Event struct Trigger* sender; }; +struct Player_Death_Event +{ + struct Player* player; + struct Enemy* enemy; +}; + +struct Scene_Cleared_Event +{ + struct Scene* scene; +}; + struct Event { int type; @@ -116,6 +129,8 @@ struct Event struct Scene_Loaded_Event scene_load; struct Trigger_Event trigger; struct Input_Map_Event input_map; + struct Player_Death_Event player_death; + struct Scene_Cleared_Event scene_cleared; }; }; diff --git a/src/game/game.c b/src/game/game.c index e869ddf..4d47b58 100755 --- a/src/game/game.c +++ b/src/game/game.c @@ -53,6 +53,9 @@ static void game_scene_setup(void); static void game_on_log_message(const char* message, va_list args); static void game_on_log_warning(const char* warning_message, va_list args); static void game_on_log_error(const char* context, const char* error_message, va_list args); +static void game_on_player_death(struct Event* event); +static void game_on_scene_loaded(struct Event* event); +static void game_on_scene_cleared(struct Event* event); static struct Game_State* game_state = NULL; @@ -70,6 +73,7 @@ bool game_init(struct Window* window, struct Hashmap* cvars) game_state->cvars = cvars; game_state->is_initialized = false; game_state->quit = false; + game_state->update_scene = true; game_state->fixed_delta_time = 1.f / 60.f; game_state->game_mode = GAME_MODE_GAME; game_state->renderer = calloc(1, sizeof(*game_state->renderer)); @@ -101,6 +105,8 @@ bool game_init(struct Window* window, struct Hashmap* cvars) hashmap_ptr_set(game_state->scene_init_func_table, "scene_1", &scene_1_init); hashmap_ptr_set(game_state->scene_cleanup_func_table, "scene_1", &scene_1_cleanup); + srand(time(NULL)); + event_manager_init(game_state->event_manager); input_init(); shader_init(); @@ -108,6 +114,7 @@ bool game_init(struct Window* window, struct Hashmap* cvars) framebuffer_init(); gui_init(game_state->gui_editor); gui_init(game_state->gui_game); + gui_game_init(game_state->gui_game); console_init(game_state->console); geom_init(); sound_init(game_state->sound); @@ -121,6 +128,10 @@ bool game_init(struct Window* window, struct Hashmap* cvars) /* Debug scene setup */ //game_scene_setup(); + event_manager_subscribe(game_state->event_manager, EVT_SCENE_LOADED, &game_on_scene_loaded); + event_manager_subscribe(game_state->event_manager, EVT_SCENE_CLEARED, &game_on_scene_cleared); + event_manager_subscribe(game_state->event_manager, EVT_PLAYER_DIED, &game_on_player_death); + game_state->is_initialized = scene_load(game_state->scene, "scene_1", DIRT_INSTALL) ? true : false; return game_state->is_initialized; } @@ -570,49 +581,61 @@ void game_update(float dt) if(input_map_state_get("Editor_Toggle", KS_RELEASED)) { if(game_state->game_mode == GAME_MODE_EDITOR) - { - game_state->game_mode = GAME_MODE_GAME; - game_state->scene->active_camera_index = CAM_GAME; - } + game_mode_set(GAME_MODE_GAME); else if(game_state->game_mode == GAME_MODE_GAME) - { - game_state->game_mode = GAME_MODE_EDITOR; - game_state->scene->active_camera_index = CAM_EDITOR; - input_mouse_mode_set(MM_NORMAL); - int width = 0, height = 0; - window_get_drawable_size(game_state_get()->window, &width, &height); - platform_mouse_position_set(game_state_get()->window, width / 2, height / 2); - } + game_mode_set(GAME_MODE_EDITOR); } if(input_map_state_get("Pause", KS_RELEASED)) { if(game_state->game_mode == GAME_MODE_PAUSE) - { - game_state->game_mode = GAME_MODE_GAME; - sound_pause_all(game_state->sound, false); - } + game_mode_set(GAME_MODE_GAME); else if(game_state->game_mode == GAME_MODE_GAME) - { - game_state->game_mode = GAME_MODE_PAUSE; - sound_pause_all(game_state->sound, true); - input_mouse_mode_set(MM_NORMAL); - int width = 0, height = 0; - window_get_drawable_size(game_state_get()->window, &width, &height); - platform_mouse_position_set(game_state_get()->window, width / 2, height / 2); - } + game_mode_set(GAME_MODE_PAUSE); } //game_debug(dt); //game_debug_gui(dt); console_update(game_state->console, game_state->game_mode == GAME_MODE_EDITOR ? game_state->gui_editor : game_state->gui_game, dt); - scene_update(game_state->scene, dt); + if(game_state->update_scene) + scene_update(game_state->scene, dt); + if(game_state->game_mode == GAME_MODE_EDITOR) - { editor_update(game_state->editor, dt); - } else - { gui_game_update(game_state->gui_game, dt); +} + +void game_mode_set(int new_mode) +{ + if(new_mode == game_state->game_mode) + return; + + if(new_mode == GAME_MODE_EDITOR) + { + game_state->game_mode = new_mode; + game_state->scene->active_camera_index = CAM_EDITOR; + input_mouse_mode_set(MM_NORMAL); + sound_pause_all(game_state->sound, true); + int width = 0, height = 0; + window_get_drawable_size(game_state_get()->window, &width, &height); + platform_mouse_position_set(game_state_get()->window, width / 2, height / 2); + } + else if(new_mode == GAME_MODE_PAUSE) + { + game_state->game_mode = new_mode; + game_state->scene->active_camera_index = CAM_GAME; + sound_pause_all(game_state->sound, true); + input_mouse_mode_set(MM_NORMAL); + int width = 0, height = 0; + window_get_drawable_size(game_state_get()->window, &width, &height); + platform_mouse_position_set(game_state_get()->window, width / 2, height / 2); + } + else if(new_mode == GAME_MODE_GAME) + { + game_state->game_mode = new_mode; + game_state->scene->active_camera_index = CAM_GAME; + game_state->game_mode = GAME_MODE_GAME; + sound_pause_all(game_state->sound, false); } } @@ -1958,6 +1981,7 @@ void game_cleanup(void) scene_destroy(game_state->scene); input_cleanup(); renderer_cleanup(game_state->renderer); + gui_game_cleanup(game_state->gui_game); gui_cleanup(game_state->gui_editor); gui_cleanup(game_state->gui_game); console_destroy(game_state->console); @@ -2010,5 +2034,21 @@ void game_on_log_error(const char* context, const char* error_message, va_list a void game_update_physics(float fixed_dt) { struct Game_State* game_state = game_state_get(); - scene_update_physics(game_state->scene, fixed_dt); + if(game_state->update_scene) + scene_update_physics(game_state->scene, fixed_dt); +} + +void game_on_scene_loaded(struct Event* event) +{ + game_state->update_scene = true; +} + +void game_on_scene_cleared(struct Event* event) +{ + game_state->update_scene = false; +} + +void game_on_player_death(struct Event* event) +{ + game_state->update_scene = false; } diff --git a/src/game/game.h b/src/game/game.h index c02e128..bbddc0a 100755 --- a/src/game/game.h +++ b/src/game/game.h @@ -26,6 +26,7 @@ struct Game_State { bool is_initialized; bool quit; + bool update_scene; int game_mode; float fixed_delta_time; struct Window* window; @@ -48,5 +49,6 @@ struct Game_State* game_state_get(void); bool game_init(struct Window* window, struct Hashmap* cvars); bool game_run(void); void game_cleanup(void); +void game_mode_set(int new_mode); #endif diff --git a/src/game/gui_game.c b/src/game/gui_game.c index d3c0203..ee948c7 100644 --- a/src/game/gui_game.c +++ b/src/game/gui_game.c @@ -4,17 +4,31 @@ #include "scene.h" #include "../common/log.h" #include "../system/platform.h" +#include "event.h" +#include "input.h" + +static bool show_next_level_dialog = false; +static bool show_restart_level_dialog = false; static void gui_game_pause_menu(struct nk_context* context); +static void gui_game_next_level_dialog(struct nk_context* context); +static void gui_game_restart_level_dialog(struct nk_context* context); +static void gui_game_on_player_death(struct Event* event); +static void gui_game_on_scene_cleared(struct Event* event); void gui_game_init(struct Gui* game_gui) { gui_theme_set(game_state_get()->gui_game, GT_RED); + struct Event_Manager* event_manager = game_state_get()->event_manager; + event_manager_subscribe(event_manager, EVT_PLAYER_DIED, &gui_game_on_player_death); + event_manager_subscribe(event_manager, EVT_SCENE_CLEARED, &gui_game_on_scene_cleared); } void gui_game_cleanup(struct Gui* game_gui) { - + struct Event_Manager* event_manager = game_state_get()->event_manager; + event_manager_unsubscribe(event_manager, EVT_PLAYER_DIED, &gui_game_on_player_death); + event_manager_unsubscribe(event_manager, EVT_SCENE_CLEARED, &gui_game_on_scene_cleared); } void gui_game_update(struct Gui* gui_game, float dt) @@ -24,6 +38,7 @@ void gui_game_update(struct Gui* gui_game, float dt) if(game_state->game_mode == GAME_MODE_GAME) { + // HUD struct Player* player = &game_state->scene->player; if(nk_begin(context, "Game Gui", nk_rect(50, 50, 200, 100), NK_WINDOW_CLOSABLE)) { @@ -31,6 +46,12 @@ void gui_game_update(struct Gui* gui_game, float dt) nk_labelf(context, NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE, "HP: %d", player->health); nk_end(context); } + + if(show_next_level_dialog) + gui_game_next_level_dialog(context); + + if(show_restart_level_dialog) + gui_game_restart_level_dialog(context); } else if(game_state->game_mode == GAME_MODE_PAUSE) { @@ -80,11 +101,129 @@ void gui_game_pause_menu(struct nk_context* context) nk_popup_end(context); } - if(nk_button_label(context, "Button")) + + nk_end(context); + } + context->style.window.fixed_background.data.color.a = previous_opacity; +} + +static void gui_game_next_level_dialog(struct nk_context* context) +{ + struct Game_State* game_state = game_state_get(); + struct Scene* scene = game_state->scene; + int row_height = 30; + int popup_x = 0; + int popup_y = 0; + int popup_width = 300; + int popup_height = 200; + int display_width = 0; + int display_height = 0; + int popup_flags = NK_WINDOW_TITLE | NK_WINDOW_BORDER; + window_get_drawable_size(game_state_get()->window, &display_width, &display_height); + popup_x = (display_width / 2) - (popup_width / 2); + popup_y = (display_height / 2) - (popup_height / 2); + + int background_window_flags = NK_WINDOW_BACKGROUND; + int previous_opacity = context->style.window.fixed_background.data.color.a; + context->style.window.fixed_background.data.color.a = 120; + if(nk_begin(context, "Scene Cleared", nk_rect(0, 0, display_width, display_height), background_window_flags)) + { + nk_window_set_focus(context, "Scene Cleared"); + if(nk_popup_begin(context, NK_POPUP_DYNAMIC, "Scene Cleared!", popup_flags, nk_recti(popup_x, popup_y, popup_width, popup_height))) + { + nk_layout_row_dynamic(context, row_height, 1); + if(nk_button_label(context, "Restart Level")) + { + char filename[MAX_FILENAME_LEN]; + strncpy(filename, game_state->scene->filename, MAX_FILENAME_LEN); + if(!scene_load(game_state->scene, filename, DIRT_INSTALL)) + log_error("gui_game:next_level_dialog", "Failed to reload Level"); + else + show_next_level_dialog = false; + } + + if(nk_button_label(context, "Next Level")) + { + if(strncmp(scene->next_level_filename, "\0", MAX_FILENAME_LEN) != 0) + { + char filename[MAX_FILENAME_LEN]; + strncpy(filename, game_state->scene->next_level_filename, MAX_FILENAME_LEN); + if(!scene_load(game_state->scene, filename, DIRT_INSTALL)) + log_error("gui_game:next_level_dialog", "Failed to load new Level"); + else + show_next_level_dialog = false; + } + else + { + log_error("gui_game:next_level_dialog", "No name provided for next scene"); + } + } + + if(nk_button_label(context, "Quit")) + game_state->quit = true; + + nk_popup_end(context); + } + + nk_end(context); + } + context->style.window.fixed_background.data.color.a = previous_opacity; +} + +static void gui_game_restart_level_dialog(struct nk_context* context) +{ + struct Game_State* game_state = game_state_get(); + int row_height = 30; + int popup_x = 0; + int popup_y = 0; + int popup_width = 300; + int popup_height = 200; + int display_width = 0; + int display_height = 0; + int popup_flags = NK_WINDOW_TITLE | NK_WINDOW_BORDER; + window_get_drawable_size(game_state_get()->window, &display_width, &display_height); + popup_x = (display_width / 2) - (popup_width / 2); + popup_y = (display_height / 2) - (popup_height / 2); + + int background_window_flags = NK_WINDOW_BACKGROUND; + int previous_opacity = context->style.window.fixed_background.data.color.a; + context->style.window.fixed_background.data.color.a = 120; + if(nk_begin(context, "Player Died Gui", nk_rect(0, 0, display_width, display_height), background_window_flags)) + { + nk_window_set_focus(context, "Player Died Gui"); + if(nk_popup_begin(context, NK_POPUP_DYNAMIC, "You Died", popup_flags, nk_recti(popup_x, popup_y, popup_width, popup_height))) { - log_message("Pressed!"); + nk_layout_row_dynamic(context, row_height, 1); + if(nk_button_label(context, "Restart Level")) + { + char filename[MAX_FILENAME_LEN]; + strncpy(filename, game_state->scene->filename, MAX_FILENAME_LEN); + if(!scene_load(game_state->scene, filename, DIRT_INSTALL)) + log_error("gui_game:pause_menu", "Failed to reload Level"); + else + show_restart_level_dialog = false; + } + + if(nk_button_label(context, "Quit")) + game_state->quit = true; + + nk_popup_end(context); } nk_end(context); } + context->style.window.fixed_background.data.color.a = previous_opacity; +} + +void gui_game_on_player_death(struct Event* event) +{ + struct Player_Death_Event* player_death_event = (struct Player_Death_Event*)event; + show_restart_level_dialog = true; + input_mouse_mode_set(MM_NORMAL); +} + +void gui_game_on_scene_cleared(struct Event* event) +{ + show_next_level_dialog = true; + input_mouse_mode_set(MM_NORMAL); } diff --git a/src/game/player.c b/src/game/player.c index 09a17a0..e5c5e5c 100755 --- a/src/game/player.c +++ b/src/game/player.c @@ -16,6 +16,7 @@ #include "im_render.h" #include "event.h" #include "sound_source.h" +#include "entity.h" #include #include @@ -355,3 +356,20 @@ void player_on_mousebutton_released(const struct Event* event) } } + +void player_apply_damage(struct Player* player, struct Enemy* enemy) +{ + log_message("Player hit!"); + player->health -= enemy->damage; + + if(player->health <= 0) + { + log_message("Player Ded!"); + struct Event_Manager* event_manager = game_state_get()->event_manager; + struct Event* player_death_event = event_manager_create_new_event(event_manager); + player_death_event->type = EVT_PLAYER_DIED; + player_death_event->player_death.player = player; + player_death_event->player_death.enemy = enemy; + event_manager_send_event(event_manager, player_death_event); + } +} diff --git a/src/game/player.h b/src/game/player.h index 19a0b14..bd5b55a 100755 --- a/src/game/player.h +++ b/src/game/player.h @@ -6,6 +6,7 @@ struct Scene; void player_init(struct Player* player, struct Scene* scene); void player_destroy(struct Player* player); +void player_apply_damage(struct Player* player, struct Enemy* enemy); void player_update_physics(struct Player* player, struct Scene* scene, float dt); #endif diff --git a/src/game/scene.c b/src/game/scene.c index 86796cc..d5770b6 100755 --- a/src/game/scene.c +++ b/src/game/scene.c @@ -37,6 +37,7 @@ void scene_init(struct Scene* scene) struct Game_State* game_state = game_state_get(); strncpy(scene->filename, "UNNAMED_SCENE", MAX_FILENAME_LEN); + memset(scene->next_level_filename, '\0', MAX_FILENAME_LEN); //Initialize the root entity entity_init(&scene->root_entity, "ROOT_ENTITY", NULL); @@ -156,6 +157,12 @@ bool scene_load(struct Scene* scene, const char* filename, int directory_type) scene->init = hashmap_value_exists(scene_data, "init_func") ? hashmap_ptr_get(game_state->scene_init_func_table, hashmap_str_get(scene_data, "init_func")) : &scene_init_stub; scene->cleanup = hashmap_value_exists(scene_data, "cleanup_func") ? hashmap_ptr_get(game_state->scene_cleanup_func_table, hashmap_str_get(scene_data, "cleanup_func")) : &scene_init_stub; + + if(hashmap_value_exists(scene_data, "next_scene")) + strncpy(scene->next_level_filename, hashmap_str_get(scene_data, "next_scene"), MAX_FILENAME_LEN); + else + memcpy(scene->next_level_filename, '\0', MAX_FILENAME_LEN); + num_objects_loaded++; } break; diff --git a/src/game/scene.h b/src/game/scene.h index 0633c32..04a2934 100755 --- a/src/game/scene.h +++ b/src/game/scene.h @@ -14,6 +14,7 @@ typedef void (*Scene_Cleanup_Func)(struct Scene* scene); struct Scene { char filename[MAX_FILENAME_LEN]; + char next_level_filename[MAX_FILENAME_LEN]; struct Entity root_entity; struct Player player; struct Entity entities[MAX_SCENE_ENTITIES]; diff --git a/src/game/scene_funcs.c b/src/game/scene_funcs.c index 26e11e6..fd59fcd 100644 --- a/src/game/scene_funcs.c +++ b/src/game/scene_funcs.c @@ -44,4 +44,9 @@ void scene_1_cleanup(struct Scene* scene) void scene_on_end_trigger(const struct Event* event, void* sender) { log_message("Scene_End_Trigger triggered, Move to next scene now!"); + struct Event_Manager* event_manager = game_state_get()->event_manager; + struct Event* scene_cleared_event = event_manager_create_new_event(event_manager); + scene_cleared_event->type = EVT_SCENE_CLEARED; + scene_cleared_event->scene_cleared.scene = game_state_get()->scene; + event_manager_send_event(event_manager, scene_cleared_event); } diff --git a/todo.txt b/todo.txt index b87a837..15f9e2e 100644 --- a/todo.txt +++ b/todo.txt @@ -1,12 +1,12 @@ Todo: - - Win/fail States - In-Game Gui - - Player Death Screen - - Proceed to next level Screen + x Player Death Screen + x Proceed to next level Screen - Key Required Screen - HUD showing health, keys currently acquired - Save case sensitive file names when scene entity entries - - Player/enemies getting hit by bullets + - Disbale all player actions when scene cleared dialog or scene restart dialog are active + - Enemies getting hit by bullets - Memory utils that provide allocation tracking - Remove excessive repitition in scene and editor code that handles multiple entity types - Allow switching to editor mode when game is in pause mode @@ -25,9 +25,8 @@ Todo: - Gameplay level features: - Each scene should always have a directional light that serves as the main source of light when there are no other lights - Each scene should always have a texture cube that serves as the sky - - Triggers - - Basic Enemy - - Main Menu Scene + x Triggers + x Basic Enemy ? Split this todo into gameplay/engine todos ? Write entity flags to scene file or when saving entity to file? ? Add scene init/de-init function hashmap that maps a function that should be called when scene is loaded and unloaded. Save this to file for every scene or map functions based on the name of the scene? @@ -428,4 +427,5 @@ Done: * RGB keys to progress to next level * Visual indicator on doors corresponding to their key masks * Audio cues when player does not have the right key combination to open a particular door - * Added saving scene init/cleanup funcs if there are any assigned when saving scene \ No newline at end of file + * Added saving scene init/cleanup funcs if there are any assigned when saving scene + * Win/fail States \ No newline at end of file