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