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