]> git.lizzy.rs Git - nothing.git/blob - src/game.c
(#892) Implement label relocation in label layer
[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 = game->level_editor->file_name;
347
348             if (!level_filename) {
349                 log_warn("Could not reload the level. There is no associated file.\n");
350                 return 0;
351             }
352
353             log_info("Reloading the level from '%s'...\n", level_filename);
354
355             game->level_editor = RESET_LT(
356                 game->lt,
357                 game->level_editor,
358                 create_level_editor_from_file(level_filename));
359             if (game->level_editor == NULL) {
360                 log_fail("Could not reload level %s\n", level_filename);
361                 game->state = GAME_STATE_QUIT;
362                 return -1;
363             }
364
365             game->level = RESET_LT(
366                 game->lt,
367                 game->level,
368                 create_level_from_level_editor(
369                     game->level_editor,
370                     game->broadcast));
371             if (game->level == NULL) {
372                 log_fail("Could not reload level %s\n", level_filename);
373                 game->state = GAME_STATE_QUIT;
374                 return -1;
375             }
376
377             camera_disable_debug_mode(game->camera);
378         } break;
379
380         case SDLK_p: {
381             game->state = GAME_STATE_PAUSE;
382             camera_toggle_blackwhite_mode(game->camera);
383             sound_samples_toggle_pause(game->sound_samples);
384         } break;
385
386         case SDLK_l: {
387             camera_toggle_debug_mode(game->camera);
388             level_toggle_debug_mode(game->level);
389         } break;
390
391         case SDLK_TAB: {
392             game->state = GAME_STATE_LEVEL_EDITOR;
393         } break;
394         }
395     } break;
396
397     case SDL_KEYUP: {
398         switch (event->key.keysym.sym) {
399         case SDLK_BACKQUOTE:
400         case SDLK_c: {
401             SDL_StartTextInput();
402             game->state = GAME_STATE_CONSOLE;
403             console_slide_down(game->console);
404         } break;
405         }
406     } break;
407     }
408
409     return level_event(game->level, event);
410 }
411
412 static int game_event_console(Game *game, const SDL_Event *event)
413 {
414     switch (event->type) {
415     case SDL_KEYDOWN:
416         switch (event->key.keysym.sym) {
417         case SDLK_ESCAPE:
418             SDL_StopTextInput();
419             game->state = GAME_STATE_RUNNING;
420             return 0;
421
422         default: {}
423         }
424
425     default: {}
426     }
427
428     return console_handle_event(game->console, event);
429 }
430
431 static int game_event_level_picker(Game *game, const SDL_Event *event)
432 {
433     trace_assert(game);
434     trace_assert(event);
435
436     switch (event->type) {
437     case SDL_KEYDOWN: {
438         switch(event->key.keysym.sym) {
439         case SDLK_n: {
440             if (game->level_editor == NULL) {
441                 game->level_editor = PUSH_LT(
442                     game->lt,
443                     create_level_editor(),
444                     destroy_level_editor);
445             } else {
446                 game->level_editor = RESET_LT(
447                     game->lt,
448                     game->level_editor,
449                     create_level_editor());
450             }
451
452             if (game->level_editor == NULL) {
453                 return -1;
454             }
455
456             if (game->level == NULL) {
457                 game->level = PUSH_LT(
458                     game->lt,
459                     create_level_from_level_editor(
460                         game->level_editor,
461                         game->broadcast),
462                     destroy_level);
463             } else {
464                 game->level = RESET_LT(
465                     game->lt,
466                     game->level,
467                     create_level_from_level_editor(
468                         game->level_editor,
469                         game->broadcast));
470             }
471
472             if (game->level == NULL) {
473                 return -1;
474             }
475
476             game->state = GAME_STATE_RUNNING;
477         } break;
478         }
479     } break;
480     }
481
482     return level_picker_event(game->level_picker, event);
483 }
484
485 static int game_event_level_editor(Game *game, const SDL_Event *event)
486 {
487     trace_assert(game);
488     trace_assert(event);
489
490     switch (event->type) {
491     case SDL_KEYDOWN: {
492         switch (event->key.keysym.sym) {
493         case SDLK_TAB: {
494             game->level = RESET_LT(
495                 game->lt,
496                 game->level,
497                 create_level_from_level_editor(
498                     game->level_editor,
499                     game->broadcast));
500             if (game->level == NULL) {
501                 return -1;
502             }
503             game->state = GAME_STATE_RUNNING;
504         } break;
505         }
506     } break;
507     }
508
509     return level_editor_event(game->level_editor, event, game->camera);
510 }
511
512 int game_event(Game *game, const SDL_Event *event)
513 {
514     trace_assert(game);
515     trace_assert(event);
516
517     switch (event->type) {
518     case SDL_QUIT: {
519         game->state = GAME_STATE_QUIT;
520         return 0;
521     } break;
522
523     case SDL_MOUSEMOTION: {
524         game->cursor_x = event->motion.x;
525         game->cursor_y = event->motion.y;
526     } break;
527     }
528
529     switch (game->state) {
530     case GAME_STATE_RUNNING:
531         return game_event_running(game, event);
532
533     case GAME_STATE_PAUSE:
534         return game_event_pause(game, event);
535
536     case GAME_STATE_CONSOLE:
537         return game_event_console(game, event);
538
539     case GAME_STATE_LEVEL_PICKER:
540         return game_event_level_picker(game, event);
541
542     case GAME_STATE_LEVEL_EDITOR:
543         return game_event_level_editor(game, event);
544
545     case GAME_STATE_QUIT:
546         return 0;
547     }
548
549     return -1;
550 }
551
552
553 int game_input(Game *game,
554                const Uint8 *const keyboard_state,
555                SDL_Joystick *the_stick_of_joy)
556 {
557     trace_assert(game);
558     trace_assert(keyboard_state);
559
560     switch (game->state) {
561     case GAME_STATE_QUIT:
562     case GAME_STATE_PAUSE:
563     case GAME_STATE_CONSOLE:
564     case GAME_STATE_LEVEL_EDITOR:
565         return 0;
566
567     case GAME_STATE_RUNNING:
568         return level_input(game->level, keyboard_state, the_stick_of_joy);
569
570     case GAME_STATE_LEVEL_PICKER:
571         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
572     }
573
574     return 0;
575 }
576
577 int game_over_check(const Game *game)
578 {
579     return game->state == GAME_STATE_QUIT;
580 }
581
582 struct EvalResult
583 game_send(Game *game, Gc *gc, struct Scope *scope,
584           struct Expr path)
585 {
586     trace_assert(game);
587     trace_assert(gc);
588     trace_assert(scope);
589
590     const char *target = NULL;
591     struct Expr rest = void_expr();
592     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
593     if (res.is_error) {
594         return res;
595     }
596
597     if (strcmp(target, "level") == 0) {
598         return level_send(game->level, gc, scope, rest);
599     } else if (strcmp(target, "menu") == 0) {
600         level_picker_clean_selection(game->level_picker);
601         game->state = GAME_STATE_LEVEL_PICKER;
602         return eval_success(NIL(gc));
603     }
604
605     return unknown_target(gc, "game", target);
606 }
607
608 // Private Functions
609
610 static int game_render_cursor(const Game *game)
611 {
612     trace_assert(game);
613
614     SDL_Rect src = {0, 0, 32, 32};
615     SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
616     if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
617         return -1;
618     }
619
620     return 0;
621 }