From ff82f59fdf0810c785d7132eeb578903d85a33ae Mon Sep 17 00:00:00 2001 From: Sven Balzer <4653051+Kyuusokuna@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:20:04 +0200 Subject: [PATCH] add world list editor change default tree ti tree_standard_tile_angle.png --- .../decorations/tree_standard_tile_angle.png | Bin 1837 -> 1311 bytes assets/maps/worlds.h | 16 ++ src/main.cpp | 246 ++++++++++++------ 3 files changed, 184 insertions(+), 78 deletions(-) create mode 100644 assets/maps/worlds.h diff --git a/assets/decorations/tree_standard_tile_angle.png b/assets/decorations/tree_standard_tile_angle.png index 77aa12f0869b3b3401022310fd96bd8073ce56db..0095a404f7a8ebdbae2dfee3043eb9d5067bb655 100644 GIT binary patch literal 1311 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 K@f_!i8Giv@EGS|C diff --git a/assets/maps/worlds.h b/assets/maps/worlds.h new file mode 100644 index 0000000..80804cd --- /dev/null +++ b/assets/maps/worlds.h @@ -0,0 +1,16 @@ +#ifndef WORLDS_H +#define WORLDS_H + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +static const char *world_names[] = { + "map", +}; + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // WORLDS_H diff --git a/src/main.cpp b/src/main.cpp index 455bce1..4abe282 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,8 @@ #include "stb_image.h" #include "change_directory.h" +#include "../assets/maps/worlds.h" + #pragma clang diagnostic ignored "-Waddress-of-temporary" using namespace glm; @@ -95,6 +97,7 @@ static float editor_camera_distance = 30.0f; static bool show_demo_window; static bool show_tile_picker; static bool show_tree_editor; +static bool show_world_editor; static bool show_settings; static float character_speed = 4.0f; @@ -201,7 +204,9 @@ struct Map { R_Buffer trees_buffer; }; -static Map current_map; +static Uint32 num_loaded_maps; +static Map *loaded_maps; +static Map *current_map; struct alignas(16) PerFrame { i32vec2 drag_start; @@ -416,7 +421,7 @@ struct TreeInfo { }; static TreeInfo tree_infos[] = { - { 0, "decorations/tree_standard_tile.png" }, + { 0, "decorations/tree_standard_tile_angle.png" }, }; static vec4 tree_uvs[SDL_arraysize(tree_infos)]; @@ -455,11 +460,37 @@ static R_Texture create_texture(const char *path) { return result; } +static bool save_worlds_list() { + const char *path = ASSETS_PATH "maps/worlds.h"; + + SDL_IOStream *file = SDL_IOFromFile(path, "wb"); + if (!file) { + log_error("Failed to open world list file for writing."); + return false; + } + defer(SDL_CloseIO(file)); + + SDL_IOprintf(file, "#ifndef WORLDS_H\n#define WORLDS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif // __cplusplus\n\nstatic const char *world_names[] = {\n"); + + for (int i = 0; i < num_loaded_maps; i++) { + SDL_IOprintf(file, " \"%.*s\",\n", (int)SDL_arraysize(loaded_maps[i].name), loaded_maps[i].name); + } + + SDL_IOprintf(file, "};\n\n#ifdef __cplusplus\n}\n#endif // __cplusplus\n\n#endif // WORLDS_H\n"); + + if(!SDL_FlushIO(file)) { + log_error("Failed to flush data to world list file."); + return false; + }; + + return true; +} + #define MAP_FILE_VERSION (3u) -static bool save_map(Map map) { +static bool save_map(Map *map) { char path[256] = ASSETS_PATH "maps/"; - SDL_strlcat(path, map.name, SDL_arraysize(path)); + SDL_strlcat(path, map->name, SDL_arraysize(path)); SDL_strlcat(path, ".sv", SDL_arraysize(path)); SDL_IOStream *file = SDL_IOFromFile(path, "wb"); @@ -474,18 +505,18 @@ static bool save_map(Map map) { return false; } - if (!SDL_WriteS32LE(file, map.size.x)) { + if (!SDL_WriteS32LE(file, map->size.x)) { log_error("Failed to write width to map file."); return false; } - if (!SDL_WriteS32LE(file, map.size.y)) { + if (!SDL_WriteS32LE(file, map->size.y)) { log_error("Failed to write height to map file."); return false; } - for (int i = 0; i < map.size.x * map.size.y; i++) { - Uint16 id = tile_infos[map.tiles[i]].serialization_id; + for (int i = 0; i < map->size.x * map->size.y; i++) { + Uint16 id = tile_infos[map->tiles[i]].serialization_id; if (!SDL_WriteU16LE(file, id)) { log_error("Failed to write tile to map file."); @@ -493,25 +524,25 @@ static bool save_map(Map map) { } } - if (!SDL_WriteU32LE(file, map.num_trees)) { + if (!SDL_WriteU32LE(file, map->num_trees)) { log_error("Failed to write num_trees to map file."); return false; } - for (int i = 0; i < map.num_trees; i++) { - Uint16 id = tree_infos[map.trees[i].kind].serialization_id; + for (int i = 0; i < map->num_trees; i++) { + Uint16 id = tree_infos[map->trees[i].kind].serialization_id; if (!SDL_WriteU16LE(file, id)) { log_error("Failed to write tree kind to map file."); return false; } - if (!SDL_WriteU32LE(file, map.trees[i].pos.x)) { + if (!SDL_WriteU32LE(file, map->trees[i].pos.x)) { log_error("Failed to write tree pos.x to map file."); return false; } - if (!SDL_WriteU32LE(file, map.trees[i].pos.y)) { + if (!SDL_WriteU32LE(file, map->trees[i].pos.y)) { log_error("Failed to write tree pos.y to map file."); return false; } @@ -978,10 +1009,10 @@ static bool imgui_time_picker(const char *label, int time[3]) { } static Uint32 get_corner_info(Sint32 tile_pos_x, Sint32 tile_pos_y) { - if (tile_pos_x < 0 || tile_pos_x >= current_map.size.x || tile_pos_y < 0 || tile_pos_y >= current_map.size.y) + if (tile_pos_x < 0 || tile_pos_x >= current_map->size.x || tile_pos_y < 0 || tile_pos_y >= current_map->size.y) return 0; - return tile_infos[current_map.tiles[tile_pos_y * current_map.size.x + tile_pos_x]].corner_info; + return tile_infos[current_map->tiles[tile_pos_y * current_map->size.x + tile_pos_x]].corner_info; } static Uint32 find_matching_tile(Uint32 corner_info) { @@ -1008,19 +1039,19 @@ static void change_map_tile(Sint32 pos_x, Sint32 pos_y, TileKind kind) { corner_infos[i] = corner_infos[i] ^ ((corner_infos[i] ^ ((kind << 24) | (kind << 16) | (kind << 8) | kind)) & replace_mask); } - if (0 <= pos_x + 0 && pos_x + 0 < current_map.size.x && 0 <= pos_y + 1 && pos_y + 1 < current_map.size.y) - current_map.tiles[(pos_y + 1) * current_map.size.x + pos_x + 0] = find_matching_tile(corner_infos[0]); + if (0 <= pos_x + 0 && pos_x + 0 < current_map->size.x && 0 <= pos_y + 1 && pos_y + 1 < current_map->size.y) + current_map->tiles[(pos_y + 1) * current_map->size.x + pos_x + 0] = find_matching_tile(corner_infos[0]); - if (0 <= pos_x + 1 && pos_x + 1 < current_map.size.x && 0 <= pos_y + 1 && pos_y + 1 < current_map.size.y) - current_map.tiles[(pos_y + 1) * current_map.size.x + pos_x + 1] = find_matching_tile(corner_infos[1]); + if (0 <= pos_x + 1 && pos_x + 1 < current_map->size.x && 0 <= pos_y + 1 && pos_y + 1 < current_map->size.y) + current_map->tiles[(pos_y + 1) * current_map->size.x + pos_x + 1] = find_matching_tile(corner_infos[1]); - if (0 <= pos_x + 1 && pos_x + 1 < current_map.size.x && 0 <= pos_y + 0 && pos_y + 0 < current_map.size.y) - current_map.tiles[(pos_y + 0) * current_map.size.x + pos_x + 1] = find_matching_tile(corner_infos[2]); + if (0 <= pos_x + 1 && pos_x + 1 < current_map->size.x && 0 <= pos_y + 0 && pos_y + 0 < current_map->size.y) + current_map->tiles[(pos_y + 0) * current_map->size.x + pos_x + 1] = find_matching_tile(corner_infos[2]); - if (0 <= pos_x + 0 && pos_x + 0 < current_map.size.x && 0 <= pos_y + 0 && pos_y + 0 < current_map.size.y) - current_map.tiles[(pos_y + 0) * current_map.size.x + pos_x + 0] = find_matching_tile(corner_infos[3]); + if (0 <= pos_x + 0 && pos_x + 0 < current_map->size.x && 0 <= pos_y + 0 && pos_y + 0 < current_map->size.y) + current_map->tiles[(pos_y + 0) * current_map->size.x + pos_x + 0] = find_matching_tile(corner_infos[3]); - renderer_texture_update(current_map.texture, 0, 0, current_map.size.x, current_map.size.y, current_map.tiles, current_map.size.x * sizeof(Uint16)); + renderer_texture_update(current_map->texture, 0, 0, current_map->size.x, current_map->size.y, current_map->tiles, current_map->size.x * sizeof(Uint16)); } static void SameLineOrWrap(const ImVec2& size) { @@ -1158,87 +1189,87 @@ static void process_event_editor(SDL_Event event) { } if (event.button.button == SDL_BUTTON_LEFT) { - if (selected_tree != -1 && 0 <= tile_pos.x && tile_pos.x < current_map.size.x -1 && 0 <= tile_pos.y && tile_pos.y < current_map.size.y - 1) { + if (selected_tree != -1 && 0 <= tile_pos.x && tile_pos.x < current_map->size.x -1 && 0 <= tile_pos.y && tile_pos.y < current_map->size.y - 1) { Sint32 found_tree = -1; - for (Sint32 i = 0; i < current_map.num_trees; i++) { - if ((current_map.trees[i].pos.x == entity_pos.x) && (current_map.trees[i].pos.y == entity_pos.y)) { + for (Sint32 i = 0; i < current_map->num_trees; i++) { + if ((current_map->trees[i].pos.x == entity_pos.x) && (current_map->trees[i].pos.y == entity_pos.y)) { found_tree = i; break; } } if (found_tree != -1) { - Tree tmp = current_map.trees[found_tree]; - SDL_memmove(¤t_map.trees[found_tree], ¤t_map.trees[found_tree + 1], (current_map.num_trees - (found_tree + 1)) * sizeof(*current_map.trees)); - current_map.trees[current_map.num_trees - 1] = tmp; + Tree tmp = current_map->trees[found_tree]; + SDL_memmove(¤t_map->trees[found_tree], ¤t_map->trees[found_tree + 1], (current_map->num_trees - (found_tree + 1)) * sizeof(*current_map->trees)); + current_map->trees[current_map->num_trees - 1] = tmp; } else { - current_map.trees = (Tree *)SDL_realloc(current_map.trees, (current_map.num_trees + 1) * sizeof(*current_map.trees)); - current_map.trees[current_map.num_trees] = { + current_map->trees = (Tree *)SDL_realloc(current_map->trees, (current_map->num_trees + 1) * sizeof(*current_map->trees)); + current_map->trees[current_map->num_trees] = { .pos = entity_pos, .kind = (Uint32)selected_tree, }; - current_map.num_trees++; + current_map->num_trees++; } - std::stable_sort(¤t_map.trees[0], ¤t_map.trees[current_map.num_trees], [](const Tree &a, const Tree &b){ return a.pos.y > b.pos.y; }); + std::stable_sort(¤t_map->trees[0], ¤t_map->trees[current_map->num_trees], [](const Tree &a, const Tree &b){ return a.pos.y > b.pos.y; }); - if (current_map.trees_buffer) renderer_buffer_destroy(current_map.trees_buffer); - current_map.trees_buffer = renderer_buffer_create(R_BUFFER_USAGE_VERTEX, current_map.num_trees * sizeof(*current_map.trees), current_map.trees, "trees_buffer"); + if (current_map->trees_buffer) renderer_buffer_destroy(current_map->trees_buffer); + current_map->trees_buffer = renderer_buffer_create(R_BUFFER_USAGE_VERTEX, current_map->num_trees * sizeof(*current_map->trees), current_map->trees, "trees_buffer"); } if (show_tree_editor && selected_tree == -1) { - for (Sint32 i = 0; i < current_map.num_trees; i++) { - if ((current_map.trees[i].pos.x == entity_pos.x) && (current_map.trees[i].pos.y == entity_pos.y)) { - SDL_memmove(¤t_map.trees[i], ¤t_map.trees[i + 1], (current_map.num_trees - (i + 1)) * sizeof(*current_map.trees)); - current_map.num_trees--; + for (Sint32 i = 0; i < current_map->num_trees; i++) { + if ((current_map->trees[i].pos.x == entity_pos.x) && (current_map->trees[i].pos.y == entity_pos.y)) { + SDL_memmove(¤t_map->trees[i], ¤t_map->trees[i + 1], (current_map->num_trees - (i + 1)) * sizeof(*current_map->trees)); + current_map->num_trees--; break; } } - if (current_map.trees_buffer) renderer_buffer_destroy(current_map.trees_buffer); - if (current_map.num_trees) current_map.trees_buffer = renderer_buffer_create(R_BUFFER_USAGE_VERTEX, current_map.num_trees * sizeof(*current_map.trees), current_map.trees, "trees_buffer"); + if (current_map->trees_buffer) renderer_buffer_destroy(current_map->trees_buffer); + if (current_map->num_trees) current_map->trees_buffer = renderer_buffer_create(R_BUFFER_USAGE_VERTEX, current_map->num_trees * sizeof(*current_map->trees), current_map->trees, "trees_buffer"); } if (selected_tile_kind != -1) { change_map_tile(tile_pos.x, tile_pos.y, (TileKind)selected_tile_kind); - if (-1 <= tile_pos.x && tile_pos.x < current_map.size.x && -1 <= tile_pos.y && tile_pos.y < current_map.size.y) { + if (-1 <= tile_pos.x && tile_pos.x < current_map->size.x && -1 <= tile_pos.y && tile_pos.y < current_map->size.y) { dragging_tile_change = true; } } - if (0 <= tile_pos.x && tile_pos.x < current_map.size.x && 0 <= tile_pos.y && tile_pos.y < current_map.size.y) { + if (0 <= tile_pos.x && tile_pos.x < current_map->size.x && 0 <= tile_pos.y && tile_pos.y < current_map->size.y) { dragging_tile_change = true; } SDL_Keymod modifiers = SDL_GetModState(); if (modifiers & SDL_KMOD_SHIFT && tile_pos.x <= -1) { if(modifiers & SDL_KMOD_CTRL) - change_map_size(¤t_map, 'W', -1); + change_map_size(current_map, 'W', -1); else - change_map_size(¤t_map, 'W', 1); + change_map_size(current_map, 'W', 1); } - if (modifiers & SDL_KMOD_SHIFT && tile_pos.x == current_map.size.x) { + if (modifiers & SDL_KMOD_SHIFT && tile_pos.x == current_map->size.x) { if (modifiers & SDL_KMOD_CTRL) - change_map_size(¤t_map, 'E', -1); + change_map_size(current_map, 'E', -1); else - change_map_size(¤t_map, 'E', 1); + change_map_size(current_map, 'E', 1); } if (modifiers & SDL_KMOD_SHIFT && tile_pos.y <= -1) { if (modifiers & SDL_KMOD_CTRL) - change_map_size(¤t_map, 'N', -1); + change_map_size(current_map, 'N', -1); else - change_map_size(¤t_map, 'N', 1); + change_map_size(current_map, 'N', 1); } - if (modifiers & SDL_KMOD_SHIFT && tile_pos.y == current_map.size.y) { + if (modifiers & SDL_KMOD_SHIFT && tile_pos.y == current_map->size.y) { if (modifiers & SDL_KMOD_CTRL) - change_map_size(¤t_map, 'S', -1); + change_map_size(current_map, 'S', -1); else - change_map_size(¤t_map, 'S', 1); + change_map_size(current_map, 'S', 1); } } } break; @@ -1256,8 +1287,8 @@ static void process_event_editor(SDL_Event event) { vec2 floor_intersection = get_floor_intersection_of_mouse(vec2(event.button.x, event.button.y)); i32vec2 tile_pos = grid_tile_pos_from_floor_intersection(floor_intersection); - Sint32 tile_x = clamp(0, tile_pos.x, current_map.size.x - 1); - Sint32 tile_y = clamp(0, tile_pos.y, current_map.size.y - 1); + Sint32 tile_x = clamp(0, tile_pos.x, current_map->size.x - 1); + Sint32 tile_y = clamp(0, tile_pos.y, current_map->size.y - 1); i32vec2 drag_start = grid_tile_pos_from_floor_intersection(drag_start_pos); @@ -1269,11 +1300,11 @@ static void process_event_editor(SDL_Event event) { for (Sint32 y = start_y; y <= end_y; y++) { for (Sint32 x = start_x; x <= end_x; x++) { - current_map.tiles[x + current_map.size.x * y] = selected_tile; + current_map->tiles[x + current_map->size.x * y] = selected_tile; } } - renderer_texture_update(current_map.texture, 0, 0, current_map.size.x, current_map.size.y, current_map.tiles, current_map.size.x * sizeof(Uint16)); + renderer_texture_update(current_map->texture, 0, 0, current_map->size.x, current_map->size.y, current_map->tiles, current_map->size.x * sizeof(Uint16)); } dragging_tile_change = false; @@ -1308,6 +1339,7 @@ static void update_state_editor() { ImGui::NewFrame(); const ImGuiViewport *viewport = ImGui::GetMainViewport(); + ImGuiStyle &style = ImGui::GetStyle(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { @@ -1335,6 +1367,7 @@ static void update_state_editor() { if (ImGui::BeginMenu("Edit")) { ImGui::MenuItem("Tile Picker", NULL, &show_tile_picker); ImGui::MenuItem("Trees", NULL, &show_tree_editor); + ImGui::MenuItem("Worlds", NULL, &show_world_editor); ImGui::EndMenu(); } @@ -1350,6 +1383,7 @@ static void update_state_editor() { ImGuiID left_dock = ImGui::DockBuilderSplitNode(main_viewport_dock, ImGuiDir_Left, 0.2f, NULL, NULL); ImGui::DockBuilderDockWindow("Tile Picker", left_dock); ImGui::DockBuilderDockWindow("Trees", left_dock); + ImGui::DockBuilderDockWindow("Worlds", left_dock); ImGui::DockBuilderFinish(main_viewport_dock); } ImGui::DockSpaceOverViewport(main_viewport_dock, ImGui::GetMainViewport(), ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode); @@ -1470,7 +1504,6 @@ static void update_state_editor() { if (show_tree_editor) { if (ImGui::Begin("Trees", &show_tree_editor, ImGuiWindowFlags_NoFocusOnAppearing)) { - ImGuiStyle &style = ImGui::GetStyle(); if (SelectableImage("##tree", tree_textures_atlas_imgui, ImVec2(64, 64), vec2(0, 0), vec2(0, 0), selected_tree == -1)) selected_tree = -1; @@ -1491,6 +1524,58 @@ static void update_state_editor() { selected_tree = -1; } + auto add_world_modal = ImGui::GetID("Add World"); + ImGui::SetNextWindowSize(ImVec2(200, 0), ImGuiCond_Appearing); + if (ImGui::BeginPopupModal("Add World")) { + static Map new_map = {}; + ImGui::SetNextItemWidth(-1); + ImGui::InputTextWithHint("##Name", "Name", new_map.name, SDL_arraysize(new_map.name)); + + float button_width = (ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) * 0.5f; + + if (ImGui::Button("Cancel", ImVec2(button_width, 0))) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + if (ImGui::Button("Add", ImVec2(button_width, 0))) { + new_map.size = vec2(2, 2); + new_map.tiles = (Uint16 *)SDL_calloc(new_map.size.x * new_map.size.y, sizeof(*new_map.tiles)); + + new_map.texture = renderer_texture_create(R_TEXTURE_FORMAT_R16_UINT, R_TEXTURE_USAGE_STORAGE, false, new_map.size.x, new_map.size.y, new_map.tiles, "map_texture"); + + size_t current_map_index = current_map - loaded_maps; + + loaded_maps = (Map *)SDL_realloc(loaded_maps, (num_loaded_maps + 1) * sizeof(new_map)); + loaded_maps[num_loaded_maps++] = new_map; + + current_map = &loaded_maps[current_map_index]; + + save_map(&new_map); + save_worlds_list(); + new_map = {}; + + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if (show_world_editor) { + if (ImGui::Begin("Worlds", &show_world_editor, ImGuiWindowFlags_NoFocusOnAppearing)) { + if(ImGui::BeginListBox("##Worlds", ImVec2(-1, 0))) { + for (int i = 0; i < num_loaded_maps; i++) { + if (ImGui::Selectable(loaded_maps[i].name, current_map == &loaded_maps[i])) { + current_map = &loaded_maps[i]; + } + } + ImGui::EndListBox(); + + if (ImGui::Button("Add World", ImVec2(-1, 0))) { + ImGui::OpenPopup(add_world_modal); + } + } + } + ImGui::End(); + } + ImGui::SetNextWindowPos({ viewport->WorkPos.x + viewport->WorkSize.x - 10.0f, viewport->WorkPos.y + 10.0f }, ImGuiCond_Always, { 1.0f, 0.0f }); if (ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) { ImGui::Checkbox("Grid", &show_grid); @@ -1530,9 +1615,9 @@ static void render_editor() { vec2 floor_intersection = get_floor_intersection_of_mouse(mouse_pos); i32vec2 tile_pos = grid_tile_pos_from_floor_intersection(floor_intersection); - per_frame.map_width = current_map.size.x; - per_frame.grid_width = selected_tile != -1 ? current_map.size.x : current_map.size.x + 1; - per_frame.grid_height = selected_tile != -1 ? current_map.size.y : current_map.size.y + 1; + per_frame.map_width = current_map->size.x; + per_frame.grid_width = selected_tile != -1 ? current_map->size.x : current_map->size.x + 1; + per_frame.grid_height = selected_tile != -1 ? current_map->size.y : current_map->size.y + 1; per_frame.grid_offset = selected_tile != -1 ? vec2(-0.5f, -0.5f) : vec2(-1.0f, -1.0f); per_frame.mouse = tile_pos; @@ -1578,8 +1663,8 @@ static void render_editor() { { ZoneScopedN("Draw Map"); renderer_frame_set_shader(R_SHADER_WORLD); - renderer_frame_draw(NULL, NULL, NULL, 6, current_map.size.y * current_map.size.x, &(R_Draw_Resources){ - .vertex_storage_textures = (R_Texture[]){ current_map.texture }, + renderer_frame_draw(NULL, NULL, NULL, 6, current_map->size.y * current_map->size.x, &(R_Draw_Resources){ + .vertex_storage_textures = (R_Texture[]){ current_map->texture }, .vertex_uniform_buffers = (R_Buffer[]){ view_projection_matrix_buffer, per_frame_buffer }, .num_vertex_storage_textures = 1, @@ -1595,10 +1680,10 @@ static void render_editor() { }); } - if (current_map.num_trees) { + if (current_map->num_trees) { ZoneScopedN("Draw Trees"); renderer_frame_set_shader(R_SHADER_TREE); - renderer_frame_draw(NULL, NULL, current_map.trees_buffer, 6, current_map.num_trees, &(R_Draw_Resources){ + renderer_frame_draw(NULL, NULL, current_map->trees_buffer, 6, current_map->num_trees, &(R_Draw_Resources){ .vertex_uniform_buffers = (R_Buffer[]){ view_projection_matrix_buffer, per_frame_buffer }, .num_vertex_uniform_buffers = 2, @@ -1711,10 +1796,10 @@ static void update_state_game() { bool is_movement_key_pressed = keyboard_state[SDL_SCANCODE_W] || keyboard_state[SDL_SCANCODE_A] || keyboard_state[SDL_SCANCODE_S] || keyboard_state[SDL_SCANCODE_D] || keyboard_state[SDL_SCANCODE_UP] || keyboard_state[SDL_SCANCODE_LEFT] || keyboard_state[SDL_SCANCODE_DOWN] || keyboard_state[SDL_SCANCODE_RIGHT]; if (!player.is_moving && (queued_movement || is_movement_key_pressed)) { switch (queued_movement_direction) { - case DIR_UP: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, 1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_LEFT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2(-1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_DOWN: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, -1), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; - case DIR_RIGHT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 1, 0), current_map.size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_UP: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, 1), current_map->size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_LEFT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2(-1, 0), current_map->size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_DOWN: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 0, -1), current_map->size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; + case DIR_RIGHT: { player.direction = queued_movement_direction; player.target_position = clamp(ivec2(0, 0), player.position + ivec2( 1, 0), current_map->size - ivec2(2, 2)); if (player.target_position != player.position) player.is_moving = true; } break; } } queued_movement = false; @@ -1759,7 +1844,7 @@ static void render_game() { mat4x4 view_projection_matrix = view_matrix * projection_matrix; renderer_buffer_update(view_projection_matrix_buffer, 0, sizeof(view_projection_matrix), &view_projection_matrix); - per_frame.map_width = current_map.size.x; + per_frame.map_width = current_map->size.x; renderer_buffer_update(per_frame_buffer, 0, sizeof(per_frame), &per_frame); } @@ -1795,8 +1880,8 @@ static void render_game() { { ZoneScopedN("Draw Map"); renderer_frame_set_shader(R_SHADER_WORLD); - renderer_frame_draw(NULL, NULL, NULL, 6, current_map.size.y * current_map.size.x, &(R_Draw_Resources){ - .vertex_storage_textures = (R_Texture[]){ current_map.texture }, + renderer_frame_draw(NULL, NULL, NULL, 6, current_map->size.y * current_map->size.x, &(R_Draw_Resources){ + .vertex_storage_textures = (R_Texture[]){ current_map->texture }, .vertex_uniform_buffers = (R_Buffer[]){ view_projection_matrix_buffer, per_frame_buffer }, .num_vertex_storage_textures = 1, @@ -1812,10 +1897,10 @@ static void render_game() { }); } - if (current_map.num_trees) { + if (current_map->num_trees) { ZoneScopedN("Draw Trees"); renderer_frame_set_shader(R_SHADER_TREE); - renderer_frame_draw(NULL, NULL, current_map.trees_buffer, 6, current_map.num_trees, &(R_Draw_Resources){ + renderer_frame_draw(NULL, NULL, current_map->trees_buffer, 6, current_map->num_trees, &(R_Draw_Resources){ .vertex_uniform_buffers = (R_Buffer[]){ view_projection_matrix_buffer, per_frame_buffer }, .num_vertex_uniform_buffers = 2, @@ -2048,10 +2133,15 @@ int main(int argc, char **argv) { return 1; } - if (!load_map("map", ¤t_map)) { - log_error("Failed to load initial map. Exiting."); - return 1; + loaded_maps = (Map *)SDL_calloc(SDL_arraysize(world_names), sizeof(*loaded_maps)); + for (int i = 0; i < SDL_arraysize(world_names); i++) { + if (!load_map(world_names[i], &loaded_maps[i])) { + log_error("Failed to load map '%s'. Exiting.", world_names[i]); + return 1; + } } + num_loaded_maps = SDL_arraysize(world_names); + current_map = &loaded_maps[0]; mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); if (!mixer) {