2 #include "system/stacktrace.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"
20 static int game_render_cursor(const Game *game);
22 typedef enum Game_state {
23 GAME_STATE_RUNNING = 0,
26 GAME_STATE_LEVEL_PICKER,
27 GAME_STATE_LEVEL_EDITOR,
37 LevelPicker *level_picker;
38 LevelEditor *level_editor;
40 Sound_samples *sound_samples;
43 SDL_Renderer *renderer;
44 SDL_Texture *texture_cursor;
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)
54 trace_assert(level_folder);
58 Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
64 game->state = GAME_STATE_LEVEL_PICKER;
66 game->broadcast = PUSH_LT(
68 create_broadcast(game),
70 if (game->broadcast == NULL) {
76 create_sprite_font_from_file(
77 "images/charmap-oldschool.bmp",
80 if (game->font == NULL) {
84 game->level_picker = PUSH_LT(
89 destroy_level_picker);
90 if (game->level_picker == NULL) {
94 game->sound_samples = PUSH_LT(
98 sound_sample_files_count),
99 destroy_sound_samples);
100 if (game->sound_samples == NULL) {
104 game->camera = PUSH_LT(
106 create_camera(renderer, game->font),
108 if (game->camera == NULL) {
112 game->console = PUSH_LT(
114 create_console(game->broadcast, game->font),
116 if (game->console == NULL) {
120 game->renderer = renderer;
121 game->texture_cursor = PUSH_LT(
123 texture_from_bmp("images/cursor.bmp", renderer),
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,
132 SDL_BLENDFACTOR_ZERO,
133 SDL_BLENDOPERATION_ADD)) < 0) {
134 log_warn("SDL error: %s\n", SDL_GetError());
142 void destroy_game(Game *game)
145 RETURN_LT0(game->lt);
148 int game_render(const Game *game)
152 switch(game->state) {
153 case GAME_STATE_RUNNING:
154 case GAME_STATE_PAUSE: {
155 if (level_render(game->level, game->camera) < 0) {
160 case GAME_STATE_CONSOLE: {
161 if (level_render(game->level, game->camera) < 0) {
165 if (console_render(game->console, game->camera, game->renderer) < 0) {
170 case GAME_STATE_LEVEL_PICKER: {
171 if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
175 if (game_render_cursor(game) < 0) {
180 case GAME_STATE_LEVEL_EDITOR: {
181 if (level_editor_render(game->level_editor, game->camera) < 0) {
185 if (game_render_cursor(game) < 0) {
190 case GAME_STATE_QUIT: break;
196 int game_sound(Game *game)
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:
212 int game_update(Game *game, float delta_time)
215 trace_assert(delta_time > 0.0f);
217 switch (game->state) {
218 case GAME_STATE_RUNNING: {
219 if (level_update(game->level, delta_time) < 0) {
223 if (level_enter_camera_event(game->level, game->camera) < 0) {
229 case GAME_STATE_CONSOLE: {
230 if (level_update(game->level, delta_time) < 0) {
234 if (level_enter_camera_event(game->level, game->camera) < 0) {
238 if (console_update(game->console, delta_time) < 0) {
243 case GAME_STATE_LEVEL_PICKER: {
244 if (level_picker_update(game->level_picker, delta_time) < 0) {
248 if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
252 const char *level_filename = level_picker_selected_level(game->level_picker);
254 if (level_filename != NULL) {
255 if (game->level_editor == NULL) {
256 game->level_editor = PUSH_LT(
258 create_level_editor_from_file(level_filename),
259 destroy_level_editor);
261 game->level_editor = RESET_LT(
264 create_level_editor_from_file(level_filename));
267 if (game->level_editor == NULL) {
271 if (game->level == NULL) {
272 game->level = PUSH_LT(
274 create_level_from_level_editor(
279 game->level = RESET_LT(
282 create_level_from_level_editor(
287 if (game->level == NULL) {
291 game->state = GAME_STATE_RUNNING;
296 case GAME_STATE_LEVEL_EDITOR: {
297 if (level_editor_focus_camera(
304 case GAME_STATE_PAUSE:
305 case GAME_STATE_QUIT:
313 static int game_event_pause(Game *game, const SDL_Event *event)
318 switch (event->type) {
320 switch (event->key.keysym.sym) {
322 game->state = GAME_STATE_RUNNING;
323 camera_toggle_blackwhite_mode(game->camera);
324 sound_samples_toggle_pause(game->sound_samples);
327 camera_toggle_debug_mode(game->camera);
328 level_toggle_debug_mode(game->level);
334 return level_event(game->level, event);
337 static int game_event_running(Game *game, const SDL_Event *event)
342 switch (event->type) {
344 switch (event->key.keysym.sym) {
346 const char *level_filename = level_picker_selected_level(game->level_picker);
348 log_info("Reloading the level from '%s'...\n", level_filename);
350 game->level_editor = RESET_LT(
353 create_level_editor_from_file(level_filename));
354 if (game->level_editor == NULL) {
355 log_fail("Could not reload level %s\n", level_filename);
356 game->state = GAME_STATE_QUIT;
360 game->level = RESET_LT(
363 create_level_from_level_editor(
366 if (game->level == NULL) {
367 log_fail("Could not reload level %s\n", level_filename);
368 game->state = GAME_STATE_QUIT;
372 camera_disable_debug_mode(game->camera);
376 game->state = GAME_STATE_PAUSE;
377 camera_toggle_blackwhite_mode(game->camera);
378 sound_samples_toggle_pause(game->sound_samples);
382 camera_toggle_debug_mode(game->camera);
383 level_toggle_debug_mode(game->level);
387 game->state = GAME_STATE_LEVEL_EDITOR;
388 SDL_SetRelativeMouseMode(true);
394 switch (event->key.keysym.sym) {
397 SDL_StartTextInput();
398 game->state = GAME_STATE_CONSOLE;
399 console_slide_down(game->console);
405 return level_event(game->level, event);
408 static int game_event_console(Game *game, const SDL_Event *event)
410 switch (event->type) {
412 switch (event->key.keysym.sym) {
415 game->state = GAME_STATE_RUNNING;
424 return console_handle_event(game->console, event);
427 static int game_event_level_picker(Game *game, const SDL_Event *event)
432 return level_picker_event(game->level_picker, event);
435 static int game_event_level_editor(Game *game, const SDL_Event *event)
440 switch (event->type) {
442 switch (event->key.keysym.sym) {
444 game->level = RESET_LT(
447 create_level_from_level_editor(
450 if (game->level == NULL) {
453 game->state = GAME_STATE_RUNNING;
454 SDL_SetRelativeMouseMode(false);
458 /* TODO: There is no indication that the level is saved when you press S in Level Editor */
459 /* TODO: Game in LevelEditor mode does not check that the saved level file is modified by external program */
460 const char *level_filename = level_picker_selected_level(game->level_picker);
461 if (level_editor_dump(game->level_editor, level_filename) < 0) {
469 return level_editor_event(game->level_editor, event, game->camera);
472 int game_event(Game *game, const SDL_Event *event)
477 switch (event->type) {
479 game->state = GAME_STATE_QUIT;
483 case SDL_MOUSEMOTION: {
484 game->cursor_x = event->motion.x;
485 game->cursor_y = event->motion.y;
489 switch (game->state) {
490 case GAME_STATE_RUNNING:
491 return game_event_running(game, event);
493 case GAME_STATE_PAUSE:
494 return game_event_pause(game, event);
496 case GAME_STATE_CONSOLE:
497 return game_event_console(game, event);
499 case GAME_STATE_LEVEL_PICKER:
500 return game_event_level_picker(game, event);
502 case GAME_STATE_LEVEL_EDITOR:
503 return game_event_level_editor(game, event);
505 case GAME_STATE_QUIT:
513 int game_input(Game *game,
514 const Uint8 *const keyboard_state,
515 SDL_Joystick *the_stick_of_joy)
518 trace_assert(keyboard_state);
520 switch (game->state) {
521 case GAME_STATE_QUIT:
522 case GAME_STATE_PAUSE:
523 case GAME_STATE_CONSOLE:
524 case GAME_STATE_LEVEL_EDITOR:
527 case GAME_STATE_RUNNING:
528 return level_input(game->level, keyboard_state, the_stick_of_joy);
530 case GAME_STATE_LEVEL_PICKER:
531 return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
537 int game_over_check(const Game *game)
539 return game->state == GAME_STATE_QUIT;
543 game_send(Game *game, Gc *gc, struct Scope *scope,
550 const char *target = NULL;
551 struct Expr rest = void_expr();
552 struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
557 if (strcmp(target, "level") == 0) {
558 return level_send(game->level, gc, scope, rest);
559 } else if (strcmp(target, "menu") == 0) {
560 level_picker_clean_selection(game->level_picker);
561 game->state = GAME_STATE_LEVEL_PICKER;
562 return eval_success(NIL(gc));
565 return unknown_target(gc, "game", target);
570 static int game_render_cursor(const Game *game)
574 SDL_Rect src = {0, 0, 32, 32};
575 SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
576 if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {