#include "overworld_editor.h" #include #include #include #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" #include "app/gfx/snes_tile.h" #include "app/rom.h" #include "app/zelda3/overworld.h" #include "gui/canvas.h" #include "gui/icons.h" /** * Drawing the Overworld * Tips by Zarby * * 1) Find the graphics pointers (constants.h) * 2) Convert the 3bpp SNES data into PC 4bpp (Hard) * 3) Get the tiles32 data * 4) Get the tiles16 data * 5) Get the map32 data using lz2 variant decompression * 6) Get the graphics data of the map * 7) Load 4bpp into Pseudo VRAM for rendering tiles to screen * 8) Render the tiles to a bitmap with a B&W palette to start * 9) Get the palette data and find how it's loaded in game * */ namespace yaze { namespace app { namespace editor { void OverworldEditor::SetupROM(ROM &rom) { rom_ = rom; } absl::Status OverworldEditor::Update() { if (rom_.isLoaded() && !all_gfx_loaded_) { LoadGraphics(); all_gfx_loaded_ = true; } auto toolset_status = DrawToolset(); if (!toolset_status.ok()) return toolset_status; ImGui::Separator(); if (ImGui::BeginTable("#owEditTable", 2, ow_edit_flags, ImVec2(0, 0))) { ImGui::TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch, ImGui::GetContentRegionAvail().x); ImGui::TableSetupColumn("Tile Selector"); ImGui::TableHeadersRow(); ImGui::TableNextRow(); ImGui::TableNextColumn(); DrawOverworldCanvas(); ImGui::TableNextColumn(); DrawTileSelector(); ImGui::EndTable(); } return absl::OkStatus(); } absl::Status OverworldEditor::DrawToolset() { if (ImGui::BeginTable("OWToolset", 17, toolset_table_flags, ImVec2(0, 0))) { for (const auto &name : kToolsetColumnNames) ImGui::TableSetupColumn(name.data()); ImGui::TableNextColumn(); ImGui::Button(ICON_MD_UNDO); ImGui::TableNextColumn(); ImGui::Button(ICON_MD_REDO); ImGui::TableNextColumn(); ImGui::Text(ICON_MD_MORE_VERT); ImGui::TableNextColumn(); ImGui::Button(ICON_MD_ZOOM_OUT); ImGui::TableNextColumn(); ImGui::Button(ICON_MD_ZOOM_IN); ImGui::TableNextColumn(); ImGui::Text(ICON_MD_MORE_VERT); ImGui::TableNextColumn(); ImGui::Button(ICON_MD_DRAW); // Entrances ImGui::TableNextColumn(); ImGui::Button(ICON_MD_DOOR_FRONT); // Exits ImGui::TableNextColumn(); ImGui::Button(ICON_MD_DOOR_BACK); // Items ImGui::TableNextColumn(); ImGui::Button(ICON_MD_GRASS); // Sprites ImGui::TableNextColumn(); ImGui::Button(ICON_MD_PEST_CONTROL_RODENT); // Transports ImGui::TableNextColumn(); ImGui::Button(ICON_MD_ADD_LOCATION); // Music ImGui::TableNextColumn(); ImGui::Button(ICON_MD_MUSIC_NOTE); ImGui::TableNextColumn(); ImGui::Text(ICON_MD_MORE_VERT); // Load Overworld ImGui::TableNextColumn(); if (ImGui::Button(ICON_MD_UPDATE)) { auto ow_status = overworld_.Load(rom_, tile16_blockset_bmp_.GetData()); if (!ow_status.ok()) return ow_status; } ImGui::TableNextColumn(); ImGui::Text(ICON_MD_MORE_VERT); ImGui::TableNextColumn(); ImGui::Text("Palette:"); for (int i = 0; i < 8; i++) { std::string id = "##PaletteColor" + std::to_string(i); ImGui::SameLine(); ImGui::ColorEdit4(id.c_str(), ¤t_palette_[i].x, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex); } ImGui::EndTable(); } return absl::OkStatus(); } void OverworldEditor::DrawOverworldMapSettings() { if (ImGui::BeginTable("#mapSettings", 7, ow_map_flags, ImVec2(0, 0), -1)) { for (const auto &name : kOverworldSettingsColumnNames) ImGui::TableSetupColumn(name.data()); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(100.f); ImGui::Combo("##world", ¤t_world_, "Light World\0Dark World\0Extra World\0"); ImGui::TableNextColumn(); ImGui::Text("GFX"); ImGui::SameLine(); ImGui::SetNextItemWidth(kInputFieldSize); ImGui::InputText("##mapGFX", map_gfx_, kByteSize); ImGui::TableNextColumn(); ImGui::Text("Palette"); ImGui::SameLine(); ImGui::SetNextItemWidth(kInputFieldSize); ImGui::InputText("##mapPal", map_palette_, kByteSize); ImGui::TableNextColumn(); ImGui::Text("Spr GFX"); ImGui::SameLine(); ImGui::SetNextItemWidth(kInputFieldSize); ImGui::InputText("##sprGFX", spr_gfx_, kByteSize); ImGui::TableNextColumn(); ImGui::Text("Spr Palette"); ImGui::SameLine(); ImGui::SetNextItemWidth(kInputFieldSize); ImGui::InputText("##sprPal", spr_palette_, kByteSize); ImGui::TableNextColumn(); ImGui::Text("Msg ID"); ImGui::SameLine(); ImGui::SetNextItemWidth(50.f); ImGui::InputText("##msgid", spr_palette_, kMessageIdSize); ImGui::TableNextColumn(); ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO ImGui::EndTable(); } } void OverworldEditor::DrawOverworldCanvas() { DrawOverworldMapSettings(); ImGui::Separator(); overworld_map_canvas_.DrawBackground(); overworld_map_canvas_.UpdateContext(); overworld_map_canvas_.DrawGrid(64.f); if (overworld_.isLoaded()) { auto map = overworld_.GetOverworldMap(0); if (map.IsInitialized() && map.IsBuilt()) { overworld_map_canvas_.GetDrawList()->AddImage( (void *)map.GetBitmap().GetTexture(), ImVec2(overworld_map_canvas_.GetZeroPoint().x + 2, overworld_map_canvas_.GetZeroPoint().y + 2), ImVec2(overworld_map_canvas_.GetZeroPoint().x + (map.GetBitmap().GetWidth() * 2), overworld_map_canvas_.GetZeroPoint().y + (map.GetBitmap().GetHeight() * 2))); } } overworld_map_canvas_.DrawOverlay(); } void OverworldEditor::DrawTileSelector() { if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) { if (ImGui::BeginTabItem("Tile8")) { ImGuiID child_id = ImGui::GetID((void *)(intptr_t)1); if (ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { DrawTile8Selector(); } ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tile16")) { if (ImGui::BeginChild("#Tile16Child", ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { DrawTile16Selector(); } ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Area Graphics")) { if (ImGui::BeginChild("#Tile16Child", ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { DrawAreaGraphics(); } ImGui::EndChild(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } } void OverworldEditor::DrawTile16Selector() { blockset_canvas_.DrawBackground(ImVec2(256 + 1, kNumSheetsToLoad * 64 + 1)); blockset_canvas_.UpdateContext(); if (map_blockset_loaded_) { blockset_canvas_.GetDrawList()->AddImage( (void *)tile16_blockset_bmp_.GetTexture(), ImVec2(blockset_canvas_.GetZeroPoint().x + 2, blockset_canvas_.GetZeroPoint().y + 2), ImVec2(blockset_canvas_.GetZeroPoint().x + (tile16_blockset_bmp_.GetWidth() * 2), blockset_canvas_.GetZeroPoint().y + (tile16_blockset_bmp_.GetHeight() * 2))); } blockset_canvas_.DrawGrid(32.0f); blockset_canvas_.DrawOverlay(); } void OverworldEditor::DrawTile8Selector() { graphics_bin_canvas_.DrawBackground( ImVec2(256 + 1, kNumSheetsToLoad * 64 + 1)); graphics_bin_canvas_.UpdateContext(); if (all_gfx_loaded_) { for (const auto &[key, value] : graphics_bin_) { int offset = 64 * (key + 1); int top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 2; if (key >= 1) { top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 64 * key; } graphics_bin_canvas_.GetDrawList()->AddImage( (void *)value.GetTexture(), ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, top_left_y), ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 256, graphics_bin_canvas_.GetZeroPoint().y + offset)); } } graphics_bin_canvas_.DrawGrid(16.0f); graphics_bin_canvas_.DrawOverlay(); } void OverworldEditor::DrawAreaGraphics() { if (overworld_.isLoaded()) { static bool set_loaded = false; if (!set_loaded) { current_graphics_set_ = overworld_.GetOverworldMap(0).GetCurrentGraphicsSet(); set_loaded = true; } current_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 16 * 64 + 1)); current_gfx_canvas_.UpdateContext(); for (const auto &[key, value] : current_graphics_set_) { int offset = 64 * (key + 1); int top_left_y = current_gfx_canvas_.GetZeroPoint().y + 2; if (key >= 1) { top_left_y = current_gfx_canvas_.GetZeroPoint().y + 64 * key; } current_gfx_canvas_.GetDrawList()->AddImage( (void *)value.GetTexture(), ImVec2(current_gfx_canvas_.GetZeroPoint().x + 2, top_left_y), ImVec2(current_gfx_canvas_.GetZeroPoint().x + 256, current_gfx_canvas_.GetZeroPoint().y + offset)); } current_gfx_canvas_.DrawGrid(32.0f); current_gfx_canvas_.DrawOverlay(); } } void OverworldEditor::LoadGraphics() { for (int i = 0; i < 8; i++) { current_palette_[i].x = (i * 0.21f); current_palette_[i].y = (i * 0.21f); current_palette_[i].z = (i * 0.21f); current_palette_[i].w = 1.f; } absl::Status graphics_data_status = rom_.LoadAllGraphicsData(); if (!graphics_data_status.ok()) { std::cout << "Error " << graphics_data_status.ToString() << std::endl; } graphics_bin_ = rom_.GetGraphicsBin(); tile16_blockset_bmp_.Create(128 * 2, 8192 * 2, 8, 1048576); } } // namespace editor } // namespace app } // namespace yaze