5#include <unordered_map>
8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
24#include "imgui/imgui.h"
32using ImGui::BeginChild;
33using ImGui::BeginTabBar;
34using ImGui::BeginTabItem;
35using ImGui::BeginTable;
36using ImGui::BeginTooltip;
40using ImGui::EndTabBar;
41using ImGui::EndTabItem;
43using ImGui::EndTooltip;
44using ImGui::IsItemHovered;
46using ImGui::PopStyleColor;
47using ImGui::PushStyleColor;
49using ImGui::Selectable;
50using ImGui::Separator;
51using ImGui::TableHeadersRow;
52using ImGui::TableNextColumn;
53using ImGui::TableNextRow;
54using ImGui::TableSetupColumn;
79 static bool use_work_area =
true;
80 static ImGuiWindowFlags
flags = ImGuiWindowFlags_NoDecoration |
81 ImGuiWindowFlags_NoMove |
82 ImGuiWindowFlags_NoSavedSettings;
83 const ImGuiViewport *viewport = ImGui::GetMainViewport();
84 ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
85 ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
96 static bool show_gfx_group =
false;
97 static bool show_properties =
false;
100 for (
const auto &name : kToolsetColumnNames)
101 ImGui::TableSetupColumn(name.data());
186 show_gfx_group = !show_gfx_group;
194 std::vector<uint8_t> png_data;
200 "Failed to convert overworld map surface to PNG");
211 Checkbox(
"Properties", &show_properties);
219 ImGuiWindowFlags_MenuBar);
224 if (show_gfx_group) {
230 if (show_properties) {
231 ImGui::Begin(
"Properties", &show_properties);
237 if (!ImGui::IsAnyItemActive()) {
238 if (ImGui::IsKeyDown(ImGuiKey_1)) {
240 }
else if (ImGui::IsKeyDown(ImGuiKey_2)) {
242 }
else if (ImGui::IsKeyDown(ImGuiKey_3)) {
244 }
else if (ImGui::IsKeyDown(ImGuiKey_4)) {
246 }
else if (ImGui::IsKeyDown(ImGuiKey_5)) {
248 }
else if (ImGui::IsKeyDown(ImGuiKey_6)) {
250 }
else if (ImGui::IsKeyDown(ImGuiKey_7)) {
252 }
else if (ImGui::IsKeyDown(ImGuiKey_8)) {
259 "##WorldId",
"##GfxId",
"##PalId",
"##SprGfxId",
260 "##5thCol",
"##6thCol",
"##7thCol",
"##8thCol"};
265 ImGui::TableSetupColumn(name);
268 ImGui::SetNextItemWidth(120.f);
275 ->mutable_area_graphics(),
286 ->mutable_area_palette(),
319 ImGui::SetNextItemWidth(100.f);
326 HOVER_HINT(
"Enable Mosaic effect for the current map");
335 ImGui::TableSetupColumn(name);
338 ImGui::SetNextItemWidth(120.f);
341 static const std::array<std::string, 8> kCustomMapSettingsColumnNames = {
342 "TileGfx0",
"TileGfx1",
"TileGfx2",
"TileGfx3",
343 "TileGfx4",
"TileGfx5",
"TileGfx6",
"TileGfx7"};
344 for (
int i = 0; i < 8; ++i) {
349 ->mutable_custom_tileset(i),
361 ->mutable_area_palette(),
394 ImGui::SetNextItemWidth(100.f);
401 HOVER_HINT(
"Enable Mosaic effect for the current map");
410 for (
int i = 0; i < 0x40; i++) {
443 int mouse_x = mouse_position.x;
444 int mouse_y = mouse_position.y;
450 auto &selected_world =
455 int index_x = superX * 32 + tile16_x;
456 int index_y = superY * 32 + tile16_y;
462 const ImVec2 &click_position,
const std::vector<uint8_t> &tile_data) {
470 ImVec2 start_position;
501 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
502 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
503 auto &selected_world =
518 if (start_x > end_x) std::swap(start_x, end_x);
519 if (start_y > end_y) std::swap(start_y, end_y);
521 constexpr int local_map_size = 512;
523 constexpr int tiles_per_local_map = local_map_size /
kTile16Size;
525 for (
int y = start_y, i = 0; y <= end_y; y +=
kTile16Size) {
526 for (
int x = start_x; x <= end_x; x +=
kTile16Size, ++i) {
528 int local_map_x = x / local_map_size;
529 int local_map_y = y / local_map_size;
536 int index_x = local_map_x * tiles_per_local_map + tile16_x;
537 int index_y = local_map_y * tiles_per_local_map + tile16_y;
540 selected_world[index_x][index_y] = tile16_id;
559 static std::vector<int> tile16_ids;
562 if (tile16_ids.size() != 0) {
578 const auto mouse_position = ImGui::GetIO().MousePos;
579 const int large_map_size = 1024;
599 const int highlight_parent =
601 const int parent_map_x = highlight_parent % 8;
602 const int parent_map_y = highlight_parent / 8;
607 const int current_map_x = current_highlighted_map % 8;
608 const int current_map_y = current_highlighted_map / 8;
615 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
622 return absl::OkStatus();
626 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
632 if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
680 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
681 ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
682 ImGui::SetScrollX(ImGui::GetScrollX() + ImGui::GetIO().MouseWheelH * 16.0f);
683 ImGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetIO().MouseWheel * 16.0f);
701 int grid_x =
static_cast<int>(tile_pos.x / 32);
702 int grid_y =
static_cast<int>(tile_pos.y / 32);
703 int id = grid_x + grid_y * 8;
719 return absl::OkStatus();
727 for (
auto &value :
rom()->gfx_sheets()) {
728 int offset = 0x40 * (key + 1);
733 auto texture = value.texture();
735 (ImTextureID)(intptr_t)texture,
773 return absl::OkStatus();
778 ImGuiTabBarFlags_FittingPolicyScroll)) {
779 if (BeginTabItem(
"Tile16")) {
783 if (BeginTabItem(
"Tile8")) {
791 if (BeginTabItem(
"Area Graphics")) {
797 return absl::OkStatus();
806 auto color = ImVec4(255, 255, 0, 100);
808 color = ImVec4(255, 255, 255, 200);
818 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
823 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
840 entrance.entrance_id_ = deleted_entrance_id;
843 entrance.deleted =
false;
847 const auto is_hovering =
850 if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
851 ImGui::OpenPopup(
"Entrance Inserter");
872 ImVec4(255, 255, 255, 150));
880 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
885 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
890 ImGui::OpenPopup(
"Exit editor");
906 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
907 ImGui::OpenPopup(
"Exit Inserter");
933 if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
939 std::string item_name =
"";
943 item_name = absl::StrFormat(
"0x%02X", item.id_);
956 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
957 ImGui::OpenPopup(
"Item Inserter");
970 if (!sprite.deleted()) {
980 int map_x = sprite.map_x();
981 int map_y = sprite.map_y();
988 ImVec4(255, 0, 255, 150));
995 ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1017 if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
1018 ImGui::OpenPopup(
"Sprite Inserter");
1048 return absl::OkStatus();
1082 tile_data[position] = value;
1107 return absl::OkStatus();
1112 for (
int i = 0; i < 3; i++)
1114 int width = sprite.width();
1115 int height = sprite.height();
1117 auto spr_gfx = sprite.PreviewGraphics();
1118 if (spr_gfx.empty() || width == 0 || height == 0) {
1125 return absl::OkStatus();
1139 maps_bmp_[map_index].set_modified(
true);
1144 std::vector<std::future<void>> futures;
1147 auto refresh_map_async = [
this](
int map_index) {
1156 for (
int i = 1; i < 4; i++) {
1158 if (i >= 2) sibling_index += 6;
1160 std::async(std::launch::async, refresh_map_async, sibling_index));
1161 indices[i] = sibling_index;
1164 indices[0] = source_map_id;
1166 std::async(std::launch::async, refresh_map_async, source_map_id));
1168 for (
auto &each : futures) {
1171 int n = is_large ? 4 : 1;
1173 for (
int i = 0; i < n; ++i) {
1185 for (
int i = 1; i < 4; i++) {
1187 if (i >= 2) sibling_index += 6;
1191 maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
1196 return absl::OkStatus();
1201 if (current_ow_map.is_large_map()) {
1203 for (
int i = 1; i < 4; i++) {
1204 int sibling_index = current_ow_map.parent() + i;
1209 map.set_area_graphics(current_ow_map.area_graphics());
1210 map.set_area_palette(current_ow_map.area_palette());
1215 map.set_message_id(current_ow_map.message_id());
1223 return absl::OkStatus();
1236 std::vector<std::future<void>> futures;
1239 futures.push_back(std::async(
1242 std::vector<uint8_t> tile_data(16 * 16, 0x00);
1243 for (
int ty = 0; ty < 16; ty++) {
1244 for (
int tx = 0; tx < 16; tx++) {
1245 int position = tx + (ty * 0x10);
1247 tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
1249 tile_data[position] = value;
1257 for (
auto &future : futures) {
1267 return absl::OkStatus();
1271 static bool init_properties =
false;
1273 if (!init_properties) {
1274 for (
int i = 0; i < 0x40; i++) {
1275 std::string area_graphics_str = absl::StrFormat(
1278 ->push_back(area_graphics_str);
1280 area_graphics_str = absl::StrFormat(
1283 ->push_back(area_graphics_str);
1285 std::string area_palette_str =
1288 ->push_back(area_palette_str);
1290 area_palette_str = absl::StrFormat(
1293 ->push_back(area_palette_str);
1294 std::string sprite_gfx_str = absl::StrFormat(
1297 ->push_back(sprite_gfx_str);
1299 sprite_gfx_str = absl::StrFormat(
1302 ->push_back(sprite_gfx_str);
1304 sprite_gfx_str = absl::StrFormat(
1307 ->push_back(sprite_gfx_str);
1309 sprite_gfx_str = absl::StrFormat(
1312 ->push_back(sprite_gfx_str);
1314 std::string sprite_palette_str = absl::StrFormat(
1317 ->push_back(sprite_palette_str);
1319 sprite_palette_str = absl::StrFormat(
1322 ->push_back(sprite_palette_str);
1324 sprite_palette_str = absl::StrFormat(
1327 ->push_back(sprite_palette_str);
1329 sprite_palette_str = absl::StrFormat(
1332 ->push_back(sprite_palette_str);
1334 init_properties =
true;
1337 Text(
"Area Gfx LW/DW");
1345 Text(
"Sprite Gfx LW/DW");
1359 Text(
"Area Pal LW/DW");
1366 static bool show_gfx_group =
false;
1367 Checkbox(
"Show Gfx Group Editor", &show_gfx_group);
1368 if (show_gfx_group) {
1376 if (BeginTable(
"UsageStatsTable", 3,
kOWEditFlags, ImVec2(0, 0))) {
1377 TableSetupColumn(
"Entrances");
1378 TableSetupColumn(
"Grid", ImGuiTableColumnFlags_WidthStretch,
1379 ImGui::GetContentRegionAvail().x);
1380 TableSetupColumn(
"Usage", ImGuiTableColumnFlags_WidthFixed, 256);
1385 if (BeginChild(
"UnusedSpritesetScroll", ImVec2(0, 0),
true,
1386 ImGuiWindowFlags_HorizontalScrollbar)) {
1387 for (
int i = 0; i < 0x81; i++) {
1388 auto entrance_name =
rom()->resource_label()->GetLabel(
1390 std::string str = absl::StrFormat(
"%#x - %s", i, entrance_name);
1393 ? ImGuiSelectableFlags_Disabled
1399 if (IsItemHovered()) {
1401 Text(
"Entrance ID: %d", i);
1422 return absl::OkStatus();
1427 int totalSquares = 128;
1428 int squaresWide = 8;
1429 int squaresTall = (totalSquares + squaresWide - 1) /
1433 for (
int row = 0; row < squaresTall; ++row) {
1436 for (
int col = 0; col < squaresWide; ++col) {
1437 if (row * squaresWide + col >= totalSquares) {
1445 PushStyleColor(ImGuiCol_Button,
1446 ImVec4(1.0f, 0.5f, 0.0f,
1451 if (Button(
"##square", ImVec2(20, 20))) {
1463 if (IsItemHovered()) {
1478 Text(
"Current Tile16 Drawn Position (Relative): %d, %d", relative_x,
1482 Text(
"Light World Map Tiles: %d",
1484 Text(
"Dark World Map Tiles: %d",
1486 Text(
"Special World Map Tiles: %d",
1489 static bool view_lw_map_tiles =
false;
1490 static MemoryEditor mem_edit;
1492 if (Button(
"View Light World Map Tiles")) {
1493 view_lw_map_tiles = !view_lw_map_tiles;
1496 if (view_lw_map_tiles) {
1497 mem_edit.DrawContents(
1510 [
this]() { DrawOverworldCanvas(); });
1512 [
this]() { status_ = DrawTileSelector(); });
1514 if (rom()->is_loaded()) {
1515 status_ = UpdateUsageStats();
1519 [
this]() { DrawToolset(); });
1521 if (rom()->is_loaded()) {
1522 status_ = tile16_editor_.Update();
1525 gui::zeml::Bind(&*layout_node_.GetNode(
"OwGfxGroupEditor"), [
this]() {
1526 if (rom()->is_loaded()) {
1527 status_ = gfx_group_editor_.Update();
void UpdateBitmap(gfx::Bitmap *bitmap)
Used to update a bitmap on the screen.
static Renderer & GetInstance()
void RenderBitmap(gfx::Bitmap *bitmap)
Used to render a bitmap to the screen.
absl::Status LoadEntranceTileTypes(Rom &rom)
void RefreshMapProperties()
absl::Status Redo() override
zelda3::overworld::OverworldItem current_item_
gui::Canvas blockset_canvas_
absl::Status Update() final
EditingMode previous_mode
zelda3::GameEntity * current_entity_
std::vector< gfx::Bitmap > tile16_individual_
zelda3::GameEntity * dragged_entity_
std::vector< std::vector< uint8_t > > tile16_individual_data_
gfx::BitmapTable current_graphics_set_
void DrawOverworldItems()
Tile16Editor tile16_editor_
gfx::SnesPalette palette_
zelda3::Sprite current_sprite_
GfxGroupEditor gfx_group_editor_
gui::Canvas graphics_bin_canvas_
void DrawOverworldCanvas()
Allows the user to make changes to the overworld map.
void DrawCustomOverworldMapSettings()
Draw the overworld settings for ZSCustomOverworld.
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling, bool holes=false)
void DrawFullscreenCanvas()
Draws the canvas, tile16 selector, and toolset in fullscreen.
absl::Status CheckForCurrentMap()
Check for changes to the overworld map. Calls RefreshOverworldMap and RefreshTile16Blockset on the cu...
gui::Canvas ow_map_canvas_
void CheckForSelectRectangle()
Draw and create the tile16 IDs that are currently selected.
absl::Status DrawAreaGraphics()
gui::Canvas current_gfx_canvas_
zelda3::overworld::Overworld overworld_
absl::Status UpdateUsageStats()
gfx::BitmapTable maps_bmp_
absl::Status Undo() override
absl::Status LoadSpriteGraphics()
void DrawOverworldEdits()
absl::Status RefreshTile16Blockset()
bool map_blockset_loaded_
bool middle_mouse_dragging_
void DrawOverworldSprites()
absl::Status RefreshMapPalette()
void RefreshChildMap(int i)
zelda3::overworld::OverworldExit current_exit_
gui::Canvas properties_canvas_
gui::zeml::Node layout_node_
gfx::Bitmap current_gfx_bmp_
zelda3::overworld::OverworldEntrance current_entrance_
gfx::BitmapTable sprite_previews_
void DrawOverworldProperties()
void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling)
absl::Status DrawTile16Selector()
void RenderUpdatedMapBitmap(const ImVec2 &click_position, const std::vector< uint8_t > &tile_data)
void RefreshOverworldMap()
void CheckForOverworldEdits()
Check for changes to the overworld map.
absl::Status LoadGraphics()
Load the Bitmap objects for each OverworldMap.
void DrawToolset()
Toolset for entrances, exits, items, sprites, and transports.
bool overworld_canvas_fullscreen_
gfx::Bitmap tile16_blockset_bmp_
absl::Status DrawTileSelector()
void DrawOverworldMapSettings()
Draws the overworld map settings. Graphics, palettes, etc.
absl::Status SetCurrentTile(int id)
absl::Status InitBlockset(const gfx::Bitmap &tile16_blockset_bmp, const gfx::Bitmap ¤t_gfx_bmp, const std::vector< gfx::Bitmap > &tile16_individual, uint8_t all_tiles_types[0x200])
Represents a bitmap image.
absl::Status ApplyPalette(const SnesPalette &palette)
Copy color data from the SnesPalette into the SDL_Palette.
void WriteToPixel(int position, uchar value)
void set_modified(bool modified)
bool DrawTileSelector(int size)
void DrawOutline(int x, int y, int w, int h)
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0), bool drag=false)
void DrawRect(int x, int y, int w, int h, ImVec4 color)
void UpdateInfoGrid(ImVec2 bg_size, int tile_size, float scale=1.0f, float grid_size=64.0f, int label_id=0)
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
void DrawBitmapGroup(std::vector< int > &group, std::vector< gfx::Bitmap > &tile16_individual_, int tile_size, float scale=1.0f)
void DrawText(std::string text, int x, int y)
auto hover_mouse_pos() const
void DrawBitmap(const Bitmap &bitmap, int border_offset=0, bool ready=true)
auto set_draggable(bool value)
auto selected_tile_pos() const
auto selected_points() const
auto mutable_labels(int i)
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
auto drawn_tile_position() const
auto set_selected_tile_pos(ImVec2 pos)
auto global_scale() const
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
auto selected_tiles() const
bool select_rect_active() const
void DrawContextMenu(gfx::Bitmap *bitmap=nullptr)
auto set_highlight_tile_id(int i)
auto mutable_deleted_entrances()
auto mutable_sprites(int state)
absl::Status SaveMap32Tiles()
auto current_graphics() const
absl::Status CreateTile32Tilemap()
void set_current_map(int i)
OWBlockset & GetMapTiles(int world_type)
absl::Status SaveMap16Tiles()
absl::Status SaveOverworldMaps()
auto deleted_entrances() const
absl::Status SaveEntrances()
auto mutable_overworld_map(int i)
absl::Status Load(Rom &rom)
auto tile16_blockset_data() const
std::vector< gfx::Tile16 > tiles16() const
auto overworld_map(int i) const
int GetTileFromPosition(ImVec2 position) const
auto sprites(int state) const
auto mutable_all_tiles_types()
auto current_area_palette() const
auto current_map_bitmap_data() const
absl::Status SaveMapProperties()
#define PRINT_IF_ERROR(expression)
#define RETURN_IF_ERROR(expression)
#define HOVER_HINT(string)
#define ICON_MD_GRID_VIEW
#define ICON_MD_MORE_VERT
#define ICON_MD_OPEN_IN_FULL
#define ICON_MD_TABLE_CHART
#define ICON_MD_DOOR_BACK
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_DOOR_FRONT
#define ICON_MD_ADD_LOCATION
#define ICON_MD_PEST_CONTROL_RODENT
#define ICON_MD_CONTENT_COPY
#define ICON_MD_PAN_TOOL_ALT
void CopyImageToClipboard(const std::vector< uint8_t > &data)
std::string UppercaseHexByte(uint8_t byte, bool leading)
constexpr uint kOverworldMapSize
constexpr absl::string_view kTileSelectorTab
void DrawSpriteInserterPopup()
constexpr absl::string_view kOWMapTable
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity, ImVec2 canvas_p0, ImVec2 scrolling)
constexpr std::array< const char *, 8 > kMapSettingsColumnNames
bool DrawEntranceInserterPopup()
bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit)
constexpr ImGuiTableFlags kOWMapFlags
constexpr absl::string_view kGamePartComboString
bool DrawOverworldEntrancePopup(zelda3::overworld::OverworldEntrance &entrance)
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
constexpr int kTile16Size
void DrawItemInsertPopup()
void DrawExitInserterPopup()
constexpr ImGuiTableFlags kOWEditFlags
constexpr ImGuiTableFlags kToolsetTableFlags
constexpr absl::string_view kWorldList
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool &is_dragging_entity, zelda3::GameEntity *&dragged_entity, zelda3::GameEntity *¤t_entity, bool free_movement)
bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item)
constexpr float kInputFieldSize
absl::Status DisplayPalette(gfx::SnesPalette &palette, bool loaded)
Node Parse(const std::string &yazon_input, const std::map< std::string, void * > &data_bindings)
Parse a zeml string.
void Bind(Node *node, std::function< void()> callback)
Bind a callback to a node.
void Render(Node &node)
Render a zeml tree.
std::string LoadFile(const std::string &filename)
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
void BeginChildWithScrollbar(const char *str_id)
void BeginWindowWithDisplaySettings(const char *id, bool *active, const ImVec2 &size, ImGuiWindowFlags flags)
void BeginChildBothScrollbars(int id)
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
void EndWindowWithDisplaySettings()
constexpr int kNumTile16Individual
constexpr int kNumOverworldMaps
const std::vector< std::string > kSecretItemNames
Node * GetNode(const std::string &searchId)