2 #include <SDL2/SDL_mixer.h>
3 #include "system/stacktrace.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"
20 static int game_render_cursor(const Game *game);
22 typedef enum Game_state {
23 GAME_STATE_RUNNING = 0,
26 GAME_STATE_LEVEL_PICKER,
36 LevelPicker *level_picker;
38 Sound_samples *sound_samples;
41 SDL_Renderer *renderer;
42 SDL_Texture *texture_cursor;
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)
52 trace_assert(level_folder);
54 Lt *const lt = create_lt();
59 Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free);
65 game->state = GAME_STATE_LEVEL_PICKER;
67 game->broadcast = PUSH_LT(
69 create_broadcast(game),
71 if (game->broadcast == NULL) {
77 create_sprite_font_from_file(
78 "images/charmap-oldschool.bmp",
81 if (game->font == NULL) {
85 game->level_picker = PUSH_LT(
90 destroy_level_picker);
91 if (game->level_picker == NULL) {
97 game->sound_samples = PUSH_LT(
101 sound_sample_files_count),
102 destroy_sound_samples);
103 if (game->sound_samples == NULL) {
107 game->camera = PUSH_LT(
109 create_camera(renderer, game->font),
111 if (game->camera == NULL) {
115 game->console = PUSH_LT(
117 create_console(game->broadcast, game->font),
119 if (game->console == NULL) {
123 game->renderer = renderer;
124 game->texture_cursor = PUSH_LT(
126 texture_from_bmp("images/cursor.bmp", renderer),
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,
135 SDL_BLENDFACTOR_ZERO,
136 SDL_BLENDOPERATION_ADD)) < 0) {
137 log_warn("SDL error: %s\n", SDL_GetError());
145 void destroy_game(Game *game)
148 RETURN_LT0(game->lt);
151 int game_render(const Game *game)
155 switch(game->state) {
156 case GAME_STATE_RUNNING:
157 case GAME_STATE_PAUSE: {
158 if (level_render(game->level, game->camera) < 0) {
162 if (game_render_cursor(game) < 0) {
167 case GAME_STATE_CONSOLE: {
168 if (level_render(game->level, game->camera) < 0) {
172 if (console_render(game->console, game->renderer) < 0) {
177 case GAME_STATE_LEVEL_PICKER: {
178 if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
182 if (game_render_cursor(game) < 0) {
187 case GAME_STATE_QUIT: break;
193 int game_sound(Game *game)
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:
208 int game_update(Game *game, float delta_time)
211 trace_assert(delta_time > 0.0f);
213 switch (game->state) {
214 case GAME_STATE_RUNNING: {
215 if (level_update(game->level, delta_time) < 0) {
219 if (level_enter_camera_event(game->level, game->camera) < 0) {
225 case GAME_STATE_CONSOLE: {
226 if (level_update(game->level, delta_time) < 0) {
230 if (level_enter_camera_event(game->level, game->camera) < 0) {
234 if (console_update(game->console, delta_time) < 0) {
239 case GAME_STATE_LEVEL_PICKER: {
240 if (level_picker_update(game->level_picker, delta_time) < 0) {
244 if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
248 const char *level_folder = level_picker_selected_level(game->level_picker);
250 if (level_folder != NULL) {
251 if (game->level == NULL) {
252 game->level = PUSH_LT(
254 create_level_from_file(level_folder, game->broadcast),
257 game->level = RESET_LT(
260 create_level_from_file(level_folder, game->broadcast));
263 if (game->level == NULL) {
267 game->state = GAME_STATE_RUNNING;
272 case GAME_STATE_PAUSE:
273 case GAME_STATE_QUIT:
281 static int game_event_pause(Game *game, const SDL_Event *event)
286 switch (event->type) {
288 game->state = GAME_STATE_QUIT;
292 switch (event->key.keysym.sym) {
294 game->state = GAME_STATE_RUNNING;
295 camera_toggle_blackwhite_mode(game->camera);
296 sound_samples_toggle_pause(game->sound_samples);
299 camera_toggle_debug_mode(game->camera);
300 level_toggle_debug_mode(game->level);
306 return level_event(game->level, event, game->camera);
309 static int game_event_running(Game *game, const SDL_Event *event)
314 switch (event->type) {
316 game->state = GAME_STATE_QUIT;
320 switch (event->key.keysym.sym) {
322 const char *level_filename = level_picker_selected_level(game->level_picker);
324 log_info("Reloading the level from '%s'...\n", level_filename);
326 game->level = RESET_LT(
329 create_level_from_file(
333 if (game->level == NULL) {
334 log_fail("Could not reload level %s\n", level_filename);
335 game->state = GAME_STATE_QUIT;
339 camera_disable_debug_mode(game->camera);
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;
351 game->state = GAME_STATE_PAUSE;
352 camera_toggle_blackwhite_mode(game->camera);
353 sound_samples_toggle_pause(game->sound_samples);
357 camera_toggle_debug_mode(game->camera);
358 level_toggle_debug_mode(game->level);
363 switch (event->key.keysym.sym) {
366 SDL_StartTextInput();
367 game->state = GAME_STATE_CONSOLE;
368 console_slide_down(game->console);
372 case SDL_MOUSEMOTION:
373 game->cursor_x = event->motion.x;
374 game->cursor_y = event->motion.y;
378 return level_event(game->level, event, game->camera);
381 static int game_event_console(Game *game, const SDL_Event *event)
383 switch (event->type) {
385 game->state = GAME_STATE_QUIT;
389 switch (event->key.keysym.sym) {
392 game->state = GAME_STATE_RUNNING;
401 return console_handle_event(game->console, event);
404 static int game_event_level_picker(Game *game, const SDL_Event *event)
409 switch (event->type) {
411 game->state = GAME_STATE_QUIT;
414 case SDL_MOUSEMOTION:
415 game->cursor_x = event->motion.x;
416 game->cursor_y = event->motion.y;
422 return level_picker_event(game->level_picker, event);
425 int game_event(Game *game, const SDL_Event *event)
430 switch (game->state) {
431 case GAME_STATE_RUNNING:
432 return game_event_running(game, event);
434 case GAME_STATE_PAUSE:
435 return game_event_pause(game, event);
437 case GAME_STATE_CONSOLE:
438 return game_event_console(game, event);
440 case GAME_STATE_LEVEL_PICKER:
441 return game_event_level_picker(game, event);
450 int game_input(Game *game,
451 const Uint8 *const keyboard_state,
452 SDL_Joystick *the_stick_of_joy)
455 trace_assert(keyboard_state);
457 switch (game->state) {
458 case GAME_STATE_QUIT:
459 case GAME_STATE_PAUSE:
460 case GAME_STATE_CONSOLE:
463 case GAME_STATE_RUNNING:
464 return level_input(game->level, keyboard_state, the_stick_of_joy);
466 case GAME_STATE_LEVEL_PICKER:
467 return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
473 int game_over_check(const Game *game)
475 return game->state == GAME_STATE_QUIT;
479 game_send(Game *game, Gc *gc, struct Scope *scope,
486 const char *target = NULL;
487 struct Expr rest = void_expr();
488 struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
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));
501 return unknown_target(gc, "game", target);
506 static int game_render_cursor(const Game *game)
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) {