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