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