]> git.lizzy.rs Git - nothing.git/blob - src/game.c
Fix #404
[nothing.git] / src / game.c
1 #include <SDL2/SDL.h>
2 #include <SDL2/SDL_mixer.h>
3 #include <assert.h>
4 #include <stdio.h>
5
6 #include "game.h"
7 #include "ui/edit_field.h"
8 #include "game/level.h"
9 #include "ui/console.h"
10 #include "game/sound_samples.h"
11 #include "system/error.h"
12 #include "system/lt.h"
13
14 typedef enum Game_state {
15     GAME_STATE_RUNNING = 0,
16     GAME_STATE_PAUSE,
17     GAME_STATE_CONSOLE,
18     GAME_STATE_QUIT,
19
20     GAME_STATE_N
21 } Game_state;
22
23 typedef struct Game {
24     Lt *lt;
25
26     Game_state state;
27     Level *level;
28     char *level_file_path;
29     Sound_samples *sound_samples;
30     Camera *camera;
31     Sprite_font *font;
32     Console *console;
33     SDL_Renderer *renderer;
34 } Game;
35
36 Game *create_game(const char *level_file_path,
37                     const char *sound_sample_files[],
38                     size_t sound_sample_files_count,
39                     SDL_Renderer *renderer)
40 {
41     assert(level_file_path);
42
43     Lt *const lt = create_lt();
44     if (lt == NULL) {
45         return NULL;
46     }
47
48     Game *game = PUSH_LT(lt, malloc(sizeof(Game)), free);
49     if (game == NULL) {
50         throw_error(ERROR_TYPE_LIBC);
51         RETURN_LT(lt, NULL);
52     }
53     game->lt = lt;
54
55     game->renderer = renderer;
56
57     game->level = PUSH_LT(
58         lt,
59         create_level_from_file(level_file_path),
60         destroy_level);
61     if (game->level == NULL) {
62         RETURN_LT(lt, NULL);
63     }
64
65     game->level_file_path = PUSH_LT(lt, malloc(sizeof(char) * (strlen(level_file_path) + 1)), free);
66     if (game->level_file_path == NULL) {
67         throw_error(ERROR_TYPE_LIBC);
68         RETURN_LT(lt, NULL);
69     }
70     strcpy(game->level_file_path, level_file_path);
71
72     game->font = PUSH_LT(
73         lt,
74         create_sprite_font_from_file("fonts/charmap-oldschool.bmp", renderer),
75         destroy_sprite_font);
76     if (game->font == NULL) {
77         RETURN_LT(lt, NULL);
78     }
79
80     game->camera = PUSH_LT(lt, create_camera(renderer, game->font), destroy_camera);
81     if (game->camera == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84
85     game->sound_samples = PUSH_LT(
86         lt,
87         create_sound_samples(
88             sound_sample_files,
89             sound_sample_files_count),
90         destroy_sound_samples);
91     if (game->sound_samples == NULL) {
92         RETURN_LT(lt, NULL);
93     }
94
95     game->console = PUSH_LT(
96         lt,
97         create_console(game->level, game->font),
98         destroy_console);
99     if (game->console == NULL) {
100         RETURN_LT(lt, NULL);
101     }
102
103     game->state = GAME_STATE_RUNNING;
104
105     return game;
106 }
107
108 void destroy_game(Game *game)
109 {
110     assert(game);
111     RETURN_LT0(game->lt);
112 }
113
114 int game_render(const Game *game)
115 {
116     assert(game);
117
118     if (game->state == GAME_STATE_QUIT) {
119         return 0;
120     }
121
122     if (level_render(game->level, game->camera) < 0) {
123         return -1;
124     }
125
126     if (game->state == GAME_STATE_CONSOLE) {
127         if (console_render(game->console, game->renderer) < 0) {
128             return -1;
129         }
130     }
131
132     return 0;
133 }
134
135 int game_sound(Game *game)
136 {
137     return level_sound(game->level, game->sound_samples);
138 }
139
140 int game_update(Game *game, float delta_time)
141 {
142     assert(game);
143     assert(delta_time > 0.0f);
144
145     if (game->state == GAME_STATE_QUIT) {
146         return 0;
147     }
148
149     if (game->state == GAME_STATE_RUNNING || game->state == GAME_STATE_CONSOLE) {
150         if (level_update(game->level, delta_time) < 0) {
151             return -1;
152         }
153
154         if (level_enter_camera_event(game->level, game->camera) < 0) {
155             return -1;
156         }
157
158         if (console_update(game->console, delta_time) < 0) {
159             return -1;
160         }
161     }
162
163     return 0;
164 }
165
166
167 static int game_event_pause(Game *game, const SDL_Event *event)
168 {
169     assert(game);
170     assert(event);
171
172     switch (event->type) {
173     case SDL_QUIT:
174         game->state = GAME_STATE_QUIT;
175         break;
176
177     case SDL_KEYDOWN:
178         switch (event->key.keysym.sym) {
179         case SDLK_p:
180             game->state = GAME_STATE_RUNNING;
181             camera_toggle_blackwhite_mode(game->camera);
182             sound_samples_toggle_pause(game->sound_samples);
183             break;
184         case SDLK_l:
185             camera_toggle_debug_mode(game->camera);
186             level_toggle_debug_mode(game->level);
187             break;
188         }
189         break;
190     }
191
192     return level_event(game->level, event);
193 }
194
195 static int game_event_running(Game *game, const SDL_Event *event)
196 {
197     assert(game);
198     assert(event);
199
200     switch (event->type) {
201     case SDL_QUIT:
202         game->state = GAME_STATE_QUIT;
203         break;
204
205     case SDL_KEYDOWN:
206         switch (event->key.keysym.sym) {
207         case SDLK_r:
208             printf("Reloading the level from '%s'...\n", game->level_file_path);
209
210             game->level = RESET_LT(
211                 game->lt,
212                 game->level,
213                 create_level_from_file(
214                     game->level_file_path));
215
216             if (game->level == NULL) {
217                 print_current_error_msg("Could not reload the level");
218                 game->state = GAME_STATE_QUIT;
219                 return -1;
220             }
221
222             camera_disable_debug_mode(game->camera);
223
224             break;
225
226         case SDLK_q:
227             printf("Reloading the level's platforms from '%s'...\n", game->level_file_path);
228             if (level_reload_preserve_player(game->level, game->level_file_path) < 0) {
229                 print_current_error_msg("Could not reload the level");
230                 game->state = GAME_STATE_QUIT;
231                 return -1;
232             }
233             break;
234
235         case SDLK_p:
236             game->state = GAME_STATE_PAUSE;
237             camera_toggle_blackwhite_mode(game->camera);
238             sound_samples_toggle_pause(game->sound_samples);
239             break;
240
241         case SDLK_l:
242             camera_toggle_debug_mode(game->camera);
243             level_toggle_debug_mode(game->level);
244             break;
245         }
246         break;
247     case SDL_KEYUP:
248         switch (event->key.keysym.sym) {
249         case SDLK_BACKQUOTE:
250         case SDLK_c:
251             SDL_StartTextInput();
252             game->state = GAME_STATE_CONSOLE;
253             /* TODO(#404): when console is enabled a backquote pressed event sneaks into edit_field a gets inserted */
254             console_slide_down(game->console);
255             break;
256         }
257         break;
258
259     }
260
261     return level_event(game->level, event);
262 }
263
264 static int game_event_console(Game *game, const SDL_Event *event)
265 {
266     switch (event->type) {
267     case SDL_QUIT:
268         game->state = GAME_STATE_QUIT;
269         return 0;
270
271     case SDL_KEYDOWN:
272         switch (event->key.keysym.sym) {
273         case SDLK_ESCAPE:
274             SDL_StopTextInput();
275             game->state = GAME_STATE_RUNNING;
276             return 0;
277
278         default: {}
279         }
280
281     default: {}
282     }
283
284     return console_handle_event(game->console, event);
285 }
286
287 int game_event(Game *game, const SDL_Event *event)
288 {
289     assert(game);
290     assert(event);
291
292     switch (game->state) {
293     case GAME_STATE_RUNNING:
294         return game_event_running(game, event);
295
296     case GAME_STATE_PAUSE:
297         return game_event_pause(game, event);
298
299     case GAME_STATE_CONSOLE:
300         return game_event_console(game, event);
301
302     default: {}
303     }
304
305     return 0;
306 }
307
308
309 int game_input(Game *game,
310                const Uint8 *const keyboard_state,
311                SDL_Joystick *the_stick_of_joy)
312 {
313     assert(game);
314     assert(keyboard_state);
315
316     if (game->state == GAME_STATE_QUIT  ||
317         game->state == GAME_STATE_PAUSE ||
318         game->state == GAME_STATE_CONSOLE) {
319         return 0;
320     }
321
322     return level_input(game->level, keyboard_state, the_stick_of_joy);
323 }
324
325 int game_over_check(const Game *game)
326 {
327     return game->state == GAME_STATE_QUIT;
328 }