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