]> git.lizzy.rs Git - nothing.git/blob - src/game.c
(#647) Introduce an actual translation unit
[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 "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     LevelPicker *level_picker;
33     Level *level;
34     char *level_file_path;
35     Sound_samples *sound_samples;
36     Sprite_font *font;
37     Camera *camera;
38     Console *console;
39     SDL_Renderer *renderer;
40 } Game;
41
42 Game *create_game(const char *level_file_path,
43                   const char *sound_sample_files[],
44                   size_t sound_sample_files_count,
45                   SDL_Renderer *renderer)
46 {
47     trace_assert(level_file_path);
48
49     Lt *const lt = create_lt();
50     if (lt == NULL) {
51         return NULL;
52     }
53
54     Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free);
55     if (game == NULL) {
56         RETURN_LT(lt, NULL);
57     }
58     game->lt = lt;
59
60     game->state = GAME_STATE_LEVEL_PICKER;
61
62     game->broadcast = PUSH_LT(
63         lt,
64         create_broadcast(game),
65         destroy_broadcast);
66     if (game->broadcast == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     game->level_picker = PUSH_LT(
71         lt,
72         create_level_picker(level_file_path),
73         destroy_level_picker);
74     if (game->level_picker == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     game->level = NULL;
79
80     game->level_file_path = PUSH_LT(
81         lt,
82         string_duplicate(level_file_path, NULL),
83         free);
84     if (game->level_file_path == 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->font = PUSH_LT(
99         lt,
100         create_sprite_font_from_file(
101             "fonts/charmap-oldschool.bmp",
102             renderer),
103         destroy_sprite_font);
104     if (game->font == NULL) {
105         RETURN_LT(lt, NULL);
106     }
107
108     game->camera = PUSH_LT(
109         lt,
110         create_camera(renderer, game->font),
111         destroy_camera);
112     if (game->camera == NULL) {
113         RETURN_LT(lt, NULL);
114     }
115
116     game->console = PUSH_LT(
117         lt,
118         create_console(game->broadcast, game->font),
119         destroy_console);
120     if (game->console == NULL) {
121         RETURN_LT(lt, NULL);
122     }
123
124     game->renderer = renderer;
125
126     return game;
127 }
128
129 void destroy_game(Game *game)
130 {
131     trace_assert(game);
132     RETURN_LT0(game->lt);
133 }
134
135 int game_render(const Game *game)
136 {
137     trace_assert(game);
138
139     switch(game->state) {
140     case GAME_STATE_RUNNING:
141     case GAME_STATE_PAUSE: {
142         if (level_render(game->level, game->camera) < 0) {
143             return -1;
144         }
145     } break;
146
147     case GAME_STATE_CONSOLE: {
148         if (level_render(game->level, game->camera) < 0) {
149             return -1;
150         }
151
152         if (console_render(game->console, game->renderer) < 0) {
153             return -1;
154         }
155     } break;
156
157     case GAME_STATE_LEVEL_PICKER: {
158         if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
159             return -1;
160         }
161     } break;
162
163     case GAME_STATE_QUIT: break;
164     }
165
166     return 0;
167 }
168
169 int game_sound(Game *game)
170 {
171     switch (game->state) {
172     case GAME_STATE_RUNNING:
173     case GAME_STATE_PAUSE:
174     case GAME_STATE_CONSOLE:
175         return level_sound(game->level, game->sound_samples);
176     case GAME_STATE_LEVEL_PICKER:
177     case GAME_STATE_QUIT:
178         return 0;
179     }
180
181     return 0;
182 }
183
184 int game_update(Game *game, float delta_time)
185 {
186     trace_assert(game);
187     trace_assert(delta_time > 0.0f);
188
189     switch (game->state) {
190     case GAME_STATE_RUNNING: {
191         if (level_update(game->level, delta_time) < 0) {
192             return -1;
193         }
194
195         if (level_enter_camera_event(game->level, game->camera) < 0) {
196             return -1;
197         }
198
199     } break;
200
201     case GAME_STATE_CONSOLE: {
202         if (level_update(game->level, delta_time) < 0) {
203             return -1;
204         }
205
206         if (level_enter_camera_event(game->level, game->camera) < 0) {
207             return -1;
208         }
209
210         if (console_update(game->console, delta_time) < 0) {
211             return -1;
212         }
213     } break;
214
215     case GAME_STATE_LEVEL_PICKER: {
216         if (level_picker_update(game->level_picker, delta_time) < 0) {
217             return -1;
218         }
219
220         if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
221             return -1;
222         }
223
224         const char *level_file_path = level_picker_selected_level(game->level_picker);
225
226         trace_assert(game->level == NULL);
227
228         if (level_file_path != NULL) {
229             game->level = PUSH_LT(
230                 game->lt,
231                 create_level_from_file(level_file_path, game->broadcast),
232                 destroy_level);
233             if (game->level == NULL) {
234                 return -1;
235             }
236
237             game->state = GAME_STATE_RUNNING;
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             log_info("Reloading the level from '%s'...\n", game->level_file_path);
292
293             game->level = RESET_LT(
294                 game->lt,
295                 game->level,
296                 create_level_from_file(
297                     game->level_file_path, game->broadcast));
298
299             if (game->level == NULL) {
300                 log_fail("Could not reload level %s\n", game->level_file_path);
301                 game->state = GAME_STATE_QUIT;
302                 return -1;
303             }
304
305             camera_disable_debug_mode(game->camera);
306
307             break;
308
309         case SDLK_q:
310             log_info("Reloading the level's platforms from '%s'...\n", game->level_file_path);
311             if (level_reload_preserve_player(game->level, game->level_file_path, game->broadcast) < 0) {
312                 log_fail("Could not reload level %s\n", game->level_file_path);
313                 game->state = GAME_STATE_QUIT;
314                 return -1;
315             }
316             break;
317
318         case SDLK_p:
319             game->state = GAME_STATE_PAUSE;
320             camera_toggle_blackwhite_mode(game->camera);
321             sound_samples_toggle_pause(game->sound_samples);
322             break;
323
324         case SDLK_l:
325             camera_toggle_debug_mode(game->camera);
326             level_toggle_debug_mode(game->level);
327             break;
328         }
329         break;
330     case SDL_KEYUP:
331         switch (event->key.keysym.sym) {
332         case SDLK_BACKQUOTE:
333         case SDLK_c:
334             SDL_StartTextInput();
335             game->state = GAME_STATE_CONSOLE;
336             console_slide_down(game->console);
337             break;
338         }
339         break;
340
341     }
342
343     return level_event(game->level, event);
344 }
345
346 static int game_event_console(Game *game, const SDL_Event *event)
347 {
348     switch (event->type) {
349     case SDL_QUIT:
350         game->state = GAME_STATE_QUIT;
351         return 0;
352
353     case SDL_KEYDOWN:
354         switch (event->key.keysym.sym) {
355         case SDLK_ESCAPE:
356             SDL_StopTextInput();
357             game->state = GAME_STATE_RUNNING;
358             return 0;
359
360         default: {}
361         }
362
363     default: {}
364     }
365
366     return console_handle_event(game->console, event);
367 }
368
369 static int game_event_level_picker(Game *game, const SDL_Event *event)
370 {
371     trace_assert(game);
372     trace_assert(event);
373
374     switch (event->type) {
375     case SDL_QUIT:
376         game->state = GAME_STATE_QUIT;
377         return 0;
378
379     default:
380         return level_picker_event(game->level_picker, event);
381     }
382 }
383
384 int game_event(Game *game, const SDL_Event *event)
385 {
386     trace_assert(game);
387     trace_assert(event);
388
389     switch (game->state) {
390     case GAME_STATE_RUNNING:
391         return game_event_running(game, event);
392
393     case GAME_STATE_PAUSE:
394         return game_event_pause(game, event);
395
396     case GAME_STATE_CONSOLE:
397         return game_event_console(game, event);
398
399     case GAME_STATE_LEVEL_PICKER:
400         return game_event_level_picker(game, event);
401
402     default: {}
403     }
404
405     return 0;
406 }
407
408
409 int game_input(Game *game,
410                const Uint8 *const keyboard_state,
411                SDL_Joystick *the_stick_of_joy)
412 {
413     trace_assert(game);
414     trace_assert(keyboard_state);
415
416     switch (game->state) {
417     case GAME_STATE_QUIT:
418     case GAME_STATE_PAUSE:
419     case GAME_STATE_CONSOLE:
420         return 0;
421
422     case GAME_STATE_RUNNING:
423         return level_input(game->level, keyboard_state, the_stick_of_joy);
424
425     case GAME_STATE_LEVEL_PICKER:
426         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
427     }
428
429     return 0;
430 }
431
432 int game_over_check(const Game *game)
433 {
434     return game->state == GAME_STATE_QUIT;
435 }
436
437 struct EvalResult
438 game_send(Game *game, Gc *gc, struct Scope *scope,
439           struct Expr path)
440 {
441     trace_assert(game);
442     trace_assert(gc);
443     trace_assert(scope);
444
445     const char *target = NULL;
446     struct Expr rest = void_expr();
447     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
448     if (res.is_error) {
449         return res;
450     }
451
452     if (strcmp(target, "level") == 0) {
453         return level_send(game->level, gc, scope, rest);
454     }
455
456     return unknown_target(gc, "game", target);
457 }