]> git.lizzy.rs Git - nothing.git/blob - src/game.c
Merge pull request #1134 from tsoding/1129
[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                 SDL_StartTextInput();
425                 game->console_enabled = 1;
426                 console_slide_down(game->console);
427             } break;
428             }
429         } break;
430         }
431     }
432
433     // State event handling
434     switch (game->state) {
435     case GAME_STATE_LEVEL:
436         return game_event_running(game, event);
437
438     case GAME_STATE_LEVEL_PICKER:
439         return game_event_level_picker(game, event);
440
441     case GAME_STATE_LEVEL_EDITOR:
442         return game_event_level_editor(game, event);
443
444     case GAME_STATE_SETTINGS: {
445         switch (event->type) {
446         case SDL_KEYDOWN: {
447             if (event->key.keysym.sym == SDLK_ESCAPE) {
448                 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
449                 return 0;
450             }
451         } break;
452         }
453
454         settings_event(&game->settings, &game->camera, event);
455         return 0;
456     } break;
457
458     case GAME_STATE_QUIT:
459         return 0;
460     }
461
462     return -1;
463 }
464
465
466 // TODO: get rid of keyboard_state (because it's a global var and can
467 // be check anywhere anyway). And introduce *_joystick methods.
468 int game_input(Game *game,
469                const Uint8 *const keyboard_state,
470                SDL_Joystick *the_stick_of_joy)
471 {
472     trace_assert(game);
473     trace_assert(keyboard_state);
474
475     if (game->console_enabled) {
476         return 0;
477     }
478
479     switch (game->state) {
480     case GAME_STATE_SETTINGS:
481     case GAME_STATE_QUIT:
482     case GAME_STATE_LEVEL_EDITOR:
483         return 0;
484
485     case GAME_STATE_LEVEL:
486         return level_input(game->level, keyboard_state, the_stick_of_joy);
487
488     case GAME_STATE_LEVEL_PICKER:
489         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
490     }
491
492     return 0;
493 }
494
495 int game_over_check(const Game *game)
496 {
497     return game->state == GAME_STATE_QUIT;
498 }
499
500 int game_load_level(Game *game, const char *level_filename)
501 {
502     trace_assert(game);
503     trace_assert(level_filename);
504
505     if (game->level_editor == NULL) {
506         game->level_editor = PUSH_LT(
507             game->lt,
508             create_level_editor_from_file(level_filename, &game->cursor),
509             destroy_level_editor);
510     } else {
511         game->level_editor = RESET_LT(
512             game->lt,
513             game->level_editor,
514             create_level_editor_from_file(level_filename, &game->cursor));
515     }
516
517     if (game->level_editor == NULL) {
518         return -1;
519     }
520
521     if (game->level == NULL) {
522         game->level = PUSH_LT(
523             game->lt,
524             create_level_from_level_editor(
525                 game->level_editor),
526             destroy_level);
527     } else {
528         game->level = RESET_LT(
529             game->lt,
530             game->level,
531             create_level_from_level_editor(
532                 game->level_editor));
533     }
534
535     if (game->level == NULL) {
536         return -1;
537     }
538
539     game_switch_state(game, GAME_STATE_LEVEL);
540
541     return 0;
542 }