]> git.lizzy.rs Git - nothing.git/blob - src/game.c
(#769) make list_selector select items with mouse
[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 typedef enum Game_state {
21     GAME_STATE_RUNNING = 0,
22     GAME_STATE_PAUSE,
23     GAME_STATE_CONSOLE,
24     GAME_STATE_LEVEL_PICKER,
25     GAME_STATE_QUIT
26 } Game_state;
27
28 typedef struct Game {
29     Lt *lt;
30
31     Game_state state;
32     Broadcast *broadcast;
33     Sprite_font *font;
34     LevelPicker *level_picker;
35     Level *level;
36     Sound_samples *sound_samples;
37     Camera *camera;
38     Console *console;
39     SDL_Renderer *renderer;
40     SDL_Texture *texture_cursor;
41     int cursor_x;
42     int cursor_y;
43 } Game;
44
45 Game *create_game(const char *level_folder,
46                   const char *sound_sample_files[],
47                   size_t sound_sample_files_count,
48                   SDL_Renderer *renderer)
49 {
50     trace_assert(level_folder);
51
52     Lt *const lt = create_lt();
53     if (lt == NULL) {
54         return NULL;
55     }
56
57     Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free);
58     if (game == NULL) {
59         RETURN_LT(lt, NULL);
60     }
61     game->lt = lt;
62
63     game->state = GAME_STATE_LEVEL_PICKER;
64
65     game->broadcast = PUSH_LT(
66         lt,
67         create_broadcast(game),
68         destroy_broadcast);
69     if (game->broadcast == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     game->font = PUSH_LT(
74         lt,
75         create_sprite_font_from_file(
76             "images/charmap-oldschool.bmp",
77             renderer),
78         destroy_sprite_font);
79     if (game->font == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     game->level_picker = PUSH_LT(
84         lt,
85         create_level_picker(
86             game->font,
87             level_folder),
88         destroy_level_picker);
89     if (game->level_picker == NULL) {
90         RETURN_LT(lt, NULL);
91     }
92
93     game->level = NULL;
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     game->cursor_x = 0;
127     game->cursor_y = 0;
128
129     return game;
130 }
131
132 void destroy_game(Game *game)
133 {
134     trace_assert(game);
135     RETURN_LT0(game->lt);
136 }
137
138 int game_render(const Game *game)
139 {
140     trace_assert(game);
141
142     switch(game->state) {
143     case GAME_STATE_RUNNING:
144     case GAME_STATE_PAUSE: {
145         if (level_render(game->level, game->camera) < 0) {
146             return -1;
147         }
148
149         SDL_Rect src = {0, 0, 32, 32};
150         SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
151         if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
152             return -1;
153         }
154     } break;
155
156     case GAME_STATE_CONSOLE: {
157         if (level_render(game->level, game->camera) < 0) {
158             return -1;
159         }
160
161         if (console_render(game->console, game->renderer) < 0) {
162             return -1;
163         }
164     } break;
165
166     case GAME_STATE_LEVEL_PICKER: {
167         if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
168             return -1;
169         }
170
171         SDL_Rect src = {0, 0, 32, 32};
172         SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
173         if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
174             return -1;
175         }
176     } break;
177
178     case GAME_STATE_QUIT: break;
179     }
180
181     return 0;
182 }
183
184 int game_sound(Game *game)
185 {
186     switch (game->state) {
187     case GAME_STATE_RUNNING:
188     case GAME_STATE_PAUSE:
189     case GAME_STATE_CONSOLE:
190         return level_sound(game->level, game->sound_samples);
191     case GAME_STATE_LEVEL_PICKER:
192     case GAME_STATE_QUIT:
193         return 0;
194     }
195
196     return 0;
197 }
198
199 int game_update(Game *game, float delta_time)
200 {
201     trace_assert(game);
202     trace_assert(delta_time > 0.0f);
203
204     switch (game->state) {
205     case GAME_STATE_RUNNING: {
206         if (level_update(game->level, delta_time) < 0) {
207             return -1;
208         }
209
210         if (level_enter_camera_event(game->level, game->camera) < 0) {
211             return -1;
212         }
213
214     } break;
215
216     case GAME_STATE_CONSOLE: {
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         if (console_update(game->console, delta_time) < 0) {
226             return -1;
227         }
228     } break;
229
230     case GAME_STATE_LEVEL_PICKER: {
231         if (level_picker_update(game->level_picker, delta_time) < 0) {
232             return -1;
233         }
234
235         if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
236             return -1;
237         }
238
239         const char *level_folder = level_picker_selected_level(game->level_picker);
240
241         if (level_folder != NULL) {
242             if (game->level == NULL) {
243                 game->level = PUSH_LT(
244                     game->lt,
245                     create_level_from_file(level_folder, game->broadcast),
246                     destroy_level);
247             } else {
248                 game->level = RESET_LT(
249                     game->lt,
250                     game->level,
251                     create_level_from_file(level_folder, game->broadcast));
252             }
253
254             if (game->level == NULL) {
255                 return -1;
256             }
257
258             game->state = GAME_STATE_RUNNING;
259         }
260
261     } break;
262
263     case GAME_STATE_PAUSE:
264     case GAME_STATE_QUIT:
265         break;
266     }
267
268     return 0;
269 }
270
271
272 static int game_event_pause(Game *game, const SDL_Event *event)
273 {
274     trace_assert(game);
275     trace_assert(event);
276
277     switch (event->type) {
278     case SDL_QUIT:
279         game->state = GAME_STATE_QUIT;
280         break;
281
282     case SDL_KEYDOWN:
283         switch (event->key.keysym.sym) {
284         case SDLK_p:
285             game->state = GAME_STATE_RUNNING;
286             camera_toggle_blackwhite_mode(game->camera);
287             sound_samples_toggle_pause(game->sound_samples);
288             break;
289         case SDLK_l:
290             camera_toggle_debug_mode(game->camera);
291             level_toggle_debug_mode(game->level);
292             break;
293         }
294         break;
295     }
296
297     return level_event(game->level, event);
298 }
299
300 static int game_event_running(Game *game, const SDL_Event *event)
301 {
302     trace_assert(game);
303     trace_assert(event);
304
305     switch (event->type) {
306     case SDL_QUIT:
307         game->state = GAME_STATE_QUIT;
308         break;
309
310     case SDL_KEYDOWN:
311         switch (event->key.keysym.sym) {
312         case SDLK_r: {
313             const char *level_filename = level_picker_selected_level(game->level_picker);
314
315             log_info("Reloading the level from '%s'...\n", level_filename);
316
317             game->level = RESET_LT(
318                 game->lt,
319                 game->level,
320                 create_level_from_file(
321                     level_filename,
322                     game->broadcast));
323
324             if (game->level == NULL) {
325                 log_fail("Could not reload level %s\n", level_filename);
326                 game->state = GAME_STATE_QUIT;
327                 return -1;
328             }
329
330             camera_disable_debug_mode(game->camera);
331         } break;
332
333         case SDLK_q:
334             if (level_reload_preserve_player(game->level, game->broadcast) < 0) {
335                 log_fail("Could not reload level\n");
336                 game->state = GAME_STATE_QUIT;
337                 return -1;
338             }
339             break;
340
341         case SDLK_p:
342             game->state = GAME_STATE_PAUSE;
343             camera_toggle_blackwhite_mode(game->camera);
344             sound_samples_toggle_pause(game->sound_samples);
345             break;
346
347         case SDLK_l:
348             camera_toggle_debug_mode(game->camera);
349             level_toggle_debug_mode(game->level);
350             break;
351         }
352         break;
353     case SDL_KEYUP:
354         switch (event->key.keysym.sym) {
355         case SDLK_BACKQUOTE:
356         case SDLK_c:
357             SDL_StartTextInput();
358             game->state = GAME_STATE_CONSOLE;
359             console_slide_down(game->console);
360             break;
361         }
362         break;
363     case SDL_MOUSEMOTION:
364         game->cursor_x = event->motion.x;
365         game->cursor_y = event->motion.y;
366         break;
367     }
368
369     return level_event(game->level, event);
370 }
371
372 static int game_event_console(Game *game, const SDL_Event *event)
373 {
374     switch (event->type) {
375     case SDL_QUIT:
376         game->state = GAME_STATE_QUIT;
377         return 0;
378
379     case SDL_KEYDOWN:
380         switch (event->key.keysym.sym) {
381         case SDLK_ESCAPE:
382             SDL_StopTextInput();
383             game->state = GAME_STATE_RUNNING;
384             return 0;
385
386         default: {}
387         }
388
389     default: {}
390     }
391
392     return console_handle_event(game->console, event);
393 }
394
395 static int game_event_level_picker(Game *game, const SDL_Event *event)
396 {
397     trace_assert(game);
398     trace_assert(event);
399
400     switch (event->type) {
401     case SDL_QUIT:
402         game->state = GAME_STATE_QUIT;
403         return 0;
404
405     case SDL_MOUSEMOTION:
406         game->cursor_x = event->motion.x;
407         game->cursor_y = event->motion.y;
408         break;
409
410     default: {}
411     }
412
413     return level_picker_event(game->level_picker, event);
414 }
415
416 int game_event(Game *game, const SDL_Event *event)
417 {
418     trace_assert(game);
419     trace_assert(event);
420
421     switch (game->state) {
422     case GAME_STATE_RUNNING:
423         return game_event_running(game, event);
424
425     case GAME_STATE_PAUSE:
426         return game_event_pause(game, event);
427
428     case GAME_STATE_CONSOLE:
429         return game_event_console(game, event);
430
431     case GAME_STATE_LEVEL_PICKER:
432         return game_event_level_picker(game, event);
433
434     default: {}
435     }
436
437     return 0;
438 }
439
440
441 int game_input(Game *game,
442                const Uint8 *const keyboard_state,
443                SDL_Joystick *the_stick_of_joy)
444 {
445     trace_assert(game);
446     trace_assert(keyboard_state);
447
448     switch (game->state) {
449     case GAME_STATE_QUIT:
450     case GAME_STATE_PAUSE:
451     case GAME_STATE_CONSOLE:
452         return 0;
453
454     case GAME_STATE_RUNNING:
455         return level_input(game->level, keyboard_state, the_stick_of_joy);
456
457     case GAME_STATE_LEVEL_PICKER:
458         return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
459     }
460
461     return 0;
462 }
463
464 int game_over_check(const Game *game)
465 {
466     return game->state == GAME_STATE_QUIT;
467 }
468
469 struct EvalResult
470 game_send(Game *game, Gc *gc, struct Scope *scope,
471           struct Expr path)
472 {
473     trace_assert(game);
474     trace_assert(gc);
475     trace_assert(scope);
476
477     const char *target = NULL;
478     struct Expr rest = void_expr();
479     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
480     if (res.is_error) {
481         return res;
482     }
483
484     if (strcmp(target, "level") == 0) {
485         return level_send(game->level, gc, scope, rest);
486     } else if (strcmp(target, "menu") == 0) {
487         level_picker_clean_selection(game->level_picker);
488         game->state = GAME_STATE_LEVEL_PICKER;
489         return eval_success(NIL(gc));
490     }
491
492     return unknown_target(gc, "game", target);
493 }