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