]> git.lizzy.rs Git - nothing.git/blob - src/game.c
ce04f25365c1ac9b04ba6425a9f3d9aeb3c1e3d6
[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         case SDLK_BACKQUOTE:
247         case SDLK_c:
248             game->state = GAME_STATE_CONSOLE;
249             /* TODO(#404): when console is enabled a backquote pressed event sneaks into edit_field a gets inserted */
250             console_slide_down(game->console);
251             break;
252         }
253         break;
254
255     }
256
257     return level_event(game->level, event);
258 }
259
260 static int game_event_console(Game *game, const SDL_Event *event)
261 {
262     switch (event->type) {
263     case SDL_QUIT:
264         game->state = GAME_STATE_QUIT;
265         return 0;
266
267     case SDL_KEYDOWN:
268         switch (event->key.keysym.sym) {
269         case SDLK_ESCAPE:
270         case SDLK_BACKQUOTE:
271         case SDLK_c:
272             game->state = GAME_STATE_RUNNING;
273             return 0;
274
275         default: {}
276         }
277
278     default: {}
279     }
280
281     return console_handle_event(game->console, event);
282 }
283
284 int game_event(Game *game, const SDL_Event *event)
285 {
286     assert(game);
287     assert(event);
288
289     switch (game->state) {
290     case GAME_STATE_RUNNING:
291         return game_event_running(game, event);
292
293     case GAME_STATE_PAUSE:
294         return game_event_pause(game, event);
295
296     case GAME_STATE_CONSOLE:
297         return game_event_console(game, event);
298
299     default: {}
300     }
301
302     return 0;
303 }
304
305
306 int game_input(Game *game,
307                const Uint8 *const keyboard_state,
308                SDL_Joystick *the_stick_of_joy)
309 {
310     assert(game);
311     assert(keyboard_state);
312
313     if (game->state == GAME_STATE_QUIT  ||
314         game->state == GAME_STATE_PAUSE ||
315         game->state == GAME_STATE_CONSOLE) {
316         return 0;
317     }
318
319     return level_input(game->level, keyboard_state, the_stick_of_joy);
320 }
321
322 int game_over_check(const Game *game)
323 {
324     return game->state == GAME_STATE_QUIT;
325 }