]> git.lizzy.rs Git - nothing.git/blob - src/game.c
a8de6b9b552f396786cac892f7e69a596e290f18
[nothing.git] / src / game.c
1 #include <SDL.h>
2 #include "system/stacktrace.h"
3 #include <stdio.h>
4
5 #include "game.h"
6 #include "game/level.h"
7 #include "game/sound_samples.h"
8 #include "game/level_picker.h"
9 #include "system/log.h"
10 #include "system/lt.h"
11 #include "system/nth_alloc.h"
12 #include "ui/console.h"
13 #include "ui/edit_field.h"
14 #include "system/str.h"
15 #include "ebisp/builtins.h"
16 #include "broadcast.h"
17 #include "sdl/texture.h"
18 #include "game/level/level_editor/background_layer.h"
19 #include "game/level/level_editor.h"
20
21 static int game_render_cursor(const Game *game);
22
23 typedef enum Game_state {
24     GAME_STATE_RUNNING = 0,
25     GAME_STATE_PAUSE,
26     GAME_STATE_CONSOLE,
27     GAME_STATE_LEVEL_PICKER,
28     GAME_STATE_LEVEL_EDITOR,
29     GAME_STATE_QUIT
30 } Game_state;
31
32 typedef struct Game {
33     Lt *lt;
34
35     Game_state state;
36     Broadcast *broadcast;
37     Sprite_font *font;
38     LevelPicker *level_picker;
39     LevelEditor *level_editor;
40     Level *level;
41     Sound_samples *sound_samples;
42     Camera *camera;
43     Console *console;
44     SDL_Renderer *renderer;
45     SDL_Texture *texture_cursor;
46     int cursor_x;
47     int cursor_y;
48 } Game;
49
50 Game *create_game(const char *level_folder,
51                   const char *sound_sample_files[],
52                   size_t sound_sample_files_count,
53                   SDL_Renderer *renderer)
54 {
55     trace_assert(level_folder);
56
57     Lt *lt = create_lt();
58
59     Game *game = PUSH_LT(lt, nth_calloc(1, 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->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     if (SDL_SetTextureBlendMode(
127             game->texture_cursor,
128             SDL_ComposeCustomBlendMode(
129                 SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
130                 SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
131                 SDL_BLENDOPERATION_ADD,
132                 SDL_BLENDFACTOR_ONE,
133                 SDL_BLENDFACTOR_ZERO,
134                 SDL_BLENDOPERATION_ADD)) < 0) {
135         log_warn("SDL error: %s\n", SDL_GetError());
136     }
137     game->cursor_x = 0;
138     game->cursor_y = 0;
139
140     return game;
141 }
142
143 void destroy_game(Game *game)
144 {
145     trace_assert(game);
146     RETURN_LT0(game->lt);
147 }
148
149 int game_render(const Game *game)
150 {
151     trace_assert(game);
152
153     switch(game->state) {
154     case GAME_STATE_RUNNING:
155     case GAME_STATE_PAUSE: {
156         if (level_render(game->level, game->camera) < 0) {
157             return -1;
158         }
159     } break;
160
161     case GAME_STATE_CONSOLE: {
162         if (level_render(game->level, game->camera) < 0) {
163             return -1;
164         }
165
166         if (console_render(game->console, game->camera, game->renderer) < 0) {
167             return -1;
168         }
169     } break;
170
171     case GAME_STATE_LEVEL_PICKER: {
172         if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
173             return -1;
174         }
175
176         if (game_render_cursor(game) < 0) {
177             return -1;
178         }
179     } break;
180
181     case GAME_STATE_LEVEL_EDITOR: {
182         if (level_editor_render(game->level_editor, game->camera) < 0) {
183             return -1;
184         }
185
186         if (game_render_cursor(game) < 0) {
187             return -1;
188         }
189     } break;
190
191     case GAME_STATE_QUIT: break;
192     }
193
194     return 0;
195 }
196
197 int game_sound(Game *game)
198 {
199     switch (game->state) {
200     case GAME_STATE_RUNNING:
201     case GAME_STATE_PAUSE:
202     case GAME_STATE_CONSOLE:
203         return level_sound(game->level, game->sound_samples);
204     case GAME_STATE_LEVEL_PICKER:
205     case GAME_STATE_LEVEL_EDITOR:
206     case GAME_STATE_QUIT:
207         return 0;
208     }
209
210     return 0;
211 }
212
213 int game_update(Game *game, float delta_time)
214 {
215     trace_assert(game);
216     trace_assert(delta_time > 0.0f);
217
218     switch (game->state) {
219     case GAME_STATE_RUNNING: {
220         if (level_update(game->level, delta_time) < 0) {
221             return -1;
222         }
223
224         if (level_enter_camera_event(game->level, game->camera) < 0) {
225             return -1;
226         }
227
228     } break;
229
230     case GAME_STATE_CONSOLE: {
231         if (level_update(game->level, delta_time) < 0) {
232             return -1;
233         }
234
235         if (level_enter_camera_event(game->level, game->camera) < 0) {
236             return -1;
237         }
238
239         if (console_update(game->console, delta_time) < 0) {
240             return -1;
241         }
242     } break;
243
244     case GAME_STATE_LEVEL_PICKER: {
245         if (level_picker_update(game->level_picker, delta_time) < 0) {
246             return -1;
247         }
248
249         if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
250             return -1;
251         }
252
253         const char *level_filename = level_picker_selected_level(game->level_picker);
254
255         if (level_filename != NULL) {
256             if (game->level_editor == NULL) {
257                 game->level_editor = PUSH_LT(
258                     game->lt,
259                     create_level_editor_from_file(level_filename),
260                     destroy_level_editor);
261             } else {
262                 game->level_editor = RESET_LT(
263                     game->lt,
264                     game->level_editor,
265                     create_level_editor_from_file(level_filename));
266             }
267
268             if (game->level_editor == NULL) {
269                 return -1;
270             }
271
272             if (game->level == NULL) {
273                 game->level = PUSH_LT(
274                     game->lt,
275                     create_level_from_level_editor(
276                         game->level_editor,
277                         game->broadcast),
278                     destroy_level);
279             } else {
280                 game->level = RESET_LT(
281                     game->lt,
282                     game->level,
283                     create_level_from_level_editor(
284                         game->level_editor,
285                         game->broadcast));
286             }
287
288             if (game->level == NULL) {
289                 return -1;
290             }
291
292             game->state = GAME_STATE_RUNNING;
293         }
294
295     } break;
296
297     case GAME_STATE_LEVEL_EDITOR: {
298         if (level_editor_focus_camera(
299                 game->level_editor,
300                 game->camera) < 0) {
301             return -1;
302         }
303
304         level_editor_update(game->level_editor, delta_time);
305     } break;
306
307     case GAME_STATE_PAUSE:
308     case GAME_STATE_QUIT:
309         break;
310     }
311
312     return 0;
313 }
314
315
316 static int game_event_pause(Game *game, const SDL_Event *event)
317 {
318     trace_assert(game);
319     trace_assert(event);
320
321     switch (event->type) {
322     case SDL_KEYDOWN:
323         switch (event->key.keysym.sym) {
324         case SDLK_p:
325             game->state = GAME_STATE_RUNNING;
326             camera_toggle_blackwhite_mode(game->camera);
327             sound_samples_toggle_pause(game->sound_samples);
328             break;
329         case SDLK_l:
330             camera_toggle_debug_mode(game->camera);
331             level_toggle_debug_mode(game->level);
332             break;
333         }
334         break;
335     }
336
337     return level_event(game->level, event);
338 }
339
340 static int game_event_running(Game *game, const SDL_Event *event)
341 {
342     trace_assert(game);
343     trace_assert(event);
344
345     switch (event->type) {
346     case SDL_KEYDOWN: {
347         switch (event->key.keysym.sym) {
348         case SDLK_r: {
349             const char *level_filename = game->level_editor->file_name;
350
351             if (!level_filename) {
352                 log_warn("Could not reload the level. There is no associated file.\n");
353                 return 0;
354             }
355
356             log_info("Reloading the level from '%s'...\n", level_filename);
357
358             game->level_editor = RESET_LT(
359                 game->lt,
360                 game->level_editor,
361                 create_level_editor_from_file(level_filename));
362             if (game->level_editor == NULL) {
363                 log_fail("Could not reload level %s\n", level_filename);
364                 game->state = GAME_STATE_QUIT;
365                 return -1;
366             }
367
368             game->level = RESET_LT(
369                 game->lt,
370                 game->level,
371                 create_level_from_level_editor(
372                     game->level_editor,
373                     game->broadcast));
374             if (game->level == NULL) {
375                 log_fail("Could not reload level %s\n", level_filename);
376                 game->state = GAME_STATE_QUIT;
377                 return -1;
378             }
379
380             camera_disable_debug_mode(game->camera);
381         } break;
382
383         case SDLK_p: {
384             game->state = GAME_STATE_PAUSE;
385             camera_toggle_blackwhite_mode(game->camera);
386             sound_samples_toggle_pause(game->sound_samples);
387         } break;
388
389         case SDLK_l: {
390             camera_toggle_debug_mode(game->camera);
391             level_toggle_debug_mode(game->level);
392         } break;
393
394         case SDLK_TAB: {
395             game->state = GAME_STATE_LEVEL_EDITOR;
396         } break;
397         }
398     } break;
399
400     case SDL_KEYUP: {
401         switch (event->key.keysym.sym) {
402         case SDLK_BACKQUOTE:
403         case SDLK_c: {
404             SDL_StartTextInput();
405             game->state = GAME_STATE_CONSOLE;
406             console_slide_down(game->console);
407         } break;
408         }
409     } break;
410     }
411
412     return level_event(game->level, event);
413 }
414
415 static int game_event_console(Game *game, const SDL_Event *event)
416 {
417     switch (event->type) {
418     case SDL_KEYDOWN:
419         switch (event->key.keysym.sym) {
420         case SDLK_ESCAPE:
421             SDL_StopTextInput();
422             game->state = GAME_STATE_RUNNING;
423             return 0;
424
425         default: {}
426         }
427
428     default: {}
429     }
430
431     return console_handle_event(game->console, event);
432 }
433
434 static int game_event_level_picker(Game *game, const SDL_Event *event)
435 {
436     trace_assert(game);
437     trace_assert(event);
438
439     switch (event->type) {
440     case SDL_KEYDOWN: {
441         switch(event->key.keysym.sym) {
442         case SDLK_n: {
443             if (game->level_editor == NULL) {
444                 game->level_editor = PUSH_LT(
445                     game->lt,
446                     create_level_editor(),
447                     destroy_level_editor);
448             } else {
449                 game->level_editor = RESET_LT(
450                     game->lt,
451                     game->level_editor,
452                     create_level_editor());
453             }
454
455             if (game->level_editor == NULL) {
456                 return -1;
457             }
458
459             if (game->level == NULL) {
460                 game->level = PUSH_LT(
461                     game->lt,
462                     create_level_from_level_editor(
463                         game->level_editor,
464                         game->broadcast),
465                     destroy_level);
466             } else {
467                 game->level = RESET_LT(
468                     game->lt,
469                     game->level,
470                     create_level_from_level_editor(
471                         game->level_editor,
472                         game->broadcast));
473             }
474
475             if (game->level == NULL) {
476                 return -1;
477             }
478
479             game->state = GAME_STATE_RUNNING;
480         } break;
481         }
482     } break;
483     }
484
485     return level_picker_event(game->level_picker, event, game->camera);
486 }
487
488 static int game_event_level_editor(Game *game, const SDL_Event *event)
489 {
490     trace_assert(game);
491     trace_assert(event);
492
493     switch (event->type) {
494     case SDL_KEYDOWN: {
495         switch (event->key.keysym.sym) {
496         case SDLK_TAB: {
497             game->level = RESET_LT(
498                 game->lt,
499                 game->level,
500                 create_level_from_level_editor(
501                     game->level_editor,
502                     game->broadcast));
503             if (game->level == NULL) {
504                 return -1;
505             }
506             game->state = GAME_STATE_RUNNING;
507         } break;
508         }
509     } break;
510     }
511
512     return level_editor_event(game->level_editor, event, game->camera);
513 }
514
515 int game_event(Game *game, const SDL_Event *event)
516 {
517     trace_assert(game);
518     trace_assert(event);
519
520     switch (event->type) {
521     case SDL_QUIT: {
522         game->state = GAME_STATE_QUIT;
523         return 0;
524     } break;
525
526     case SDL_MOUSEMOTION: {
527         game->cursor_x = event->motion.x;
528         game->cursor_y = event->motion.y;
529     } break;
530     }
531
532     switch (game->state) {
533     case GAME_STATE_RUNNING:
534         return game_event_running(game, event);
535
536     case GAME_STATE_PAUSE:
537         return game_event_pause(game, event);
538
539     case GAME_STATE_CONSOLE:
540         return game_event_console(game, event);
541
542     case GAME_STATE_LEVEL_PICKER:
543         return game_event_level_picker(game, event);
544
545     case GAME_STATE_LEVEL_EDITOR:
546         return game_event_level_editor(game, event);
547
548     case GAME_STATE_QUIT:
549         return 0;
550     }
551
552     return -1;
553 }
554
555
556 int game_input(Game *game,
557                const Uint8 *const keyboard_state,
558                SDL_Joystick *the_stick_of_joy)
559 {
560     trace_assert(game);
561     trace_assert(keyboard_state);
562
563     switch (game->state) {
564     case GAME_STATE_QUIT:
565     case GAME_STATE_PAUSE:
566     case GAME_STATE_CONSOLE:
567     case GAME_STATE_LEVEL_EDITOR:
568         return 0;
569
570     case GAME_STATE_RUNNING:
571         return level_input(game->level, keyboard_state, the_stick_of_joy);
572
573     case GAME_STATE_LEVEL_PICKER:
574         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
575     }
576
577     return 0;
578 }
579
580 int game_over_check(const Game *game)
581 {
582     return game->state == GAME_STATE_QUIT;
583 }
584
585 struct EvalResult
586 game_send(Game *game, Gc *gc, struct Scope *scope,
587           struct Expr path)
588 {
589     trace_assert(game);
590     trace_assert(gc);
591     trace_assert(scope);
592
593     const char *target = NULL;
594     struct Expr rest = void_expr();
595     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
596     if (res.is_error) {
597         return res;
598     }
599
600     if (strcmp(target, "level") == 0) {
601         return level_send(game->level, gc, scope, rest);
602     } else if (strcmp(target, "menu") == 0) {
603         level_picker_clean_selection(game->level_picker);
604         game->state = GAME_STATE_LEVEL_PICKER;
605         return eval_success(NIL(gc));
606     }
607
608     return unknown_target(gc, "game", target);
609 }
610
611 // Private Functions
612
613 static int game_render_cursor(const Game *game)
614 {
615     trace_assert(game);
616
617     SDL_Rect src = {0, 0, 32, 32};
618     SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
619     if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
620         return -1;
621     }
622
623     return 0;
624 }