-#include <SDL2/SDL.h>
-#include <SDL2/SDL_mixer.h>
+#include <SDL.h>
#include "system/stacktrace.h"
#include <stdio.h>
#include "system/str.h"
#include "ebisp/builtins.h"
#include "broadcast.h"
+#include "sdl/texture.h"
+#include "game/level/level_editor.h"
+
+static int game_render_cursor(const Game *game);
typedef enum Game_state {
GAME_STATE_RUNNING = 0,
GAME_STATE_PAUSE,
GAME_STATE_CONSOLE,
GAME_STATE_LEVEL_PICKER,
+ GAME_STATE_LEVEL_EDITOR,
GAME_STATE_QUIT
} Game_state;
Broadcast *broadcast;
Sprite_font *font;
LevelPicker *level_picker;
+ LevelEditor *level_editor;
Level *level;
- char *level_file_path;
Sound_samples *sound_samples;
Camera *camera;
Console *console;
SDL_Renderer *renderer;
+ SDL_Texture *texture_cursor;
+ int cursor_x;
+ int cursor_y;
} Game;
-Game *create_game(const char *level_file_path,
+Game *create_game(const char *level_folder,
const char *sound_sample_files[],
size_t sound_sample_files_count,
SDL_Renderer *renderer)
{
- trace_assert(level_file_path);
+ trace_assert(level_folder);
- Lt *const lt = create_lt();
- if (lt == NULL) {
- return NULL;
- }
+ Lt *lt = create_lt();
- Game *game = PUSH_LT(lt, nth_alloc(sizeof(Game)), free);
+ Game *game = PUSH_LT(lt, nth_calloc(1, sizeof(Game)), free);
if (game == NULL) {
RETURN_LT(lt, NULL);
}
game->font = PUSH_LT(
lt,
create_sprite_font_from_file(
- "fonts/charmap-oldschool.bmp",
+ "images/charmap-oldschool.bmp",
renderer),
destroy_sprite_font);
if (game->font == NULL) {
lt,
create_level_picker(
game->font,
- level_file_path),
+ level_folder),
destroy_level_picker);
if (game->level_picker == NULL) {
RETURN_LT(lt, NULL);
}
- game->level = NULL;
-
- game->level_file_path = PUSH_LT(
- lt,
- string_duplicate(level_file_path, NULL),
- free);
- if (game->level_file_path == NULL) {
- RETURN_LT(lt, NULL);
- }
-
game->sound_samples = PUSH_LT(
lt,
create_sound_samples(
}
game->renderer = renderer;
+ game->texture_cursor = PUSH_LT(
+ lt,
+ texture_from_bmp("images/cursor.bmp", renderer),
+ SDL_DestroyTexture);
+ if (SDL_SetTextureBlendMode(
+ game->texture_cursor,
+ SDL_ComposeCustomBlendMode(
+ SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR,
+ SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
+ SDL_BLENDOPERATION_ADD,
+ SDL_BLENDFACTOR_ONE,
+ SDL_BLENDFACTOR_ZERO,
+ SDL_BLENDOPERATION_ADD)) < 0) {
+ log_warn("SDL error: %s\n", SDL_GetError());
+ }
+ game->cursor_x = 0;
+ game->cursor_y = 0;
return game;
}
return -1;
}
- if (console_render(game->console, game->renderer) < 0) {
+ if (console_render(game->console, game->camera, game->renderer) < 0) {
return -1;
}
} break;
if (level_picker_render(game->level_picker, game->camera, game->renderer) < 0) {
return -1;
}
+
+ if (game_render_cursor(game) < 0) {
+ return -1;
+ }
+ } break;
+
+ case GAME_STATE_LEVEL_EDITOR: {
+ if (level_editor_render(game->level_editor, game->camera) < 0) {
+ return -1;
+ }
+
+ if (game_render_cursor(game) < 0) {
+ return -1;
+ }
} break;
case GAME_STATE_QUIT: break;
case GAME_STATE_CONSOLE:
return level_sound(game->level, game->sound_samples);
case GAME_STATE_LEVEL_PICKER:
+ case GAME_STATE_LEVEL_EDITOR:
case GAME_STATE_QUIT:
return 0;
}
return -1;
}
- // TODO: There is no way to go back to LevelPicker when the level is loaded
- const char *level_file_path = level_picker_selected_level(game->level_picker);
+ const char *level_filename = level_picker_selected_level(game->level_picker);
+
+ if (level_filename != NULL) {
+ if (game->level_editor == NULL) {
+ game->level_editor = PUSH_LT(
+ game->lt,
+ create_level_editor_from_file(level_filename),
+ destroy_level_editor);
+ } else {
+ game->level_editor = RESET_LT(
+ game->lt,
+ game->level_editor,
+ create_level_editor_from_file(level_filename));
+ }
- trace_assert(game->level == NULL);
+ if (game->level_editor == NULL) {
+ return -1;
+ }
+
+ if (game->level == NULL) {
+ game->level = PUSH_LT(
+ game->lt,
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast),
+ destroy_level);
+ } else {
+ game->level = RESET_LT(
+ game->lt,
+ game->level,
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast));
+ }
- if (level_file_path != NULL) {
- game->level = PUSH_LT(
- game->lt,
- create_level_from_file(level_file_path, game->broadcast),
- destroy_level);
if (game->level == NULL) {
return -1;
}
game->state = GAME_STATE_RUNNING;
}
+
+ } break;
+
+ case GAME_STATE_LEVEL_EDITOR: {
+ if (level_editor_focus_camera(
+ game->level_editor,
+ game->camera) < 0) {
+ return -1;
+ }
} break;
case GAME_STATE_PAUSE:
trace_assert(event);
switch (event->type) {
- case SDL_QUIT:
- game->state = GAME_STATE_QUIT;
- break;
-
case SDL_KEYDOWN:
switch (event->key.keysym.sym) {
case SDLK_p:
trace_assert(event);
switch (event->type) {
- case SDL_QUIT:
- game->state = GAME_STATE_QUIT;
- break;
-
- case SDL_KEYDOWN:
+ case SDL_KEYDOWN: {
switch (event->key.keysym.sym) {
- case SDLK_r:
- log_info("Reloading the level from '%s'...\n", game->level_file_path);
+ case SDLK_r: {
+ const char *level_filename = level_picker_selected_level(game->level_picker);
+
+ log_info("Reloading the level from '%s'...\n", level_filename);
+
+ game->level_editor = RESET_LT(
+ game->lt,
+ game->level_editor,
+ create_level_editor_from_file(level_filename));
+ if (game->level_editor == NULL) {
+ log_fail("Could not reload level %s\n", level_filename);
+ game->state = GAME_STATE_QUIT;
+ return -1;
+ }
game->level = RESET_LT(
game->lt,
game->level,
- create_level_from_file(
- game->level_file_path, game->broadcast));
-
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast));
if (game->level == NULL) {
- log_fail("Could not reload level %s\n", game->level_file_path);
+ log_fail("Could not reload level %s\n", level_filename);
game->state = GAME_STATE_QUIT;
return -1;
}
camera_disable_debug_mode(game->camera);
+ } break;
- break;
-
- case SDLK_q:
- log_info("Reloading the level's platforms from '%s'...\n", game->level_file_path);
- if (level_reload_preserve_player(game->level, game->level_file_path, game->broadcast) < 0) {
- log_fail("Could not reload level %s\n", game->level_file_path);
- game->state = GAME_STATE_QUIT;
- return -1;
- }
- break;
-
- case SDLK_p:
+ case SDLK_p: {
game->state = GAME_STATE_PAUSE;
camera_toggle_blackwhite_mode(game->camera);
sound_samples_toggle_pause(game->sound_samples);
- break;
+ } break;
- case SDLK_l:
+ case SDLK_l: {
camera_toggle_debug_mode(game->camera);
level_toggle_debug_mode(game->level);
- break;
+ } break;
+
+ case SDLK_TAB: {
+ game->state = GAME_STATE_LEVEL_EDITOR;
+ SDL_SetRelativeMouseMode(true);
+ } break;
}
- break;
- case SDL_KEYUP:
+ } break;
+
+ case SDL_KEYUP: {
switch (event->key.keysym.sym) {
case SDLK_BACKQUOTE:
- case SDLK_c:
+ case SDLK_c: {
SDL_StartTextInput();
game->state = GAME_STATE_CONSOLE;
console_slide_down(game->console);
- break;
+ } break;
}
- break;
-
+ } break;
}
return level_event(game->level, event);
static int game_event_console(Game *game, const SDL_Event *event)
{
switch (event->type) {
- case SDL_QUIT:
- game->state = GAME_STATE_QUIT;
- return 0;
-
case SDL_KEYDOWN:
switch (event->key.keysym.sym) {
case SDLK_ESCAPE:
trace_assert(event);
switch (event->type) {
- case SDL_QUIT:
- game->state = GAME_STATE_QUIT;
- return 0;
+ case SDL_KEYDOWN: {
+ switch(event->key.keysym.sym) {
+ case SDLK_n: {
+ if (game->level_editor == NULL) {
+ game->level_editor = PUSH_LT(
+ game->lt,
+ create_level_editor(),
+ destroy_level_editor);
+ } else {
+ game->level_editor = RESET_LT(
+ game->lt,
+ game->level_editor,
+ create_level_editor());
+ }
+
+ if (game->level_editor == NULL) {
+ return -1;
+ }
+
+ if (game->level == NULL) {
+ game->level = PUSH_LT(
+ game->lt,
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast),
+ destroy_level);
+ } else {
+ game->level = RESET_LT(
+ game->lt,
+ game->level,
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast));
+ }
- default:
- return level_picker_event(game->level_picker, event);
+ if (game->level == NULL) {
+ return -1;
+ }
+
+ game->state = GAME_STATE_RUNNING;
+ } break;
+ }
+ } break;
+ }
+
+ return level_picker_event(game->level_picker, event);
+}
+
+static int game_event_level_editor(Game *game, const SDL_Event *event)
+{
+ trace_assert(game);
+ trace_assert(event);
+
+ switch (event->type) {
+ case SDL_KEYDOWN: {
+ switch (event->key.keysym.sym) {
+ case SDLK_TAB: {
+ game->level = RESET_LT(
+ game->lt,
+ game->level,
+ create_level_from_level_editor(
+ game->level_editor,
+ game->broadcast));
+ if (game->level == NULL) {
+ return -1;
+ }
+ game->state = GAME_STATE_RUNNING;
+ SDL_SetRelativeMouseMode(false);
+ } break;
+ }
+ } break;
}
+
+ return level_editor_event(game->level_editor, event, game->camera);
}
int game_event(Game *game, const SDL_Event *event)
trace_assert(game);
trace_assert(event);
+ switch (event->type) {
+ case SDL_QUIT: {
+ game->state = GAME_STATE_QUIT;
+ return 0;
+ } break;
+
+ case SDL_MOUSEMOTION: {
+ game->cursor_x = event->motion.x;
+ game->cursor_y = event->motion.y;
+ } break;
+ }
+
switch (game->state) {
case GAME_STATE_RUNNING:
return game_event_running(game, event);
case GAME_STATE_LEVEL_PICKER:
return game_event_level_picker(game, event);
- default: {}
+ case GAME_STATE_LEVEL_EDITOR:
+ return game_event_level_editor(game, event);
+
+ case GAME_STATE_QUIT:
+ return 0;
}
- return 0;
+ return -1;
}
case GAME_STATE_QUIT:
case GAME_STATE_PAUSE:
case GAME_STATE_CONSOLE:
+ case GAME_STATE_LEVEL_EDITOR:
return 0;
case GAME_STATE_RUNNING:
if (strcmp(target, "level") == 0) {
return level_send(game->level, gc, scope, rest);
+ } else if (strcmp(target, "menu") == 0) {
+ level_picker_clean_selection(game->level_picker);
+ game->state = GAME_STATE_LEVEL_PICKER;
+ return eval_success(NIL(gc));
}
return unknown_target(gc, "game", target);
}
+
+// Private Functions
+
+static int game_render_cursor(const Game *game)
+{
+ trace_assert(game);
+
+ SDL_Rect src = {0, 0, 32, 32};
+ SDL_Rect dest = {game->cursor_x, game->cursor_y, 32, 32};
+ if (SDL_RenderCopy(game->renderer, game->texture_cursor, &src, &dest) < 0) {
+ return -1;
+ }
+
+ return 0;
+}