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 {
25 GAME_STATE_LEVEL_PICKER,
26 GAME_STATE_LEVEL_EDITOR,
36 LevelPicker *level_picker;
37 LevelEditor *level_editor;
39 Sound_samples *sound_samples;
41 SDL_Renderer *renderer;
42 SDL_Texture *texture_cursor;
48 void game_switch_state(Game *game, Game_state state)
50 game->camera = create_camera(game->renderer, game->font);
54 Game *create_game(const char *level_folder,
55 const char *sound_sample_files[],
56 size_t sound_sample_files_count,
57 SDL_Renderer *renderer)
59 trace_assert(level_folder);
63 Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
70 game->broadcast = PUSH_LT(
72 create_broadcast(game),
74 if (game->broadcast == NULL) {
80 create_sprite_font_from_file(
81 "images/charmap-oldschool.bmp",
84 if (game->font == NULL) {
88 game->level_picker = PUSH_LT(
93 destroy_level_picker);
94 if (game->level_picker == NULL) {
98 game->sound_samples = PUSH_LT(
100 create_sound_samples(
102 sound_sample_files_count),
103 destroy_sound_samples);
104 if (game->sound_samples == NULL) {
108 game->renderer = renderer;
109 game->texture_cursor = PUSH_LT(
111 texture_from_bmp("images/cursor.bmp", renderer),
113 if (SDL_SetTextureBlendMode(
114 game->texture_cursor,
115 SDL_ComposeCustomBlendMode(
116 SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
117 SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
118 SDL_BLENDOPERATION_ADD,
120 SDL_BLENDFACTOR_ZERO,
121 SDL_BLENDOPERATION_ADD)) < 0) {
122 log_warn("SDL error: %s\n", SDL_GetError());
127 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
132 void destroy_game(Game *game)
135 RETURN_LT0(game->lt);
138 int game_render(const Game *game)
142 switch(game->state) {
143 case GAME_STATE_LEVEL: {
144 if (level_render(game->level, &game->camera) < 0) {
149 case GAME_STATE_LEVEL_PICKER: {
150 if (level_picker_render(game->level_picker, &game->camera) < 0) {
154 if (game_render_cursor(game) < 0) {
159 case GAME_STATE_LEVEL_EDITOR: {
160 if (level_editor_render(game->level_editor, &game->camera) < 0) {
164 if (game_render_cursor(game) < 0) {
169 case GAME_STATE_QUIT: break;
175 int game_sound(Game *game)
177 switch (game->state) {
178 case GAME_STATE_LEVEL:
179 return level_sound(game->level, game->sound_samples);
180 case GAME_STATE_LEVEL_PICKER:
181 case GAME_STATE_LEVEL_EDITOR:
182 case GAME_STATE_QUIT:
189 int game_update(Game *game, float delta_time)
192 trace_assert(delta_time > 0.0f);
194 switch (game->state) {
195 case GAME_STATE_LEVEL: {
196 if (level_update(game->level, delta_time) < 0) {
200 if (level_enter_camera_event(game->level, &game->camera) < 0) {
206 case GAME_STATE_LEVEL_PICKER: {
207 if (level_picker_update(game->level_picker, delta_time) < 0) {
211 if (level_picker_enter_camera_event(game->level_picker, &game->camera) < 0) {
215 const char *level_filename = level_picker_selected_level(game->level_picker);
217 if (level_filename != NULL) {
218 if (game->level_editor == NULL) {
219 game->level_editor = PUSH_LT(
221 create_level_editor_from_file(level_filename),
222 destroy_level_editor);
224 game->level_editor = RESET_LT(
227 create_level_editor_from_file(level_filename));
230 if (game->level_editor == NULL) {
234 if (game->level == NULL) {
235 game->level = PUSH_LT(
237 create_level_from_level_editor(
242 game->level = RESET_LT(
245 create_level_from_level_editor(
250 if (game->level == NULL) {
254 game_switch_state(game, GAME_STATE_LEVEL);
259 case GAME_STATE_LEVEL_EDITOR: {
260 if (level_editor_focus_camera(
262 &game->camera) < 0) {
266 level_editor_update(game->level_editor, delta_time);
269 case GAME_STATE_QUIT:
276 static int game_event_running(Game *game, const SDL_Event *event)
281 if (!SDL_IsTextInputActive()) {
282 switch (event->type) {
284 switch (event->key.keysym.sym) {
286 game->level = RESET_LT(
289 create_level_from_level_editor(
292 if (game->level == NULL) {
293 game_switch_state(game, GAME_STATE_QUIT);
297 camera_disable_debug_mode(&game->camera);
301 game_switch_state(game, GAME_STATE_LEVEL_EDITOR);
308 return level_event(game->level, event, &game->camera, game->sound_samples);
311 static int game_event_level_picker(Game *game, const SDL_Event *event)
316 switch (event->type) {
318 switch(event->key.keysym.sym) {
320 if (game->level_editor == NULL) {
321 game->level_editor = PUSH_LT(
323 create_level_editor(),
324 destroy_level_editor);
326 game->level_editor = RESET_LT(
329 create_level_editor());
332 if (game->level_editor == NULL) {
336 if (game->level == NULL) {
337 game->level = PUSH_LT(
339 create_level_from_level_editor(
344 game->level = RESET_LT(
347 create_level_from_level_editor(
352 if (game->level == NULL) {
356 game_switch_state(game, GAME_STATE_LEVEL);
362 return level_picker_event(game->level_picker, event, &game->camera);
365 static int game_event_level_editor(Game *game, const SDL_Event *event)
370 switch (event->type) {
372 switch (event->key.keysym.sym) {
374 game->level = RESET_LT(
377 create_level_from_level_editor(
380 if (game->level == NULL) {
383 game_switch_state(game, GAME_STATE_LEVEL);
389 return level_editor_event(game->level_editor, event, &game->camera);
392 int game_event(Game *game, const SDL_Event *event)
397 switch (event->type) {
399 game_switch_state(game, GAME_STATE_QUIT);
403 case SDL_MOUSEMOTION: {
404 game->cursor_x = event->motion.x;
405 game->cursor_y = event->motion.y;
409 if (event->key.keysym.sym == SDLK_q && event->key.keysym.mod & KMOD_CTRL) {
410 game_switch_state(game, GAME_STATE_QUIT);
416 switch (game->state) {
417 case GAME_STATE_LEVEL:
418 return game_event_running(game, event);
420 case GAME_STATE_LEVEL_PICKER:
421 return game_event_level_picker(game, event);
423 case GAME_STATE_LEVEL_EDITOR:
424 return game_event_level_editor(game, event);
426 case GAME_STATE_QUIT:
434 int game_input(Game *game,
435 const Uint8 *const keyboard_state,
436 SDL_Joystick *the_stick_of_joy)
439 trace_assert(keyboard_state);
441 switch (game->state) {
442 case GAME_STATE_QUIT:
443 case GAME_STATE_LEVEL_EDITOR:
446 case GAME_STATE_LEVEL:
447 return level_input(game->level, keyboard_state, the_stick_of_joy);
449 case GAME_STATE_LEVEL_PICKER:
450 return level_picker_input(game->level_picker, keyboard_state, the_stick_of_joy);
456 int game_over_check(const Game *game)
458 return game->state == GAME_STATE_QUIT;
462 game_send(Game *game, Gc *gc, struct Scope *scope,
469 const char *target = NULL;
470 struct Expr rest = void_expr();
471 struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
476 if (strcmp(target, "level") == 0) {
477 return level_send(game->level, gc, scope, rest);
478 } else if (strcmp(target, "menu") == 0) {
479 level_picker_clean_selection(game->level_picker);
480 game_switch_state(game, GAME_STATE_LEVEL_PICKER);
481 return eval_success(NIL(gc));
484 return unknown_target(gc, "game", target);
489 static int game_render_cursor(const Game *game)
493 SDL_Rect src = {0, 0, 32, 32};
494 SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
495 if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {