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