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