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/background_layer.h"
19 #include "game/level/level_editor.h"
21 static int game_render_cursor(const Game *game);
23 typedef enum Game_state {
24 GAME_STATE_RUNNING = 0,
27 GAME_STATE_LEVEL_PICKER,
28 GAME_STATE_LEVEL_EDITOR,
38 LevelPicker *level_picker;
39 LevelEditor *level_editor;
41 Sound_samples *sound_samples;
44 SDL_Renderer *renderer;
45 SDL_Texture *texture_cursor;
50 Game *create_game(const char *level_folder,
51 const char *sound_sample_files[],
52 size_t sound_sample_files_count,
53 SDL_Renderer *renderer)
55 trace_assert(level_folder);
59 Game *game = PUSH_LT(lt, nth_calloc(1, 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) {
95 game->sound_samples = PUSH_LT(
99 sound_sample_files_count),
100 destroy_sound_samples);
101 if (game->sound_samples == NULL) {
105 game->camera = PUSH_LT(
107 create_camera(renderer, game->font),
109 if (game->camera == NULL) {
113 game->console = PUSH_LT(
115 create_console(game->broadcast, game->font),
117 if (game->console == NULL) {
121 game->renderer = renderer;
122 game->texture_cursor = PUSH_LT(
124 texture_from_bmp("images/cursor.bmp", renderer),
126 if (SDL_SetTextureBlendMode(
127 game->texture_cursor,
128 SDL_ComposeCustomBlendMode(
129 SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
130 SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
131 SDL_BLENDOPERATION_ADD,
133 SDL_BLENDFACTOR_ZERO,
134 SDL_BLENDOPERATION_ADD)) < 0) {
135 log_warn("SDL error: %s\n", SDL_GetError());
143 void destroy_game(Game *game)
146 RETURN_LT0(game->lt);
149 int game_render(const Game *game)
153 switch(game->state) {
154 case GAME_STATE_RUNNING:
155 case GAME_STATE_PAUSE: {
156 if (level_render(game->level, game->camera) < 0) {
161 case GAME_STATE_CONSOLE: {
162 if (level_render(game->level, game->camera) < 0) {
166 if (console_render(game->console, game->camera, game->renderer) < 0) {
171 case GAME_STATE_LEVEL_PICKER: {
172 if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
176 if (game_render_cursor(game) < 0) {
181 case GAME_STATE_LEVEL_EDITOR: {
182 if (level_editor_render(game->level_editor, game->camera) < 0) {
186 if (game_render_cursor(game) < 0) {
191 case GAME_STATE_QUIT: break;
197 int game_sound(Game *game)
199 switch (game->state) {
200 case GAME_STATE_RUNNING:
201 case GAME_STATE_PAUSE:
202 case GAME_STATE_CONSOLE:
203 return level_sound(game->level, game->sound_samples);
204 case GAME_STATE_LEVEL_PICKER:
205 case GAME_STATE_LEVEL_EDITOR:
206 case GAME_STATE_QUIT:
213 int game_update(Game *game, float delta_time)
216 trace_assert(delta_time > 0.0f);
218 switch (game->state) {
219 case GAME_STATE_RUNNING: {
220 if (level_update(game->level, delta_time) < 0) {
224 if (level_enter_camera_event(game->level, game->camera) < 0) {
230 case GAME_STATE_CONSOLE: {
231 if (level_update(game->level, delta_time) < 0) {
235 if (level_enter_camera_event(game->level, game->camera) < 0) {
239 if (console_update(game->console, delta_time) < 0) {
244 case GAME_STATE_LEVEL_PICKER: {
245 if (level_picker_update(game->level_picker, delta_time) < 0) {
249 if (level_picker_enter_camera_event(game->level_picker, game->camera) < 0) {
253 const char *level_filename = level_picker_selected_level(game->level_picker);
255 if (level_filename != NULL) {
256 if (game->level_editor == NULL) {
257 game->level_editor = PUSH_LT(
259 create_level_editor_from_file(level_filename),
260 destroy_level_editor);
262 game->level_editor = RESET_LT(
265 create_level_editor_from_file(level_filename));
268 if (game->level_editor == NULL) {
272 if (game->level == NULL) {
273 game->level = PUSH_LT(
275 create_level_from_level_editor(
280 game->level = RESET_LT(
283 create_level_from_level_editor(
288 if (game->level == NULL) {
292 game->state = GAME_STATE_RUNNING;
297 case GAME_STATE_LEVEL_EDITOR: {
298 if (level_editor_focus_camera(
304 level_editor_update(game->level_editor, delta_time);
307 case GAME_STATE_PAUSE:
308 case GAME_STATE_QUIT:
316 static int game_event_pause(Game *game, const SDL_Event *event)
321 switch (event->type) {
323 switch (event->key.keysym.sym) {
325 game->state = GAME_STATE_RUNNING;
326 camera_toggle_blackwhite_mode(game->camera);
327 sound_samples_toggle_pause(game->sound_samples);
330 camera_toggle_debug_mode(game->camera);
331 level_toggle_debug_mode(game->level);
337 return level_event(game->level, event);
340 static int game_event_running(Game *game, const SDL_Event *event)
345 switch (event->type) {
347 switch (event->key.keysym.sym) {
349 const char *level_filename = game->level_editor->file_name;
351 if (!level_filename) {
352 log_warn("Could not reload the level. There is no associated file.\n");
356 log_info("Reloading the level from '%s'...\n", level_filename);
358 game->level_editor = RESET_LT(
361 create_level_editor_from_file(level_filename));
362 if (game->level_editor == NULL) {
363 log_fail("Could not reload level %s\n", level_filename);
364 game->state = GAME_STATE_QUIT;
368 game->level = RESET_LT(
371 create_level_from_level_editor(
374 if (game->level == NULL) {
375 log_fail("Could not reload level %s\n", level_filename);
376 game->state = GAME_STATE_QUIT;
380 camera_disable_debug_mode(game->camera);
384 game->state = GAME_STATE_PAUSE;
385 camera_toggle_blackwhite_mode(game->camera);
386 sound_samples_toggle_pause(game->sound_samples);
390 camera_toggle_debug_mode(game->camera);
391 level_toggle_debug_mode(game->level);
395 game->state = GAME_STATE_LEVEL_EDITOR;
401 switch (event->key.keysym.sym) {
404 SDL_StartTextInput();
405 game->state = GAME_STATE_CONSOLE;
406 console_slide_down(game->console);
412 return level_event(game->level, event);
415 static int game_event_console(Game *game, const SDL_Event *event)
417 switch (event->type) {
419 switch (event->key.keysym.sym) {
422 game->state = GAME_STATE_RUNNING;
431 return console_handle_event(game->console, event);
434 static int game_event_level_picker(Game *game, const SDL_Event *event)
439 switch (event->type) {
441 switch(event->key.keysym.sym) {
443 if (game->level_editor == NULL) {
444 game->level_editor = PUSH_LT(
446 create_level_editor(),
447 destroy_level_editor);
449 game->level_editor = RESET_LT(
452 create_level_editor());
455 if (game->level_editor == NULL) {
459 if (game->level == NULL) {
460 game->level = PUSH_LT(
462 create_level_from_level_editor(
467 game->level = RESET_LT(
470 create_level_from_level_editor(
475 if (game->level == NULL) {
479 game->state = GAME_STATE_RUNNING;
485 return level_picker_event(game->level_picker, event, game->camera);
488 static int game_event_level_editor(Game *game, const SDL_Event *event)
493 switch (event->type) {
495 switch (event->key.keysym.sym) {
497 game->level = RESET_LT(
500 create_level_from_level_editor(
503 if (game->level == NULL) {
506 game->state = GAME_STATE_RUNNING;
512 return level_editor_event(game->level_editor, event, game->camera);
515 int game_event(Game *game, const SDL_Event *event)
520 switch (event->type) {
522 game->state = GAME_STATE_QUIT;
526 case SDL_MOUSEMOTION: {
527 game->cursor_x = event->motion.x;
528 game->cursor_y = event->motion.y;
532 switch (game->state) {
533 case GAME_STATE_RUNNING:
534 return game_event_running(game, event);
536 case GAME_STATE_PAUSE:
537 return game_event_pause(game, event);
539 case GAME_STATE_CONSOLE:
540 return game_event_console(game, event);
542 case GAME_STATE_LEVEL_PICKER:
543 return game_event_level_picker(game, event);
545 case GAME_STATE_LEVEL_EDITOR:
546 return game_event_level_editor(game, event);
548 case GAME_STATE_QUIT:
556 int game_input(Game *game,
557 const Uint8 *const keyboard_state,
558 SDL_Joystick *the_stick_of_joy)
561 trace_assert(keyboard_state);
563 switch (game->state) {
564 case GAME_STATE_QUIT:
565 case GAME_STATE_PAUSE:
566 case GAME_STATE_CONSOLE:
567 case GAME_STATE_LEVEL_EDITOR:
570 case GAME_STATE_RUNNING:
571 return level_input(game->level, keyboard_state, the_stick_of_joy);
573 case GAME_STATE_LEVEL_PICKER:
574 return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
580 int game_over_check(const Game *game)
582 return game->state == GAME_STATE_QUIT;
586 game_send(Game *game, Gc *gc, struct Scope *scope,
593 const char *target = NULL;
594 struct Expr rest = void_expr();
595 struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
600 if (strcmp(target, "level") == 0) {
601 return level_send(game->level, gc, scope, rest);
602 } else if (strcmp(target, "menu") == 0) {
603 level_picker_clean_selection(game->level_picker);
604 game->state = GAME_STATE_LEVEL_PICKER;
605 return eval_success(NIL(gc));
608 return unknown_target(gc, "game", target);
613 static int game_render_cursor(const Game *game)
617 SDL_Rect src = {0, 0, 32, 32};
618 SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
619 if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {