2 #include "system/stacktrace.h"
6 #include "game/level.h"
7 #include "game/sound_samples.h"
8 #include "game/level_picker.h"
9 #include "system/log.h"
10 #include "system/lt.h"
11 #include "system/nth_alloc.h"
12 #include "ui/console.h"
13 #include "ui/edit_field.h"
14 #include "ui/cursor.h"
15 #include "system/str.h"
16 #include "sdl/texture.h"
17 #include "game/level/level_editor/background_layer.h"
18 #include "game/level/level_editor.h"
19 #include "game/settings.h"
21 typedef enum Game_state {
23 GAME_STATE_LEVEL_PICKER,
24 GAME_STATE_LEVEL_EDITOR,
34 LevelPicker *level_picker;
35 LevelEditor *level_editor;
38 Sound_samples *sound_samples;
40 SDL_Renderer *renderer;
47 void game_switch_state(Game *game, Game_state state)
49 game->camera = create_camera(game->renderer, game->font);
53 Game *create_game(const char *level_folder,
54 const char *sound_sample_files[],
55 size_t sound_sample_files_count,
56 SDL_Renderer *renderer)
58 trace_assert(level_folder);
62 Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
70 create_sprite_font_from_file(
71 "./assets/images/charmap-oldschool.bmp",
74 if (game->font == NULL) {
78 game->level_picker = PUSH_LT(
83 destroy_level_picker);
84 if (game->level_picker == NULL) {
88 game->sound_samples = PUSH_LT(
92 sound_sample_files_count),
93 destroy_sound_samples);
94 if (game->sound_samples == NULL) {
98 game->settings = create_settings();
100 game->renderer = renderer;
102 for (Cursor_Style style = 0; style < CURSOR_STYLE_N; ++style) {
103 game->cursor.texs[style] = PUSH_LT(
105 texture_from_bmp(cursor_style_tex_files[style], renderer),
107 if (SDL_SetTextureBlendMode(
108 game->cursor.texs[style],
109 SDL_ComposeCustomBlendMode(
110 SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
111 SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
112 SDL_BLENDOPERATION_ADD,
114 SDL_BLENDFACTOR_ZERO,
115 SDL_BLENDOPERATION_ADD)) < 0) {
116 log_warn("SDL error: %s\n", SDL_GetError());
121 game->console = PUSH_LT(
123 create_console(game),
125 if (game->console == NULL) {
128 game->console_enabled = 0;
130 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
135 void destroy_game(Game *game)
138 RETURN_LT0(game->lt);
141 int game_render(const Game *game)
145 switch(game->state) {
146 case GAME_STATE_LEVEL: {
147 if (level_render(game->level, &game->camera) < 0) {
152 case GAME_STATE_LEVEL_PICKER: {
153 if (level_picker_render(game->level_picker, &game->camera) < 0) {
158 case GAME_STATE_LEVEL_EDITOR: {
159 if (level_editor_render(game->level_editor, &game->camera) < 0) {
164 case GAME_STATE_SETTINGS: {
165 settings_render(&game->settings, &game->camera);
168 case GAME_STATE_QUIT: break;
171 if (game->console_enabled) {
172 if (console_render(game->console, &game->camera) < 0) {
177 if (cursor_render(&game->cursor, game->renderer) < 0) {
184 int game_sound(Game *game)
186 switch (game->state) {
187 case GAME_STATE_LEVEL:
188 return level_sound(game->level, game->sound_samples);
189 case GAME_STATE_LEVEL_EDITOR:
190 level_editor_sound(game->level_editor, game->sound_samples);
192 case GAME_STATE_LEVEL_PICKER:
193 case GAME_STATE_SETTINGS:
194 case GAME_STATE_QUIT:
201 int game_update(Game *game, float delta_time)
204 trace_assert(delta_time > 0.0f);
206 if (game->console_enabled) {
207 if (console_update(game->console, delta_time) < 0) {
212 switch (game->state) {
213 case GAME_STATE_LEVEL: {
214 if (level_update(game->level, delta_time) < 0) {
218 if (level_enter_camera_event(game->level, &game->camera) < 0) {
224 case GAME_STATE_LEVEL_PICKER: {
225 if (level_picker_update(game->level_picker, delta_time) < 0) {
229 if (level_picker_enter_camera_event(game->level_picker, &game->camera) < 0) {
233 const char *level_filename = level_picker_selected_level(game->level_picker);
235 if (level_filename != NULL) {
236 if (game_load_level(game, level_filename) < 0) {
242 case GAME_STATE_LEVEL_EDITOR: {
243 if (level_editor_focus_camera(
245 &game->camera) < 0) {
249 level_editor_update(game->level_editor, delta_time);
252 case GAME_STATE_SETTINGS: {
253 settings_update(&game->settings, &game->camera, delta_time);
254 sound_samples_update_volume(
256 game->settings.volume_slider.value);
259 case GAME_STATE_QUIT:
266 static int game_event_running(Game *game, const SDL_Event *event)
271 if (!SDL_IsTextInputActive()) {
272 switch (event->type) {
274 switch (event->key.keysym.sym) {
276 game->level = RESET_LT(
279 create_level_from_level_editor(
280 game->level_editor));
281 if (game->level == NULL) {
282 game_switch_state(game, GAME_STATE_QUIT);
286 camera_disable_debug_mode(&game->camera);
290 game_switch_state(game, GAME_STATE_LEVEL_EDITOR);
297 return level_event(game->level, event, &game->camera, game->sound_samples);
300 static int game_event_level_picker(Game *game, const SDL_Event *event)
305 switch (event->type) {
307 switch(event->key.keysym.sym) {
309 if (game->level_editor == NULL) {
310 game->level_editor = PUSH_LT(
312 create_level_editor(&game->cursor),
313 destroy_level_editor);
315 game->level_editor = RESET_LT(
318 create_level_editor(&game->cursor));
321 if (game->level_editor == NULL) {
325 if (game->level == NULL) {
326 game->level = PUSH_LT(
328 create_level_from_level_editor(
332 game->level = RESET_LT(
335 create_level_from_level_editor(
336 game->level_editor));
339 if (game->level == NULL) {
343 game_switch_state(game, GAME_STATE_LEVEL);
347 game_switch_state(game, GAME_STATE_SETTINGS);
353 return level_picker_event(game->level_picker, event, &game->camera);
356 static int game_event_level_editor(Game *game, const SDL_Event *event)
361 switch (event->type) {
363 switch (event->key.keysym.sym) {
365 game->level = RESET_LT(
368 create_level_from_level_editor(
369 game->level_editor));
370 if (game->level == NULL) {
373 game_switch_state(game, GAME_STATE_LEVEL);
379 return level_editor_event(game->level_editor, event, &game->camera);
382 int game_event(Game *game, const SDL_Event *event)
387 // Global event handling
388 switch (event->type) {
390 game_switch_state(game, GAME_STATE_QUIT);
395 if (event->key.keysym.sym == SDLK_q && event->key.keysym.mod & KMOD_CTRL) {
396 game_switch_state(game, GAME_STATE_QUIT);
402 // Console event handling
403 if (game->console_enabled) {
404 switch (event->type) {
406 switch (event->key.keysym.sym) {
409 game->console_enabled = 0;
417 return console_handle_event(game->console, event);
419 switch (event->type) {
421 switch (event->key.keysym.sym) {
424 SDL_StartTextInput();
425 game->console_enabled = 1;
426 console_slide_down(game->console);
433 // State event handling
434 switch (game->state) {
435 case GAME_STATE_LEVEL:
436 return game_event_running(game, event);
438 case GAME_STATE_LEVEL_PICKER:
439 return game_event_level_picker(game, event);
441 case GAME_STATE_LEVEL_EDITOR:
442 return game_event_level_editor(game, event);
444 case GAME_STATE_SETTINGS: {
445 switch (event->type) {
447 if (event->key.keysym.sym == SDLK_ESCAPE) {
448 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
454 settings_event(&game->settings, &game->camera, event);
458 case GAME_STATE_QUIT:
466 // TODO: get rid of keyboard_state (because it's a global var and can
467 // be check anywhere anyway). And introduce *_joystick methods.
468 int game_input(Game *game,
469 const Uint8 *const keyboard_state,
470 SDL_Joystick *the_stick_of_joy)
473 trace_assert(keyboard_state);
475 if (game->console_enabled) {
479 switch (game->state) {
480 case GAME_STATE_SETTINGS:
481 case GAME_STATE_QUIT:
482 case GAME_STATE_LEVEL_EDITOR:
485 case GAME_STATE_LEVEL:
486 return level_input(game->level, keyboard_state, the_stick_of_joy);
488 case GAME_STATE_LEVEL_PICKER:
489 return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
495 int game_over_check(const Game *game)
497 return game->state == GAME_STATE_QUIT;
500 int game_load_level(Game *game, const char *level_filename)
503 trace_assert(level_filename);
505 if (game->level_editor == NULL) {
506 game->level_editor = PUSH_LT(
508 create_level_editor_from_file(level_filename, &game->cursor),
509 destroy_level_editor);
511 game->level_editor = RESET_LT(
514 create_level_editor_from_file(level_filename, &game->cursor));
517 if (game->level_editor == NULL) {
521 if (game->level == NULL) {
522 game->level = PUSH_LT(
524 create_level_from_level_editor(
528 game->level = RESET_LT(
531 create_level_from_level_editor(
532 game->level_editor));
535 if (game->level == NULL) {
539 game_switch_state(game, GAME_STATE_LEVEL);