]> git.lizzy.rs Git - nothing.git/blob - src/game.c
2dd10049a0919bc10c4cf6393748f844ad89cfa7
[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             level_picker_clean_selection(game->level_picker);
372             game_switch_state(game, GAME_STATE_LEVEL);
373         } break;
374
375         case SDLK_i: {
376             game_switch_state(game, GAME_STATE_CREDITS);
377         } break;
378
379         case SDLK_s: {
380             game_switch_state(game, GAME_STATE_SETTINGS);
381         } break;
382         }
383     } break;
384     }
385
386     return level_picker_event(game->level_picker, event);
387 }
388
389 static int game_event_level_editor(Game *game, const SDL_Event *event)
390 {
391     trace_assert(game);
392     trace_assert(event);
393
394     switch (event->type) {
395     case SDL_KEYDOWN: {
396         switch (event->key.keysym.sym) {
397         case SDLK_TAB: {
398             game->level = RESET_LT(
399                 game->lt,
400                 game->level,
401                 create_level_from_level_editor(
402                     game->level_editor));
403             if (game->level == NULL) {
404                 return -1;
405             }
406
407             level_picker_clean_selection(game->level_picker);
408             game_switch_state(game, GAME_STATE_LEVEL);
409         } break;
410         }
411     } break;
412     }
413
414     return level_editor_event(game->level_editor, event, &game->camera);
415 }
416
417 int game_event(Game *game, const SDL_Event *event)
418 {
419     trace_assert(game);
420     trace_assert(event);
421
422     // Global event handling
423     switch (event->type) {
424     case SDL_QUIT: {
425         game_switch_state(game, GAME_STATE_QUIT);
426         return 0;
427     } break;
428
429     case SDL_KEYDOWN: {
430         if (event->key.keysym.sym == SDLK_q && event->key.keysym.mod & KMOD_CTRL) {
431             game_switch_state(game, GAME_STATE_QUIT);
432             return 0;
433         }
434     } break;
435     }
436
437     // Console event handling
438     if (game->console_enabled) {
439         switch (event->type) {
440         case SDL_KEYDOWN:
441             switch (event->key.keysym.sym) {
442             case SDLK_ESCAPE:
443                 SDL_StopTextInput();
444                 game->console_enabled = 0;
445                 return 0;
446             default: {}
447             }
448
449         default: {}
450         }
451
452         return console_handle_event(game->console, event);
453     } else {
454         switch (event->type) {
455         case SDL_KEYUP: {
456             switch (event->key.keysym.sym) {
457             case SDLK_BACKQUOTE:
458             case SDLK_c: {
459                 if (event->key.keysym.mod == 0) {
460                     SDL_StartTextInput();
461                     game->console_enabled = 1;
462                     console_slide_down(game->console);
463                 }
464             } break;
465             }
466         } break;
467         case SDL_KEYDOWN: {
468             switch (event->key.keysym.sym) {
469             case SDLK_ESCAPE: {
470                 if (game->state == GAME_STATE_LEVEL) {
471                     game_switch_state(game, GAME_STATE_LEVEL_PICKER);
472                 }
473             } break;
474             }
475         } break;
476         }
477     }
478
479     // State event handling
480     switch (game->state) {
481     case GAME_STATE_LEVEL:
482         return game_event_running(game, event);
483
484     case GAME_STATE_LEVEL_PICKER:
485         return game_event_level_picker(game, event);
486
487     case GAME_STATE_LEVEL_EDITOR:
488         return game_event_level_editor(game, event);
489
490     case GAME_STATE_CREDITS: {
491         switch (event->type) {
492         case SDL_KEYDOWN: {
493             if (event->key.keysym.sym == SDLK_ESCAPE) {
494                 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
495                 return 0;
496             }
497         } break;
498         }
499
500         return 0;
501     } break;
502
503     case GAME_STATE_SETTINGS: {
504         switch (event->type) {
505         case SDL_KEYDOWN: {
506             if (event->key.keysym.sym == SDLK_ESCAPE) {
507                 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
508                 return 0;
509             }
510         } break;
511         }
512
513         settings_event(&game->settings, &game->camera, event);
514         return 0;
515     } break;
516
517     case GAME_STATE_QUIT:
518         return 0;
519     }
520
521     return -1;
522 }
523
524 // TODO(#1145): get rid of keyboard_state and introduce *_joystick methods
525 //
526 // keyboard_state is a global var and can be check anywhere anyway
527 int game_input(Game *game,
528                const Uint8 *const keyboard_state,
529                SDL_Joystick *the_stick_of_joy)
530 {
531     trace_assert(game);
532     trace_assert(keyboard_state);
533
534     if (game->console_enabled) {
535         return 0;
536     }
537
538     switch (game->state) {
539     case GAME_STATE_SETTINGS:
540     case GAME_STATE_CREDITS:
541     case GAME_STATE_QUIT:
542     case GAME_STATE_LEVEL_EDITOR:
543         return 0;
544
545     case GAME_STATE_LEVEL:
546         return level_input(game->level, keyboard_state, the_stick_of_joy);
547
548     case GAME_STATE_LEVEL_PICKER:
549         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
550     }
551
552     return 0;
553 }
554
555 int game_over_check(const Game *game)
556 {
557     return game->state == GAME_STATE_QUIT;
558 }
559
560 int game_load_level(Game *game, const char *level_filename)
561 {
562     trace_assert(game);
563     trace_assert(level_filename);
564
565     if (game->level_editor == NULL) {
566         game->level_editor = PUSH_LT(
567             game->lt,
568             create_level_editor_from_file(level_filename, &game->cursor),
569             destroy_level_editor);
570     } else {
571         game->level_editor = RESET_LT(
572             game->lt,
573             game->level_editor,
574             create_level_editor_from_file(level_filename, &game->cursor));
575     }
576
577     if (game->level_editor == NULL) {
578         return -1;
579     }
580
581     if (game->level == NULL) {
582         game->level = PUSH_LT(
583             game->lt,
584             create_level_from_level_editor(
585                 game->level_editor),
586             destroy_level);
587     } else {
588         game->level = RESET_LT(
589             game->lt,
590             game->level,
591             create_level_from_level_editor(
592                 game->level_editor));
593     }
594
595     if (game->level == NULL) {
596         return -1;
597     }
598
599     level_picker_clean_selection(game->level_picker);
600     game_switch_state(game, GAME_STATE_LEVEL);
601
602     return 0;
603 }