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