]> git.lizzy.rs Git - nothing.git/blob - src/game.c
Merge pull request #777 from tsoding/278
[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 (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         break;
362     case SDL_KEYUP:
363         switch (event->key.keysym.sym) {
364         case SDLK_BACKQUOTE:
365         case SDLK_c:
366             SDL_StartTextInput();
367             game->state = GAME_STATE_CONSOLE;
368             console_slide_down(game->console);
369             break;
370         }
371         break;
372     case SDL_MOUSEMOTION:
373         game->cursor_x = event->motion.x;
374         game->cursor_y = event->motion.y;
375         break;
376     }
377
378     return level_event(game->level, event, game->camera);
379 }
380
381 static int game_event_console(Game *game, const SDL_Event *event)
382 {
383     switch (event->type) {
384     case SDL_QUIT:
385         game->state = GAME_STATE_QUIT;
386         return 0;
387
388     case SDL_KEYDOWN:
389         switch (event->key.keysym.sym) {
390         case SDLK_ESCAPE:
391             SDL_StopTextInput();
392             game->state = GAME_STATE_RUNNING;
393             return 0;
394
395         default: {}
396         }
397
398     default: {}
399     }
400
401     return console_handle_event(game->console, event);
402 }
403
404 static int game_event_level_picker(Game *game, const SDL_Event *event)
405 {
406     trace_assert(game);
407     trace_assert(event);
408
409     switch (event->type) {
410     case SDL_QUIT:
411         game->state = GAME_STATE_QUIT;
412         return 0;
413
414     case SDL_MOUSEMOTION:
415         game->cursor_x = event->motion.x;
416         game->cursor_y = event->motion.y;
417         break;
418
419     default: {}
420     }
421
422     return level_picker_event(game->level_picker, event);
423 }
424
425 int game_event(Game *game, const SDL_Event *event)
426 {
427     trace_assert(game);
428     trace_assert(event);
429
430     switch (game->state) {
431     case GAME_STATE_RUNNING:
432         return game_event_running(game, event);
433
434     case GAME_STATE_PAUSE:
435         return game_event_pause(game, event);
436
437     case GAME_STATE_CONSOLE:
438         return game_event_console(game, event);
439
440     case GAME_STATE_LEVEL_PICKER:
441         return game_event_level_picker(game, event);
442
443     default: {}
444     }
445
446     return 0;
447 }
448
449
450 int game_input(Game *game,
451                const Uint8 *const keyboard_state,
452                SDL_Joystick *the_stick_of_joy)
453 {
454     trace_assert(game);
455     trace_assert(keyboard_state);
456
457     switch (game->state) {
458     case GAME_STATE_QUIT:
459     case GAME_STATE_PAUSE:
460     case GAME_STATE_CONSOLE:
461         return 0;
462
463     case GAME_STATE_RUNNING:
464         return level_input(game->level, keyboard_state, the_stick_of_joy);
465
466     case GAME_STATE_LEVEL_PICKER:
467         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
468     }
469
470     return 0;
471 }
472
473 int game_over_check(const Game *game)
474 {
475     return game->state == GAME_STATE_QUIT;
476 }
477
478 struct EvalResult
479 game_send(Game *game, Gc *gc, struct Scope *scope,
480           struct Expr path)
481 {
482     trace_assert(game);
483     trace_assert(gc);
484     trace_assert(scope);
485
486     const char *target = NULL;
487     struct Expr rest = void_expr();
488     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
489     if (res.is_error) {
490         return res;
491     }
492
493     if (strcmp(target, "level") == 0) {
494         return level_send(game->level, gc, scope, rest);
495     } else if (strcmp(target, "menu") == 0) {
496         level_picker_clean_selection(game->level_picker);
497         game->state = GAME_STATE_LEVEL_PICKER;
498         return eval_success(NIL(gc));
499     }
500
501     return unknown_target(gc, "game", target);
502 }
503
504 // Private Functions
505
506 static int game_render_cursor(const Game *game)
507 {
508     trace_assert(game);
509
510     SDL_Rect src = {0, 0, 32, 32};
511     SDL_Rect dest = {game->cursor_x, game->cursor_y, 128, 128};
512     if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
513         return -1;
514     }
515
516     return 0;
517 }