]> git.lizzy.rs Git - nothing.git/blob - src/game.c
2d68c37dbfb3e800fb7a26867c24acd8567975a7
[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     int cursor_x;
44     int cursor_y;
45 } Game;
46
47 static
48 void game_switch_state(Game *game, Game_state state)
49 {
50     game->camera = create_camera(game->renderer, game->font);
51     game->state = state;
52 }
53
54 Game *create_game(const char *level_folder,
55                   const char *sound_sample_files[],
56                   size_t sound_sample_files_count,
57                   SDL_Renderer *renderer)
58 {
59     trace_assert(level_folder);
60
61     Lt *lt = create_lt();
62
63     Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
64     if (game == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67     game->lt = lt;
68
69     game->font = PUSH_LT(
70         lt,
71         create_sprite_font_from_file(
72             "./assets/images/charmap-oldschool.bmp",
73             renderer),
74         destroy_sprite_font);
75     if (game->font == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     game->level_picker = PUSH_LT(
80         lt,
81         create_level_picker(
82             game->font,
83             level_folder),
84         destroy_level_picker);
85     if (game->level_picker == NULL) {
86         RETURN_LT(lt, NULL);
87     }
88
89     game->sound_samples = PUSH_LT(
90         lt,
91         create_sound_samples(
92             sound_sample_files,
93             sound_sample_files_count),
94         destroy_sound_samples);
95     if (game->sound_samples == NULL) {
96         RETURN_LT(lt, NULL);
97     }
98
99     game->settings.volume_slider = (Slider) {
100         .drag = 0,
101         .value = 80.0f,
102         .max_value = 100.0f,
103     };
104     game->settings.volume_slider_scale = vec(0.25f, 0.10f);
105
106     game->renderer = renderer;
107     game->texture_cursor = PUSH_LT(
108         lt,
109         texture_from_bmp("./assets/images/cursor.bmp", renderer),
110         SDL_DestroyTexture);
111     if (SDL_SetTextureBlendMode(
112             game->texture_cursor,
113             SDL_ComposeCustomBlendMode(
114                 SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
115                 SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
116                 SDL_BLENDOPERATION_ADD,
117                 SDL_BLENDFACTOR_ONE,
118                 SDL_BLENDFACTOR_ZERO,
119                 SDL_BLENDOPERATION_ADD)) < 0) {
120         log_warn("SDL error: %s\n", SDL_GetError());
121     }
122     game->cursor_x = 0;
123     game->cursor_y = 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     RETURN_LT0(game->lt);
134 }
135
136 int game_render(const Game *game)
137 {
138     trace_assert(game);
139
140     switch(game->state) {
141     case GAME_STATE_LEVEL: {
142         if (level_render(game->level, &game->camera) < 0) {
143             return -1;
144         }
145
146         if (game_render_cursor(game) < 0) {
147             return -1;
148         }
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
157         if (game_render_cursor(game) < 0) {
158             return -1;
159         }
160     } break;
161
162     case GAME_STATE_LEVEL_EDITOR: {
163         if (level_editor_render(game->level_editor, &game->camera) < 0) {
164             return -1;
165         }
166
167         if (game_render_cursor(game) < 0) {
168             return -1;
169         }
170     } break;
171
172     case GAME_STATE_SETTINGS: {
173         settings_render(&game->settings, &game->camera);
174     } break;
175
176     case GAME_STATE_QUIT: break;
177     }
178
179     return 0;
180 }
181
182 int game_sound(Game *game)
183 {
184     switch (game->state) {
185     case GAME_STATE_LEVEL:
186         return level_sound(game->level, game->sound_samples);
187     case GAME_STATE_LEVEL_EDITOR:
188         level_editor_sound(game->level_editor, game->sound_samples);
189         return 0;
190     case GAME_STATE_LEVEL_PICKER:
191     case GAME_STATE_SETTINGS:
192     case GAME_STATE_QUIT:
193         return 0;
194     }
195
196     return 0;
197 }
198
199 int game_update(Game *game, float delta_time)
200 {
201     trace_assert(game);
202     trace_assert(delta_time > 0.0f);
203
204     switch (game->state) {
205     case GAME_STATE_LEVEL: {
206         if (level_update(game->level, delta_time) < 0) {
207             return -1;
208         }
209
210         if (level_enter_camera_event(game->level, &game->camera) < 0) {
211             return -1;
212         }
213
214     } break;
215
216     case GAME_STATE_LEVEL_PICKER: {
217         if (level_picker_update(game->level_picker, delta_time) < 0) {
218             return -1;
219         }
220
221         if (level_picker_enter_camera_event(game->level_picker, &game->camera) < 0) {
222             return -1;
223         }
224
225         const char *level_filename = level_picker_selected_level(game->level_picker);
226
227         if (level_filename != NULL) {
228             if (game->level_editor == NULL) {
229                 game->level_editor = PUSH_LT(
230                     game->lt,
231                     create_level_editor_from_file(level_filename),
232                     destroy_level_editor);
233             } else {
234                 game->level_editor = RESET_LT(
235                     game->lt,
236                     game->level_editor,
237                     create_level_editor_from_file(level_filename));
238             }
239
240             if (game->level_editor == NULL) {
241                 return -1;
242             }
243
244             if (game->level == NULL) {
245                 game->level = PUSH_LT(
246                     game->lt,
247                     create_level_from_level_editor(
248                         game->level_editor),
249                     destroy_level);
250             } else {
251                 game->level = RESET_LT(
252                     game->lt,
253                     game->level,
254                     create_level_from_level_editor(
255                         game->level_editor));
256             }
257
258             if (game->level == NULL) {
259                 return -1;
260             }
261
262             // TODO(#1071): Move level picker volume update to a more appropriate place
263             // sound_samples_update_volume(game->sound_samples, level_picker_get_volume(game->level_picker));
264             game_switch_state(game, GAME_STATE_LEVEL);
265         }
266
267     } break;
268
269     case GAME_STATE_LEVEL_EDITOR: {
270         if (level_editor_focus_camera(
271                 game->level_editor,
272                 &game->camera) < 0) {
273             return -1;
274         }
275
276         level_editor_update(game->level_editor, delta_time);
277     } break;
278
279     case GAME_STATE_SETTINGS: {
280         settings_update(&game->settings, delta_time);
281     } break;
282
283     case GAME_STATE_QUIT:
284         break;
285     }
286
287     return 0;
288 }
289
290 static int game_event_running(Game *game, const SDL_Event *event)
291 {
292     trace_assert(game);
293     trace_assert(event);
294
295     if (!SDL_IsTextInputActive()) {
296         switch (event->type) {
297         case SDL_KEYDOWN: {
298             switch (event->key.keysym.sym) {
299             case SDLK_r: {
300                 game->level = RESET_LT(
301                     game->lt,
302                     game->level,
303                     create_level_from_level_editor(
304                         game->level_editor));
305                 if (game->level == NULL) {
306                     game_switch_state(game, GAME_STATE_QUIT);
307                     return -1;
308                 }
309
310                 camera_disable_debug_mode(&game->camera);
311             } break;
312
313             case SDLK_TAB: {
314                 game_switch_state(game, GAME_STATE_LEVEL_EDITOR);
315             } break;
316             }
317         } break;
318         }
319     }
320
321     return level_event(game->level, event, &game->camera, game->sound_samples);
322 }
323
324 static int game_event_level_picker(Game *game, const SDL_Event *event)
325 {
326     trace_assert(game);
327     trace_assert(event);
328
329     switch (event->type) {
330     case SDL_KEYDOWN: {
331         switch(event->key.keysym.sym) {
332         case SDLK_n: {
333             if (game->level_editor == NULL) {
334                 game->level_editor = PUSH_LT(
335                     game->lt,
336                     create_level_editor(),
337                     destroy_level_editor);
338             } else {
339                 game->level_editor = RESET_LT(
340                     game->lt,
341                     game->level_editor,
342                     create_level_editor());
343             }
344
345             if (game->level_editor == NULL) {
346                 return -1;
347             }
348
349             if (game->level == NULL) {
350                 game->level = PUSH_LT(
351                     game->lt,
352                     create_level_from_level_editor(
353                         game->level_editor),
354                     destroy_level);
355             } else {
356                 game->level = RESET_LT(
357                     game->lt,
358                     game->level,
359                     create_level_from_level_editor(
360                         game->level_editor));
361             }
362
363             if (game->level == NULL) {
364                 return -1;
365             }
366
367             game_switch_state(game, GAME_STATE_LEVEL);
368         } break;
369         }
370     } break;
371     }
372
373     return level_picker_event(game->level_picker, event, &game->camera);
374 }
375
376 static int game_event_level_editor(Game *game, const SDL_Event *event)
377 {
378     trace_assert(game);
379     trace_assert(event);
380
381     switch (event->type) {
382     case SDL_KEYDOWN: {
383         switch (event->key.keysym.sym) {
384         case SDLK_TAB: {
385             game->level = RESET_LT(
386                 game->lt,
387                 game->level,
388                 create_level_from_level_editor(
389                     game->level_editor));
390             if (game->level == NULL) {
391                 return -1;
392             }
393             game_switch_state(game, GAME_STATE_LEVEL);
394         } break;
395         }
396     } break;
397     }
398
399     return level_editor_event(game->level_editor, event, &game->camera);
400 }
401
402 int game_event(Game *game, const SDL_Event *event)
403 {
404     trace_assert(game);
405     trace_assert(event);
406
407     switch (event->type) {
408     case SDL_QUIT: {
409         game_switch_state(game, GAME_STATE_QUIT);
410         return 0;
411     } break;
412
413     case SDL_MOUSEMOTION: {
414         game->cursor_x = event->motion.x;
415         game->cursor_y = event->motion.y;
416     } break;
417
418     case SDL_KEYDOWN: {
419         if (event->key.keysym.sym == SDLK_q && event->key.keysym.mod & KMOD_CTRL) {
420             game_switch_state(game, GAME_STATE_QUIT);
421             return 0;
422         }
423     } break;
424     }
425
426     switch (game->state) {
427     case GAME_STATE_LEVEL:
428         return game_event_running(game, event);
429
430     case GAME_STATE_LEVEL_PICKER:
431         return game_event_level_picker(game, event);
432
433     case GAME_STATE_LEVEL_EDITOR:
434         return game_event_level_editor(game, event);
435
436     case GAME_STATE_SETTINGS: {
437         settings_event(&game->settings, &game->camera, event);
438     } break;
439
440     case GAME_STATE_QUIT:
441         return 0;
442     }
443
444     return -1;
445 }
446
447
448 // TODO: get rid of keyboard_state (because it's a global var and can
449 // be check anywhere anyway). And introduce *_joystick methods.
450 int game_input(Game *game,
451                const Uint8 *const keyboard_state,
452                SDL_Joystick *the_stick_of_joy)
453 {
454     trace_assert(game);
455     trace_assert(keyboard_state);
456
457     switch (game->state) {
458     case GAME_STATE_SETTINGS:
459     case GAME_STATE_QUIT:
460     case GAME_STATE_LEVEL_EDITOR:
461         return 0;
462
463     case GAME_STATE_LEVEL:
464         return level_input(game->level, keyboard_state, the_stick_of_joy);
465
466     case GAME_STATE_LEVEL_PICKER:
467         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
468     }
469
470     return 0;
471 }
472
473 int game_over_check(const Game *game)
474 {
475     return game->state == GAME_STATE_QUIT;
476 }
477
478 // Private Functions
479
480 static int game_render_cursor(const Game *game)
481 {
482     trace_assert(game);
483
484     SDL_Rect src = {0, 0, 32, 32};
485     SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
486     if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
487         return -1;
488     }
489
490     return 0;
491 }