X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgame.c;h=6f989a1057109e8b4d96a0af5a7470ce2484d9ce;hb=49fc76165d4471c3e9db737bbb86f7448d27f625;hp=fa65ccb1f3fad76cdfa8655ac4a9b15dff39c50d;hpb=1b0670d5957e84c1ef0c8c7cd35217c471d4d62a;p=nothing.git diff --git a/src/game.c b/src/game.c index fa65ccb1..6f989a10 100644 --- a/src/game.c +++ b/src/game.c @@ -1,83 +1,93 @@ -#include -#include +#include #include "system/stacktrace.h" #include #include "game.h" #include "game/level.h" #include "game/sound_samples.h" +#include "game/level_picker.h" #include "system/log.h" #include "system/lt.h" #include "system/nth_alloc.h" #include "ui/console.h" #include "ui/edit_field.h" +#include "system/str.h" +#include "ebisp/builtins.h" +#include "broadcast.h" +#include "sdl/texture.h" +#include "game/level/level_editor.h" + +static int game_render_cursor(const Game *game); typedef enum Game_state { GAME_STATE_RUNNING = 0, GAME_STATE_PAUSE, GAME_STATE_CONSOLE, - GAME_STATE_QUIT, - - GAME_STATE_N + GAME_STATE_LEVEL_PICKER, + GAME_STATE_LEVEL_EDITOR, + GAME_STATE_QUIT } Game_state; typedef struct Game { Lt *lt; Game_state state; + Broadcast *broadcast; + Sprite_font *font; + LevelPicker *level_picker; + LevelEditor *level_editor; Level *level; - char *level_file_path; Sound_samples *sound_samples; Camera *camera; - Sprite_font *font; Console *console; SDL_Renderer *renderer; + SDL_Texture *texture_cursor; + int cursor_x; + int cursor_y; } Game; -Game *create_game(const char *level_file_path, - const char *sound_sample_files[], - size_t sound_sample_files_count, - SDL_Renderer *renderer) +Game *create_game(const char *level_folder, + const char *sound_sample_files[], + size_t sound_sample_files_count, + SDL_Renderer *renderer) { - trace_assert(level_file_path); + trace_assert(level_folder); - Lt *const lt = create_lt(); - if (lt == NULL) { - return NULL; - } + Lt *lt = create_lt(); - Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free); + Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free); if (game == NULL) { RETURN_LT(lt, NULL); } game->lt = lt; - game->renderer = renderer; + game->state = GAME_STATE_LEVEL_PICKER; - game->level = PUSH_LT( + game->broadcast = PUSH_LT( lt, - create_level_from_file(level_file_path), - destroy_level); - if (game->level == NULL) { + create_broadcast(game), + destroy_broadcast); + if (game->broadcast == NULL) { RETURN_LT(lt, NULL); } - game->level_file_path = PUSH_LT(lt, nth_alloc(sizeof(char) * (strlen(level_file_path) + 1)), free); - if (game->level_file_path == NULL) { - RETURN_LT(lt, NULL); - } - strcpy(game->level_file_path, level_file_path); - game->font = PUSH_LT( lt, - create_sprite_font_from_file("fonts/charmap-oldschool.bmp", renderer), + create_sprite_font_from_file( + "images/charmap-oldschool.bmp", + renderer), destroy_sprite_font); if (game->font == NULL) { RETURN_LT(lt, NULL); } - game->camera = PUSH_LT(lt, create_camera(renderer, game->font), destroy_camera); - if (game->camera == NULL) { + game->level_picker = PUSH_LT( + lt, + create_level_picker( + game->font, + level_folder), + destroy_level_picker); + if (game->level_picker == NULL) { RETURN_LT(lt, NULL); } @@ -91,15 +101,40 @@ Game *create_game(const char *level_file_path, RETURN_LT(lt, NULL); } + game->camera = PUSH_LT( + lt, + create_camera(renderer, game->font), + destroy_camera); + if (game->camera == NULL) { + RETURN_LT(lt, NULL); + } + game->console = PUSH_LT( lt, - create_console(game->level, game->font), + create_console(game->broadcast, game->font), destroy_console); if (game->console == NULL) { RETURN_LT(lt, NULL); } - game->state = GAME_STATE_RUNNING; + game->renderer = renderer; + game->texture_cursor = PUSH_LT( + lt, + texture_from_bmp("images/cursor.bmp", renderer), + SDL_DestroyTexture); + if (SDL_SetTextureBlendMode( + game->texture_cursor, + SDL_ComposeCustomBlendMode( + SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SDL_BLENDOPERATION_ADD, + SDL_BLENDFACTOR_ONE, + SDL_BLENDFACTOR_ZERO, + SDL_BLENDOPERATION_ADD)) < 0) { + log_warn("SDL error: %s\n", SDL_GetError()); + } + game->cursor_x = 0; + game->cursor_y = 0; return game; } @@ -114,18 +149,45 @@ int game_render(const Game *game) { trace_assert(game); - if (game->state == GAME_STATE_QUIT) { - return 0; - } + switch(game->state) { + case GAME_STATE_RUNNING: + case GAME_STATE_PAUSE: { + if (level_render(game->level, game->camera) < 0) { + return -1; + } + } break; - if (level_render(game->level, game->camera) < 0) { - return -1; - } + case GAME_STATE_CONSOLE: { + if (level_render(game->level, game->camera) < 0) { + return -1; + } - if (game->state == GAME_STATE_CONSOLE) { - if (console_render(game->console, game->renderer) < 0) { + if (console_render(game->console, game->camera, game->renderer) < 0) { return -1; } + } break; + + case GAME_STATE_LEVEL_PICKER: { + if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) { + return -1; + } + + if (game_render_cursor(game) < 0) { + return -1; + } + } break; + + case GAME_STATE_LEVEL_EDITOR: { + if (level_editor_render(game->level_editor, game->camera) < 0) { + return -1; + } + + if (game_render_cursor(game) < 0) { + return -1; + } + } break; + + case GAME_STATE_QUIT: break; } return 0; @@ -133,7 +195,18 @@ int game_render(const Game *game) int game_sound(Game *game) { - return level_sound(game->level, game->sound_samples); + switch (game->state) { + case GAME_STATE_RUNNING: + case GAME_STATE_PAUSE: + case GAME_STATE_CONSOLE: + return level_sound(game->level, game->sound_samples); + case GAME_STATE_LEVEL_PICKER: + case GAME_STATE_LEVEL_EDITOR: + case GAME_STATE_QUIT: + return 0; + } + + return 0; } int game_update(Game *game, float delta_time) @@ -141,11 +214,19 @@ int game_update(Game *game, float delta_time) trace_assert(game); trace_assert(delta_time > 0.0f); - if (game->state == GAME_STATE_QUIT) { - return 0; - } + switch (game->state) { + case GAME_STATE_RUNNING: { + if (level_update(game->level, delta_time) < 0) { + return -1; + } - if (game->state == GAME_STATE_RUNNING || game->state == GAME_STATE_CONSOLE) { + if (level_enter_camera_event(game->level, game->camera) < 0) { + return -1; + } + + } break; + + case GAME_STATE_CONSOLE: { if (level_update(game->level, delta_time) < 0) { return -1; } @@ -157,6 +238,74 @@ int game_update(Game *game, float delta_time) if (console_update(game->console, delta_time) < 0) { return -1; } + } break; + + case GAME_STATE_LEVEL_PICKER: { + if (level_picker_update(game->level_picker, delta_time) < 0) { + return -1; + } + + if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) { + return -1; + } + + const char *level_filename = level_picker_selected_level(game->level_picker); + + if (level_filename != NULL) { + if (game->level_editor == NULL) { + game->level_editor = PUSH_LT( + game->lt, + create_level_editor_from_file(level_filename), + destroy_level_editor); + } else { + game->level_editor = RESET_LT( + game->lt, + game->level_editor, + create_level_editor_from_file(level_filename)); + } + + if (game->level_editor == NULL) { + return -1; + } + + if (game->level == NULL) { + game->level = PUSH_LT( + game->lt, + create_level_from_level_editor( + game->level_editor, + game->broadcast), + destroy_level); + } else { + game->level = RESET_LT( + game->lt, + game->level, + create_level_from_level_editor( + game->level_editor, + game->broadcast)); + } + + if (game->level == NULL) { + return -1; + } + + game->state = GAME_STATE_RUNNING; + } + + } break; + + case GAME_STATE_LEVEL_EDITOR: { + if (level_editor_focus_camera( + game->level_editor, + game->camera) < 0) { + return -1; + } + + level_editor_update(game->level_editor, delta_time); + } break; + + case GAME_STATE_PAUSE: + case GAME_STATE_QUIT: + break; } return 0; @@ -169,10 +318,6 @@ static int game_event_pause(Game *game, const SDL_Event *event) trace_assert(event); switch (event->type) { - case SDL_QUIT: - game->state = GAME_STATE_QUIT; - break; - case SDL_KEYDOWN: switch (event->key.keysym.sym) { case SDLK_p: @@ -197,63 +342,70 @@ static int game_event_running(Game *game, const SDL_Event *event) trace_assert(event); switch (event->type) { - case SDL_QUIT: - game->state = GAME_STATE_QUIT; - break; - - case SDL_KEYDOWN: + case SDL_KEYDOWN: { switch (event->key.keysym.sym) { - case SDLK_r: - log_info("Reloading the level from '%s'...\n", game->level_file_path); + case SDLK_r: { + const char *level_filename = game->level_editor->file_name; + + if (!level_filename) { + log_warn("Could not reload the level. There is no associated file.\n"); + return 0; + } + + log_info("Reloading the level from '%s'...\n", level_filename); + + game->level_editor = RESET_LT( + game->lt, + game->level_editor, + create_level_editor_from_file(level_filename)); + if (game->level_editor == NULL) { + log_fail("Could not reload level %s\n", level_filename); + game->state = GAME_STATE_QUIT; + return -1; + } game->level = RESET_LT( game->lt, game->level, - create_level_from_file( - game->level_file_path)); - + create_level_from_level_editor( + game->level_editor, + game->broadcast)); if (game->level == NULL) { - log_fail("Could not reload level %s\n", game->level_file_path); + log_fail("Could not reload level %s\n", level_filename); game->state = GAME_STATE_QUIT; return -1; } camera_disable_debug_mode(game->camera); + } break; - break; - - case SDLK_q: - log_info("Reloading the level's platforms from '%s'...\n", game->level_file_path); - if (level_reload_preserve_player(game->level, game->level_file_path) < 0) { - log_fail("Could not reload level %s\n", game->level_file_path); - game->state = GAME_STATE_QUIT; - return -1; - } - break; - - case SDLK_p: + case SDLK_p: { game->state = GAME_STATE_PAUSE; camera_toggle_blackwhite_mode(game->camera); sound_samples_toggle_pause(game->sound_samples); - break; + } break; - case SDLK_l: + case SDLK_l: { camera_toggle_debug_mode(game->camera); level_toggle_debug_mode(game->level); - break; + } break; + + case SDLK_TAB: { + game->state = GAME_STATE_LEVEL_EDITOR; + } break; } - break; - case SDL_KEYUP: + } break; + + case SDL_KEYUP: { switch (event->key.keysym.sym) { case SDLK_BACKQUOTE: - case SDLK_c: + case SDLK_c: { SDL_StartTextInput(); game->state = GAME_STATE_CONSOLE; console_slide_down(game->console); - break; + } break; } - break; - + } break; } return level_event(game->level, event); @@ -262,10 +414,6 @@ static int game_event_running(Game *game, const SDL_Event *event) static int game_event_console(Game *game, const SDL_Event *event) { switch (event->type) { - case SDL_QUIT: - game->state = GAME_STATE_QUIT; - return 0; - case SDL_KEYDOWN: switch (event->key.keysym.sym) { case SDLK_ESCAPE: @@ -282,11 +430,104 @@ static int game_event_console(Game *game, const SDL_Event *event) return console_handle_event(game->console, event); } +static int game_event_level_picker(Game *game, const SDL_Event *event) +{ + trace_assert(game); + trace_assert(event); + + switch (event->type) { + case SDL_KEYDOWN: { + switch(event->key.keysym.sym) { + case SDLK_n: { + if (game->level_editor == NULL) { + game->level_editor = PUSH_LT( + game->lt, + create_level_editor(), + destroy_level_editor); + } else { + game->level_editor = RESET_LT( + game->lt, + game->level_editor, + create_level_editor()); + } + + if (game->level_editor == NULL) { + return -1; + } + + if (game->level == NULL) { + game->level = PUSH_LT( + game->lt, + create_level_from_level_editor( + game->level_editor, + game->broadcast), + destroy_level); + } else { + game->level = RESET_LT( + game->lt, + game->level, + create_level_from_level_editor( + game->level_editor, + game->broadcast)); + } + + if (game->level == NULL) { + return -1; + } + + game->state = GAME_STATE_RUNNING; + } break; + } + } break; + } + + return level_picker_event(game->level_picker, event, game->camera); +} + +static int game_event_level_editor(Game *game, const SDL_Event *event) +{ + trace_assert(game); + trace_assert(event); + + switch (event->type) { + case SDL_KEYDOWN: { + switch (event->key.keysym.sym) { + case SDLK_TAB: { + game->level = RESET_LT( + game->lt, + game->level, + create_level_from_level_editor( + game->level_editor, + game->broadcast)); + if (game->level == NULL) { + return -1; + } + game->state = GAME_STATE_RUNNING; + } break; + } + } break; + } + + return level_editor_event(game->level_editor, event, game->camera); +} + int game_event(Game *game, const SDL_Event *event) { trace_assert(game); trace_assert(event); + switch (event->type) { + case SDL_QUIT: { + game->state = GAME_STATE_QUIT; + return 0; + } break; + + case SDL_MOUSEMOTION: { + game->cursor_x = event->motion.x; + game->cursor_y = event->motion.y; + } break; + } + switch (game->state) { case GAME_STATE_RUNNING: return game_event_running(game, event); @@ -297,10 +538,17 @@ int game_event(Game *game, const SDL_Event *event) case GAME_STATE_CONSOLE: return game_event_console(game, event); - default: {} + case GAME_STATE_LEVEL_PICKER: + return game_event_level_picker(game, event); + + case GAME_STATE_LEVEL_EDITOR: + return game_event_level_editor(game, event); + + case GAME_STATE_QUIT: + return 0; } - return 0; + return -1; } @@ -311,16 +559,65 @@ int game_input(Game *game, trace_assert(game); trace_assert(keyboard_state); - if (game->state == GAME_STATE_QUIT || - game->state == GAME_STATE_PAUSE || - game->state == GAME_STATE_CONSOLE) { + switch (game->state) { + case GAME_STATE_QUIT: + case GAME_STATE_PAUSE: + case GAME_STATE_CONSOLE: + case GAME_STATE_LEVEL_EDITOR: return 0; + + case GAME_STATE_RUNNING: + return level_input(game->level, keyboard_state, the_stick_of_joy); + + case GAME_STATE_LEVEL_PICKER: + return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy); } - return level_input(game->level, keyboard_state, the_stick_of_joy); + return 0; } int game_over_check(const Game *game) { return game->state == GAME_STATE_QUIT; } + +struct EvalResult +game_send(Game *game, Gc *gc, struct Scope *scope, + struct Expr path) +{ + trace_assert(game); + trace_assert(gc); + trace_assert(scope); + + const char *target = NULL; + struct Expr rest = void_expr(); + struct EvalResult res = match_list(gc, "q*", path, &target, &rest); + if (res.is_error) { + return res; + } + + if (strcmp(target, "level") == 0) { + return level_send(game->level, gc, scope, rest); + } else if (strcmp(target, "menu") == 0) { + level_picker_clean_selection(game->level_picker); + game->state = GAME_STATE_LEVEL_PICKER; + return eval_success(NIL(gc)); + } + + return unknown_target(gc, "game", target); +} + +// Private Functions + +static int game_render_cursor(const Game *game) +{ + trace_assert(game); + + SDL_Rect src = {0, 0, 32, 32}; + SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32}; + if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) { + return -1; + } + + return 0; +}