]> git.lizzy.rs Git - nothing.git/blob - src/game.c
818af559dff78f58acb40cdbef7059ee8588373b
[nothing.git] / src / game.c
1 #include <SDL.h>
2 #include "system/stacktrace.h"
3 #include <stdio.h>
4
5 #include "game.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"
20 #include "game/credits.h"
21
22 typedef enum Game_state {
23     GAME_STATE_LEVEL = 0,
24     GAME_STATE_LEVEL_PICKER,
25     GAME_STATE_LEVEL_EDITOR,
26     GAME_STATE_CREDITS,
27     GAME_STATE_SETTINGS,
28     GAME_STATE_QUIT
29 } Game_state;
30
31 typedef struct Game {
32     Lt *lt;
33
34     Game_state state;
35     Sprite_font *font;
36     LevelPicker *level_picker;
37     LevelEditor *level_editor;
38     Credits *credits;
39     Level *level;
40     Settings settings;
41     Sound_samples *sound_samples;
42     Camera camera;
43     SDL_Renderer *renderer;
44     Console *console;
45     Cursor cursor;
46     int console_enabled;
47 } Game;
48
49 static
50 void game_switch_state(Game *game, Game_state state)
51 {
52     game->camera = create_camera(game->renderer, game->font);
53     game->state = state;
54 }
55
56 Game *create_game(const char *level_folder,
57                   const char *sound_sample_files[],
58                   size_t sound_sample_files_count,
59                   SDL_Renderer *renderer)
60 {
61     trace_assert(level_folder);
62
63     Lt *lt = create_lt();
64
65     Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
66     if (game == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69     game->lt = lt;
70
71     game->font = PUSH_LT(
72         lt,
73         create_sprite_font_from_file(
74             "./assets/images/charmap-oldschool.bmp",
75             renderer),
76         destroy_sprite_font);
77     if (game->font == NULL) {
78         RETURN_LT(lt, NULL);
79     }
80
81     game->level_picker = PUSH_LT(
82         lt,
83         create_level_picker(
84             game->font,
85             level_folder),
86         destroy_level_picker);
87     if (game->level_picker == NULL) {
88         RETURN_LT(lt, NULL);
89     }
90
91     game->credits = PUSH_LT(
92         lt,
93         create_credits(),
94         destroy_credits);
95     if (game->credits == NULL) {
96         RETURN_LT(lt, NULL);
97     }
98
99     game->sound_samples = PUSH_LT(
100         lt,
101         create_sound_samples(
102             sound_sample_files,
103             sound_sample_files_count),
104         destroy_sound_samples);
105     if (game->sound_samples == NULL) {
106         RETURN_LT(lt, NULL);
107     }
108
109     game->settings = create_settings();
110
111     game->renderer = renderer;
112
113     for (Cursor_Style style = 0; style < CURSOR_STYLE_N; ++style) {
114         game->cursor.texs[style] = PUSH_LT(
115             lt,
116             texture_from_bmp(cursor_style_tex_files[style], renderer),
117             SDL_DestroyTexture);
118         if (SDL_SetTextureBlendMode(
119                 game->cursor.texs[style],
120                 SDL_ComposeCustomBlendMode(
121                     SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
122                     SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
123                     SDL_BLENDOPERATION_ADD,
124                     SDL_BLENDFACTOR_ONE,
125                     SDL_BLENDFACTOR_ZERO,
126                     SDL_BLENDOPERATION_ADD)) < 0) {
127             log_warn("SDL error: %s\n", SDL_GetError());
128         }
129     }
130
131
132     game->console = PUSH_LT(
133         lt,
134         create_console(game),
135         destroy_console);
136     if (game->console == NULL) {
137         RETURN_LT(lt, NULL);
138     }
139     game->console_enabled = 0;
140
141     game_switch_state(game, GAME_STATE_LEVEL_PICKER);
142
143     return game;
144 }
145
146 void destroy_game(Game *game)
147 {
148     trace_assert(game);
149     RETURN_LT0(game->lt);
150 }
151
152 int game_render(const Game *game)
153 {
154     trace_assert(game);
155
156     switch(game->state) {
157     case GAME_STATE_LEVEL: {
158         if (level_render(game->level, &game->camera) < 0) {
159             return -1;
160         }
161     } break;
162
163     case GAME_STATE_LEVEL_PICKER: {
164         if (level_picker_render(game->level_picker, &game->camera) < 0) {
165             return -1;
166         }
167     } break;
168
169     case GAME_STATE_LEVEL_EDITOR: {
170         if (level_editor_render(game->level_editor, &game->camera) < 0) {
171             return -1;
172         }
173     } break;
174
175     case GAME_STATE_CREDITS: {
176         if (credits_render(game->credits, &game->camera) < 0) {
177             return -1;
178         }
179     } break;
180
181     case GAME_STATE_SETTINGS: {
182         settings_render(&game->settings, &game->camera);
183     } break;
184
185     case GAME_STATE_QUIT: break;
186     }
187
188     if (game->console_enabled) {
189         if (console_render(game->console, &game->camera) < 0) {
190             return -1;
191         }
192     }
193
194     if (cursor_render(&game->cursor, game->renderer) < 0) {
195         return -1;
196     }
197
198     return 0;
199 }
200
201 int game_sound(Game *game)
202 {
203     switch (game->state) {
204     case GAME_STATE_LEVEL:
205         return level_sound(game->level, game->sound_samples);
206     case GAME_STATE_LEVEL_EDITOR:
207         level_editor_sound(game->level_editor, game->sound_samples);
208         return 0;
209     case GAME_STATE_LEVEL_PICKER:
210     case GAME_STATE_CREDITS:
211     case GAME_STATE_SETTINGS:
212     case GAME_STATE_QUIT:
213         return 0;
214     }
215
216     return 0;
217 }
218
219 int game_update(Game *game, float delta_time)
220 {
221     trace_assert(game);
222     trace_assert(delta_time > 0.0f);
223
224     if (game->console_enabled) {
225         if (console_update(game->console, delta_time) < 0) {
226             return -1;
227         }
228     }
229
230     switch (game->state) {
231     case GAME_STATE_LEVEL: {
232         if (level_update(game->level, delta_time) < 0) {
233             return -1;
234         }
235
236         if (level_enter_camera_event(game->level, &game->camera) < 0) {
237             return -1;
238         }
239
240     } break;
241
242     case GAME_STATE_LEVEL_PICKER: {
243         if (level_picker_update(game->level_picker, delta_time) < 0) {
244             return -1;
245         }
246
247         if (level_picker_enter_camera_event(game->level_picker, &game->camera) < 0) {
248             return -1;
249         }
250
251         const char *level_filename = level_picker_selected_level(game->level_picker);
252
253         if (level_filename != NULL) {
254             if (game_load_level(game, level_filename) < 0) {
255                 return -1;
256             }
257         }
258     } break;
259
260     case GAME_STATE_LEVEL_EDITOR: {
261         if (level_editor_focus_camera(
262                 game->level_editor,
263                 &game->camera) < 0) {
264             return -1;
265         }
266
267         level_editor_update(game->level_editor, delta_time);
268     } break;
269
270     case GAME_STATE_CREDITS: {
271         if (credits_update(game->credits, &game->camera, delta_time) < 0) {
272             return -1;
273         }
274     } break;
275
276     case GAME_STATE_SETTINGS: {
277         settings_update(&game->settings, &game->camera, delta_time);
278         sound_samples_update_volume(
279             game->sound_samples,
280             game->settings.volume_slider.value);
281     } break;
282
283     case GAME_STATE_QUIT:
284         break;
285     }
286
287     return 0;
288 }
289
290 static int game_event_running(Game *game, const SDL_Event *event)
291 {
292     trace_assert(game);
293     trace_assert(event);
294
295     if (!SDL_IsTextInputActive()) {
296         switch (event->type) {
297         case SDL_KEYDOWN: {
298             switch (event->key.keysym.sym) {
299             case SDLK_r: {
300                 game->level = RESET_LT(
301                     game->lt,
302                     game->level,
303                     create_level_from_level_editor(
304                         game->level_editor));
305                 if (game->level == NULL) {
306                     game_switch_state(game, GAME_STATE_QUIT);
307                     return -1;
308                 }
309
310                 level_disable_pause_mode(
311                     game->level,
312                     &game->camera,
313                     game->sound_samples);
314                 camera_disable_debug_mode(&game->camera);
315             } break;
316
317             case SDLK_TAB: {
318                 game_switch_state(game, GAME_STATE_LEVEL_EDITOR);
319             } break;
320             }
321         } break;
322         }
323     }
324
325     return level_event(game->level, event, &game->camera, game->sound_samples);
326 }
327
328 static int game_event_level_picker(Game *game, const SDL_Event *event)
329 {
330     trace_assert(game);
331     trace_assert(event);
332
333     switch (event->type) {
334     case SDL_KEYDOWN: {
335         switch(event->key.keysym.sym) {
336         case SDLK_n: {
337             if (game->level_editor == NULL) {
338                 game->level_editor = PUSH_LT(
339                     game->lt,
340                     create_level_editor(&game->cursor),
341                     destroy_level_editor);
342             } else {
343                 game->level_editor = RESET_LT(
344                     game->lt,
345                     game->level_editor,
346                     create_level_editor(&game->cursor));
347             }
348
349             if (game->level_editor == NULL) {
350                 return -1;
351             }
352
353             if (game->level == NULL) {
354                 game->level = PUSH_LT(
355                     game->lt,
356                     create_level_from_level_editor(
357                         game->level_editor),
358                     destroy_level);
359             } else {
360                 game->level = RESET_LT(
361                     game->lt,
362                     game->level,
363                     create_level_from_level_editor(
364                         game->level_editor));
365             }
366
367             if (game->level == NULL) {
368                 return -1;
369             }
370
371             game_switch_state(game, GAME_STATE_LEVEL);
372         } break;
373
374         case SDLK_i: {
375             game_switch_state(game, GAME_STATE_CREDITS);
376         } break;
377
378         case SDLK_s: {
379             game_switch_state(game, GAME_STATE_SETTINGS);
380         } break;
381         }
382     } break;
383     }
384
385     return level_picker_event(game->level_picker, event);
386 }
387
388 static int game_event_level_editor(Game *game, const SDL_Event *event)
389 {
390     trace_assert(game);
391     trace_assert(event);
392
393     switch (event->type) {
394     case SDL_KEYDOWN: {
395         switch (event->key.keysym.sym) {
396         case SDLK_TAB: {
397             game->level = RESET_LT(
398                 game->lt,
399                 game->level,
400                 create_level_from_level_editor(
401                     game->level_editor));
402             if (game->level == NULL) {
403                 return -1;
404             }
405             game_switch_state(game, GAME_STATE_LEVEL);
406         } break;
407         }
408     } break;
409     }
410
411     return level_editor_event(game->level_editor, event, &game->camera);
412 }
413
414 int game_event(Game *game, const SDL_Event *event)
415 {
416     trace_assert(game);
417     trace_assert(event);
418
419     // Global event handling
420     switch (event->type) {
421     case SDL_QUIT: {
422         game_switch_state(game, GAME_STATE_QUIT);
423         return 0;
424     } break;
425
426     case SDL_KEYDOWN: {
427         if (event->key.keysym.sym == SDLK_q && event->key.keysym.mod & KMOD_CTRL) {
428             game_switch_state(game, GAME_STATE_QUIT);
429             return 0;
430         }
431     } break;
432     }
433
434     // Console event handling
435     if (game->console_enabled) {
436         switch (event->type) {
437         case SDL_KEYDOWN:
438             switch (event->key.keysym.sym) {
439             case SDLK_ESCAPE:
440                 SDL_StopTextInput();
441                 game->console_enabled = 0;
442                 return 0;
443             default: {}
444             }
445
446         default: {}
447         }
448
449         return console_handle_event(game->console, event);
450     } else {
451         switch (event->type) {
452         case SDL_KEYUP: {
453             switch (event->key.keysym.sym) {
454             case SDLK_BACKQUOTE:
455             case SDLK_c: {
456                 if (event->key.keysym.mod == 0) {
457                     SDL_StartTextInput();
458                     game->console_enabled = 1;
459                     console_slide_down(game->console);
460                 }
461             } break;
462             }
463         } break;
464         }
465     }
466
467     // State event handling
468     switch (game->state) {
469     case GAME_STATE_LEVEL:
470         return game_event_running(game, event);
471
472     case GAME_STATE_LEVEL_PICKER:
473         return game_event_level_picker(game, event);
474
475     case GAME_STATE_LEVEL_EDITOR:
476         return game_event_level_editor(game, event);
477
478     case GAME_STATE_CREDITS: {
479         switch (event->type) {
480         case SDL_KEYDOWN: {
481             if (event->key.keysym.sym == SDLK_ESCAPE) {
482                 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
483                 return 0;
484             }
485         } break;
486         }
487
488         return 0;
489     } break;
490
491     case GAME_STATE_SETTINGS: {
492         switch (event->type) {
493         case SDL_KEYDOWN: {
494             if (event->key.keysym.sym == SDLK_ESCAPE) {
495                 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
496                 return 0;
497             }
498         } break;
499         }
500
501         settings_event(&game->settings, &game->camera, event);
502         return 0;
503     } break;
504
505     case GAME_STATE_QUIT:
506         return 0;
507     }
508
509     return -1;
510 }
511
512 // TODO(#1145): get rid of keyboard_state and introduce *_joystick methods
513 //
514 // keyboard_state is a global var and can be check anywhere anyway
515 int game_input(Game *game,
516                const Uint8 *const keyboard_state,
517                SDL_Joystick *the_stick_of_joy)
518 {
519     trace_assert(game);
520     trace_assert(keyboard_state);
521
522     if (game->console_enabled) {
523         return 0;
524     }
525
526     switch (game->state) {
527     case GAME_STATE_SETTINGS:
528     case GAME_STATE_CREDITS:
529     case GAME_STATE_QUIT:
530     case GAME_STATE_LEVEL_EDITOR:
531         return 0;
532
533     case GAME_STATE_LEVEL:
534         return level_input(game->level, keyboard_state, the_stick_of_joy);
535
536     case GAME_STATE_LEVEL_PICKER:
537         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
538     }
539
540     return 0;
541 }
542
543 int game_over_check(const Game *game)
544 {
545     return game->state == GAME_STATE_QUIT;
546 }
547
548 int game_load_level(Game *game, const char *level_filename)
549 {
550     trace_assert(game);
551     trace_assert(level_filename);
552
553     if (game->level_editor == NULL) {
554         game->level_editor = PUSH_LT(
555             game->lt,
556             create_level_editor_from_file(level_filename, &game->cursor),
557             destroy_level_editor);
558     } else {
559         game->level_editor = RESET_LT(
560             game->lt,
561             game->level_editor,
562             create_level_editor_from_file(level_filename, &game->cursor));
563     }
564
565     if (game->level_editor == NULL) {
566         return -1;
567     }
568
569     if (game->level == NULL) {
570         game->level = PUSH_LT(
571             game->lt,
572             create_level_from_level_editor(
573                 game->level_editor),
574             destroy_level);
575     } else {
576         game->level = RESET_LT(
577             game->lt,
578             game->level,
579             create_level_from_level_editor(
580                 game->level_editor));
581     }
582
583     if (game->level == NULL) {
584         return -1;
585     }
586
587     game_switch_state(game, GAME_STATE_LEVEL);
588
589     return 0;
590 }