]> git.lizzy.rs Git - nothing.git/blob - src/game.c
2c5d49b5ad3902581f31f66248cd46239a054f52
[nothing.git] / src / game.c
1 #include <SDL2/SDL.h>
2 #include <SDL2/SDL_mixer.h>
3 #include "system/stacktrace.h"
4 #include <stdio.h>
5
6 #include "game.h"
7 #include "game/level.h"
8 #include "game/sound_samples.h"
9 #include "game/level_picker.h"
10 #include "system/log.h"
11 #include "system/lt.h"
12 #include "system/nth_alloc.h"
13 #include "ui/console.h"
14 #include "ui/edit_field.h"
15 #include "system/str.h"
16 #include "ebisp/builtins.h"
17 #include "broadcast.h"
18
19 typedef enum Game_state {
20     GAME_STATE_RUNNING = 0,
21     GAME_STATE_PAUSE,
22     GAME_STATE_CONSOLE,
23     GAME_STATE_LEVEL_PICKER,
24     GAME_STATE_QUIT
25 } Game_state;
26
27 typedef struct Game {
28     Lt *lt;
29
30     Game_state state;
31     Broadcast *broadcast;
32     Sprite_font *font;
33     LevelPicker *level_picker;
34     Level *level;
35     Sound_samples *sound_samples;
36     Camera *camera;
37     Console *console;
38     SDL_Renderer *renderer;
39 } Game;
40
41 Game *create_game(const char *level_folder,
42                   const char *sound_sample_files[],
43                   size_t sound_sample_files_count,
44                   SDL_Renderer *renderer)
45 {
46     trace_assert(level_folder);
47
48     Lt *const lt = create_lt();
49     if (lt == NULL) {
50         return NULL;
51     }
52
53     Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free);
54     if (game == NULL) {
55         RETURN_LT(lt, NULL);
56     }
57     game->lt = lt;
58
59     game->state = GAME_STATE_LEVEL_PICKER;
60
61     game->broadcast = PUSH_LT(
62         lt,
63         create_broadcast(game),
64         destroy_broadcast);
65     if (game->broadcast == NULL) {
66         RETURN_LT(lt, NULL);
67     }
68
69     game->font = PUSH_LT(
70         lt,
71         create_sprite_font_from_file(
72             "fonts/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->level = NULL;
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->camera = PUSH_LT(
102         lt,
103         create_camera(renderer, game->font),
104         destroy_camera);
105     if (game->camera == NULL) {
106         RETURN_LT(lt, NULL);
107     }
108
109     game->console = PUSH_LT(
110         lt,
111         create_console(game->broadcast, game->font),
112         destroy_console);
113     if (game->console == NULL) {
114         RETURN_LT(lt, NULL);
115     }
116
117     game->renderer = renderer;
118
119     return game;
120 }
121
122 void destroy_game(Game *game)
123 {
124     trace_assert(game);
125     RETURN_LT0(game->lt);
126 }
127
128 int game_render(const Game *game)
129 {
130     trace_assert(game);
131
132     switch(game->state) {
133     case GAME_STATE_RUNNING:
134     case GAME_STATE_PAUSE: {
135         if (level_render(game->level, game->camera) < 0) {
136             return -1;
137         }
138     } break;
139
140     case GAME_STATE_CONSOLE: {
141         if (level_render(game->level, game->camera) < 0) {
142             return -1;
143         }
144
145         if (console_render(game->console, game->renderer) < 0) {
146             return -1;
147         }
148     } break;
149
150     case GAME_STATE_LEVEL_PICKER: {
151         if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
152             return -1;
153         }
154     } break;
155
156     case GAME_STATE_QUIT: break;
157     }
158
159     return 0;
160 }
161
162 int game_sound(Game *game)
163 {
164     switch (game->state) {
165     case GAME_STATE_RUNNING:
166     case GAME_STATE_PAUSE:
167     case GAME_STATE_CONSOLE:
168         return level_sound(game->level, game->sound_samples);
169     case GAME_STATE_LEVEL_PICKER:
170     case GAME_STATE_QUIT:
171         return 0;
172     }
173
174     return 0;
175 }
176
177 int game_update(Game *game, float delta_time)
178 {
179     trace_assert(game);
180     trace_assert(delta_time > 0.0f);
181
182     switch (game->state) {
183     case GAME_STATE_RUNNING: {
184         if (level_update(game->level, delta_time) < 0) {
185             return -1;
186         }
187
188         if (level_enter_camera_event(game->level, game->camera) < 0) {
189             return -1;
190         }
191
192     } break;
193
194     case GAME_STATE_CONSOLE: {
195         if (level_update(game->level, delta_time) < 0) {
196             return -1;
197         }
198
199         if (level_enter_camera_event(game->level, game->camera) < 0) {
200             return -1;
201         }
202
203         if (console_update(game->console, delta_time) < 0) {
204             return -1;
205         }
206     } break;
207
208     case GAME_STATE_LEVEL_PICKER: {
209         if (level_picker_update(game->level_picker, delta_time) < 0) {
210             return -1;
211         }
212
213         if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
214             return -1;
215         }
216
217         const char *level_folder = level_picker_selected_level(game->level_picker);
218
219         if (level_folder != NULL) {
220             if (game->level == NULL) {
221                 game->level = PUSH_LT(
222                     game->lt,
223                     create_level_from_file(level_folder, game->broadcast),
224                     destroy_level);
225             } else {
226                 game->level = RESET_LT(
227                     game->lt,
228                     game->level,
229                     create_level_from_file(level_folder, game->broadcast));
230             }
231
232             if (game->level == NULL) {
233                 return -1;
234             }
235
236             game->state = GAME_STATE_RUNNING;
237         }
238
239     } break;
240
241     case GAME_STATE_PAUSE:
242     case GAME_STATE_QUIT:
243         break;
244     }
245
246     return 0;
247 }
248
249
250 static int game_event_pause(Game *game, const SDL_Event *event)
251 {
252     trace_assert(game);
253     trace_assert(event);
254
255     switch (event->type) {
256     case SDL_QUIT:
257         game->state = GAME_STATE_QUIT;
258         break;
259
260     case SDL_KEYDOWN:
261         switch (event->key.keysym.sym) {
262         case SDLK_p:
263             game->state = GAME_STATE_RUNNING;
264             camera_toggle_blackwhite_mode(game->camera);
265             sound_samples_toggle_pause(game->sound_samples);
266             break;
267         case SDLK_l:
268             camera_toggle_debug_mode(game->camera);
269             level_toggle_debug_mode(game->level);
270             break;
271         }
272         break;
273     }
274
275     return level_event(game->level, event);
276 }
277
278 static int game_event_running(Game *game, const SDL_Event *event)
279 {
280     trace_assert(game);
281     trace_assert(event);
282
283     switch (event->type) {
284     case SDL_QUIT:
285         game->state = GAME_STATE_QUIT;
286         break;
287
288     case SDL_KEYDOWN:
289         switch (event->key.keysym.sym) {
290         case SDLK_r: {
291             const char *level_filename = level_picker_selected_level(game->level_picker);
292
293             log_info("Reloading the level from '%s'...\n", level_filename);
294
295             game->level = RESET_LT(
296                 game->lt,
297                 game->level,
298                 create_level_from_file(
299                     level_filename,
300                     game->broadcast));
301
302             if (game->level == NULL) {
303                 log_fail("Could not reload level %s\n", level_filename);
304                 game->state = GAME_STATE_QUIT;
305                 return -1;
306             }
307
308             camera_disable_debug_mode(game->camera);
309         } break;
310
311         case SDLK_q:
312             if (level_reload_preserve_player(game->level, game->broadcast) < 0) {
313                 log_fail("Could not reload level\n");
314                 game->state = GAME_STATE_QUIT;
315                 return -1;
316             }
317             break;
318
319         case SDLK_p:
320             game->state = GAME_STATE_PAUSE;
321             camera_toggle_blackwhite_mode(game->camera);
322             sound_samples_toggle_pause(game->sound_samples);
323             break;
324
325         case SDLK_l:
326             camera_toggle_debug_mode(game->camera);
327             level_toggle_debug_mode(game->level);
328             break;
329         }
330         break;
331     case SDL_KEYUP:
332         switch (event->key.keysym.sym) {
333         case SDLK_BACKQUOTE:
334         case SDLK_c:
335             SDL_StartTextInput();
336             game->state = GAME_STATE_CONSOLE;
337             console_slide_down(game->console);
338             break;
339         }
340         break;
341
342     }
343
344     return level_event(game->level, event);
345 }
346
347 static int game_event_console(Game *game, const SDL_Event *event)
348 {
349     switch (event->type) {
350     case SDL_QUIT:
351         game->state = GAME_STATE_QUIT;
352         return 0;
353
354     case SDL_KEYDOWN:
355         switch (event->key.keysym.sym) {
356         case SDLK_ESCAPE:
357             SDL_StopTextInput();
358             game->state = GAME_STATE_RUNNING;
359             return 0;
360
361         default: {}
362         }
363
364     default: {}
365     }
366
367     return console_handle_event(game->console, event);
368 }
369
370 static int game_event_level_picker(Game *game, const SDL_Event *event)
371 {
372     trace_assert(game);
373     trace_assert(event);
374
375     switch (event->type) {
376     case SDL_QUIT:
377         game->state = GAME_STATE_QUIT;
378         return 0;
379
380     default:
381         return level_picker_event(game->level_picker, event);
382     }
383 }
384
385 int game_event(Game *game, const SDL_Event *event)
386 {
387     trace_assert(game);
388     trace_assert(event);
389
390     switch (game->state) {
391     case GAME_STATE_RUNNING:
392         return game_event_running(game, event);
393
394     case GAME_STATE_PAUSE:
395         return game_event_pause(game, event);
396
397     case GAME_STATE_CONSOLE:
398         return game_event_console(game, event);
399
400     case GAME_STATE_LEVEL_PICKER:
401         return game_event_level_picker(game, event);
402
403     default: {}
404     }
405
406     return 0;
407 }
408
409
410 int game_input(Game *game,
411                const Uint8 *const keyboard_state,
412                SDL_Joystick *the_stick_of_joy)
413 {
414     trace_assert(game);
415     trace_assert(keyboard_state);
416
417     switch (game->state) {
418     case GAME_STATE_QUIT:
419     case GAME_STATE_PAUSE:
420     case GAME_STATE_CONSOLE:
421         return 0;
422
423     case GAME_STATE_RUNNING:
424         return level_input(game->level, keyboard_state, the_stick_of_joy);
425
426     case GAME_STATE_LEVEL_PICKER:
427         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
428     }
429
430     return 0;
431 }
432
433 int game_over_check(const Game *game)
434 {
435     return game->state == GAME_STATE_QUIT;
436 }
437
438 struct EvalResult
439 game_send(Game *game, Gc *gc, struct Scope *scope,
440           struct Expr path)
441 {
442     trace_assert(game);
443     trace_assert(gc);
444     trace_assert(scope);
445
446     const char *target = NULL;
447     struct Expr rest = void_expr();
448     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
449     if (res.is_error) {
450         return res;
451     }
452
453     if (strcmp(target, "level") == 0) {
454         return level_send(game->level, gc, scope, rest);
455     } else if (strcmp(target, "menu") == 0) {
456         level_picker_clean_selection(game->level_picker);
457         game->state = GAME_STATE_LEVEL_PICKER;
458         return eval_success(NIL(gc));
459     }
460
461     return unknown_target(gc, "game", target);
462 }