7#include "absl/strings/str_format.h"
19#include "imgui/imgui.h"
33 {.card_id =
"screen.dungeon_maps",
34 .display_name =
"Dungeon Maps",
35 .window_title =
" Dungeon Map Editor",
38 .shortcut_hint =
"Alt+1",
40 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
41 .disabled_tooltip =
"Load a ROM first"});
42 panel_manager->RegisterPanel(
43 {.card_id =
"screen.inventory_menu",
44 .display_name =
"Inventory Menu",
45 .window_title =
" Inventory Menu",
48 .shortcut_hint =
"Alt+2",
50 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
51 .disabled_tooltip =
"Load a ROM first"});
52 panel_manager->RegisterPanel(
53 {.card_id =
"screen.overworld_map",
54 .display_name =
"Overworld Map",
55 .window_title =
" Overworld Map",
58 .shortcut_hint =
"Alt+3",
60 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
61 .disabled_tooltip =
"Load a ROM first"});
62 panel_manager->RegisterPanel(
63 {.card_id =
"screen.title_screen",
64 .display_name =
"Title Screen",
65 .window_title =
" Title Screen",
68 .shortcut_hint =
"Alt+4",
70 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
71 .disabled_tooltip =
"Load a ROM first"});
72 panel_manager->RegisterPanel(
73 {.card_id =
"screen.naming_screen",
74 .display_name =
"Naming Screen",
75 .window_title =
" Naming Screen",
78 .shortcut_hint =
"Alt+5",
80 .enabled_condition = [
this]() {
return rom()->
is_loaded(); },
81 .disabled_tooltip =
"Load a ROM first"});
84 panel_manager->RegisterEditorPanel(std::make_unique<DungeonMapsPanel>(
86 panel_manager->RegisterEditorPanel(std::make_unique<InventoryMenuPanel>(
88 panel_manager->RegisterEditorPanel(std::make_unique<OverworldMapScreenPanel>(
90 panel_manager->RegisterEditorPanel(std::make_unique<TitleScreenPanel>(
92 panel_manager->RegisterEditorPanel(std::make_unique<NamingScreenPanel>(
96 panel_manager->ShowPanel(
"screen.title_screen");
120 for (
int i = 0; i < 4; i++) {
122 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
130 const int tile8_width = 128;
131 const int tile8_height = 128;
132 std::vector<uint8_t> tile8_data(tile8_width * tile8_height);
135 for (
int sheet_idx = 0; sheet_idx < 4; sheet_idx++) {
136 const auto& sheet = *
sheets_[sheet_idx];
137 int dest_y_offset = sheet_idx * 32;
139 for (
int y = 0; y < 32; y++) {
140 for (
int x = 0; x < 128; x++) {
141 int src_index = y * 128 + x;
142 int dest_index = (dest_y_offset + y) * 128 + x;
144 if (src_index < sheet.size() && dest_index < tile8_data.size()) {
145 tile8_data[dest_index] = sheet.data()[src_index];
161 return absl::OkStatus();
171 return absl::OkStatus();
194 ImGui::TextColored(theme.text_error_red,
"Error loading inventory: %s",
202 if (ImGui::BeginTable(
"InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
203 ImGui::TableSetupColumn(
"Canvas");
204 ImGui::TableSetupColumn(
"Tilesheet");
205 ImGui::TableSetupColumn(
"Item Icons");
206 ImGui::TableSetupColumn(
"Palette");
207 ImGui::TableHeadersRow();
209 ImGui::TableNextColumn();
221 ImGui::TableNextColumn();
224 frame_opts.
canvas_size = ImVec2(128 * 2 + 2, (192 * 2) + 4);
234 ImGui::TableNextColumn();
237 ImGui::TableNextColumn();
253 if (ImGui::BeginTable(
"InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
255 ImGui::TableSetupColumn(
"#drawTool");
256 ImGui::TableSetupColumn(
"#sep1");
257 ImGui::TableSetupColumn(
"#zoomOut");
258 ImGui::TableSetupColumn(
"#zoomIN");
259 ImGui::TableSetupColumn(
"#sep2");
260 ImGui::TableSetupColumn(
"#bg2Tool");
261 ImGui::TableSetupColumn(
"#bg3Tool");
262 ImGui::TableSetupColumn(
"#itemTool");
264 ImGui::TableNextColumn();
268 ImGui::TableNextColumn();
272 ImGui::TableNextColumn();
274 ImGui::TableNextColumn();
278 ImGui::TableNextColumn();
282 ImGui::TableNextColumn();
284 ImGui::TableNextColumn();
288 ImGui::TableNextColumn();
298 if (ImGui::BeginChild(
"##ItemIconsList", ImVec2(0, 0),
true,
299 ImGuiWindowFlags_HorizontalScrollbar)) {
300 ImGui::Text(
"Item Icons (2x2 tiles each)");
306 "No item icons loaded. Icons will be loaded when the "
307 "inventory is initialized.");
313 if (ImGui::BeginTable(
"##IconsTable", 2,
314 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
315 ImGui::TableSetupColumn(
"Icon Name");
316 ImGui::TableSetupColumn(
"Tile Data");
317 ImGui::TableHeadersRow();
319 for (
size_t i = 0; i < icons.size(); i++) {
320 const auto& icon = icons[i];
322 ImGui::TableNextRow();
323 ImGui::TableNextColumn();
326 if (ImGui::Selectable(icon.name.c_str(),
false,
327 ImGuiSelectableFlags_SpanAllColumns)) {
331 ImGui::TableNextColumn();
333 ImGui::Text(
"TL:%04X TR:%04X", icon.tile_tl, icon.tile_tr);
335 ImGui::Text(
"BL:%04X BR:%04X", icon.tile_bl, icon.tile_br);
343 "NOTE: Individual icon editing will be implemented in the future "
344 "Oracle of Secrets menu editor. Each icon is composed of 4 tile words "
345 "representing a 2x2 arrangement of 8x8 tiles in SNES tile format "
346 "(vhopppcc cccccccc).");
361 auto boss_room = current_dungeon.boss_room;
364 std::vector<int> tile_ids_to_render;
365 std::vector<ImVec2> tile_positions;
370 if (current_dungeon.floor_rooms[
floor_number][j] != 0x0F) {
371 int tile16_id = current_dungeon.floor_gfx[
floor_number][j];
372 int posX = ((j % 5) * 32);
373 int posY = ((j / 5) * 32);
376 tile_ids_to_render.push_back(tile16_id);
377 tile_positions.emplace_back(posX * 2, posY * 2);
382 for (
size_t idx = 0; idx < tile_ids_to_render.size(); ++idx) {
383 int tile16_id = tile_ids_to_render[idx];
384 ImVec2 pos = tile_positions[idx];
388 const int tile_x = (tile16_id % tiles_per_row) * 16;
389 const int tile_y = (tile16_id / tiles_per_row) * 16;
391 std::vector<uint8_t> tile_data(16 * 16);
392 int tile_data_offset = 0;
409 if (cached_tile && cached_tile->is_active()) {
411 if (!cached_tile->texture()) {
422 if (current_dungeon.floor_rooms[
floor_number][j] != 0x0F) {
423 int posX = ((j % 5) * 32);
424 int posY = ((j / 5) * 32);
426 if (current_dungeon.floor_rooms[
floor_number][j] == boss_room) {
454 current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
455 for (
int i = 0; i < nbr_floors; i++) {
456 int basement_num = current_dungeon.nbr_of_basement - i;
457 std::string tab_name = absl::StrFormat(
"Basement %d", basement_num);
458 if (i >= current_dungeon.nbr_of_basement) {
459 tab_name = absl::StrFormat(
"Floor %d",
460 i - current_dungeon.nbr_of_basement + 1);
462 if (ImGui::BeginTabItem(tab_name.data())) {
476 const auto button_size = ImVec2(130, 0);
478 if (ImGui::Button(
"Add Floor", button_size) &&
479 current_dungeon.nbr_of_floor < 8) {
480 current_dungeon.nbr_of_floor++;
484 if (ImGui::Button(
"Remove Floor", button_size) &&
485 current_dungeon.nbr_of_floor > 0) {
486 current_dungeon.nbr_of_floor--;
490 if (ImGui::Button(
"Add Basement", button_size) &&
491 current_dungeon.nbr_of_basement < 8) {
492 current_dungeon.nbr_of_basement++;
496 if (ImGui::Button(
"Remove Basement", button_size) &&
497 current_dungeon.nbr_of_basement > 0) {
498 current_dungeon.nbr_of_basement--;
502 if (ImGui::Button(
"Copy Floor", button_size)) {
506 if (ImGui::Button(
"Paste Floor", button_size)) {
530 if (ImGui::BeginChild(
"##DungeonMapTiles", ImVec2(0, 0),
true)) {
534 tilesheet_opts.
canvas_size = ImVec2((256 * 2) + 2, (192 * 2) + 4);
549 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
584 auto current_tile_rt =
593 const int tiles_per_row =
599 std::vector<uint8_t> tile_data(64);
600 for (
int py = 0; py < 8; py++) {
601 for (
int px = 0; px < 8; px++) {
602 int src_x = tile_x + px;
603 int src_y = tile_y + py;
605 int dst_index = py * 8 + px;
616 std::move(new_tile8));
620 if (cached_tile8 && cached_tile8->is_active()) {
622 if (!cached_tile8->texture()) {
641 auto* selected_tile =
643 if (selected_tile && selected_tile->is_active()) {
645 if (!selected_tile->texture()) {
662 if (ImGui::Button(
"Modify Tile16")) {
703 static std::vector<std::string> dungeon_names = {
704 "Sewers/Sanctuary",
"Hyrule Castle",
"Eastern Palace",
705 "Desert Palace",
"Tower of Hera",
"Agahnim's Tower",
706 "Palace of Darkness",
"Swamp Palace",
"Skull Woods",
707 "Thieves' Town",
"Ice Palace",
"Misery Mire",
708 "Turtle Rock",
"Ganon's Tower"};
710 if (ImGui::BeginTable(
"DungeonMapsTable", 4,
711 ImGuiTableFlags_Resizable |
712 ImGuiTableFlags_Reorderable |
713 ImGuiTableFlags_Hideable)) {
714 ImGui::TableSetupColumn(
"Dungeon");
715 ImGui::TableSetupColumn(
"Map");
716 ImGui::TableSetupColumn(
"Rooms Gfx");
717 ImGui::TableSetupColumn(
"Tiles Gfx");
718 ImGui::TableHeadersRow();
720 ImGui::TableNextColumn();
721 for (
int i = 0; i < dungeon_names.size(); i++) {
725 if (ImGui::IsItemClicked()) {
730 ImGui::TableNextColumn();
733 ImGui::TableNextColumn();
736 ImGui::TableNextColumn();
749 ImGui::Text(
"For use with custom inserted graphics assembly patches.");
750 if (ImGui::Button(
"Load GFX from BIN file"))
759 if (!bin_file.empty()) {
760 std::ifstream file(bin_file, std::ios::binary);
761 if (file.is_open()) {
763 std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
764 std::istreambuf_iterator<char>());
770 std::vector<std::vector<uint8_t>> gfx_sheets;
771 for (
int i = 0; i < 4; i++) {
772 gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
773 converted_bin.begin() + ((i + 1) * 0x1000));
774 sheets_[i] = std::make_unique<gfx::Bitmap>(128, 32, 8, gfx_sheets[i]);
776 *
game_data()->palette_groups.dungeon_main.mutable_palette(3));
783 status_ = absl::InternalError(
"Failed to load dungeon map tile16");
796 ImGui::TextColored(theme.text_error_red,
"Error loading title screen: %s",
804 ImGui::Text(
"Title screen not loaded. Ensure ROM is loaded.");
816 ImGui::OpenPopup(
"SaveSuccess");
823 if (ImGui::BeginPopup(
"SaveSuccess")) {
824 ImGui::Text(
"Title screen saved successfully!");
847 if (ImGui::BeginTable(
"TitleScreenTable", 2,
848 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
849 ImGui::TableSetupColumn(
"Title Screen (Composite)");
850 ImGui::TableSetupColumn(
"Tile Selector");
851 ImGui::TableHeadersRow();
854 ImGui::TableNextColumn();
858 ImGui::TableNextColumn();
871 if (composite_bitmap.is_active()) {
880 int tile_x =
static_cast<int>(click_pos.x) / 8;
881 int tile_y =
static_cast<int>(click_pos.y) / 8;
883 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
884 int tilemap_index = tile_y * 32 + tile_x;
926 if (bg1_bitmap.is_active()) {
935 int tile_x =
static_cast<int>(click_pos.x) / 8;
936 int tile_y =
static_cast<int>(click_pos.y) / 8;
938 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
939 int tilemap_index = tile_y * 32 + tile_x;
971 if (bg2_bitmap.is_active()) {
980 int tile_x =
static_cast<int>(click_pos.x) / 8;
981 int tile_y =
static_cast<int>(click_pos.y) / 8;
983 if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
984 int tilemap_index = tile_y * 32 + tile_x;
1016 if (tiles8_bitmap.is_active()) {
1025 int tile_x =
static_cast<int>(click_pos.x) / 8;
1026 int tile_y =
static_cast<int>(click_pos.y) / 8;
1027 int tiles_per_row = 128 / 8;
1045 ImGui::SetNextItemWidth(100);
1058 ImGui::TextColored(theme.text_error_red,
1059 "Error loading overworld map: %s",
1067 ImGui::Text(
"Overworld map not loaded. Ensure ROM is loaded.");
1079 ImGui::OpenPopup(
"OWSaveSuccess");
1097 if (ImGui::Button(
"Load Custom Map...")) {
1099 if (!path.empty()) {
1102 ImGui::OpenPopup(
"CustomMapLoadError");
1107 if (ImGui::Button(
"Save Custom Map...")) {
1109 if (!path.empty()) {
1112 ImGui::OpenPopup(
"CustomMapSaveSuccess");
1121 if (ImGui::BeginPopup(
"CustomMapLoadError")) {
1122 ImGui::Text(
"Error loading custom map: %s",
status_.message().data());
1125 if (ImGui::BeginPopup(
"CustomMapSaveSuccess")) {
1126 ImGui::Text(
"Custom map saved successfully!");
1131 if (ImGui::BeginPopup(
"OWSaveSuccess")) {
1132 ImGui::Text(
"Overworld map saved successfully!");
1137 if (ImGui::BeginTable(
"OWMapTable", 3,
1138 ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
1139 ImGui::TableSetupColumn(
"Map Canvas");
1140 ImGui::TableSetupColumn(
"Tileset");
1141 ImGui::TableSetupColumn(
"Palette");
1142 ImGui::TableHeadersRow();
1145 ImGui::TableNextColumn();
1150 if (map_bitmap.is_active()) {
1159 int tile_x =
static_cast<int>(click_pos.x) / 8;
1160 int tile_y =
static_cast<int>(click_pos.y) / 8;
1162 if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
1163 int tile_index = tile_x + (tile_y * 64);
1187 ImGui::TableNextColumn();
1192 if (tiles8_bitmap.is_active()) {
1200 int tile_x =
static_cast<int>(click_pos.x) / 8;
1201 int tile_y =
static_cast<int>(click_pos.y) / 8;
1210 ImGui::TableNextColumn();
1221 static bool show_bg1 =
true;
1222 static bool show_bg2 =
true;
1223 static bool show_bg3 =
true;
1225 static bool drawing_bg1 =
true;
1226 static bool drawing_bg2 =
false;
1227 static bool drawing_bg3 =
false;
1229 ImGui::Checkbox(
"Show BG1", &show_bg1);
1231 ImGui::Checkbox(
"Show BG2", &show_bg2);
1233 ImGui::Checkbox(
"Draw BG1", &drawing_bg1);
1235 ImGui::Checkbox(
"Draw BG2", &drawing_bg2);
1237 ImGui::Checkbox(
"Draw BG3", &drawing_bg3);
project::ResourceLabelManager * resource_label()
zelda3::GameData * game_data() const
EditorDependencies dependencies_
void RegisterPanel(size_t session_id, const PanelDescriptor &base_info)
void DrawTitleScreenEditor()
void DrawDungeonMapsRoomGfx()
Draw dungeon room graphics editor with enhanced tile16 editing.
gfx::SnesPalette palette_
void DrawTitleScreenBlocksetSelector()
bool title_screen_loaded_
gui::Canvas ow_map_canvas_
gfx::Tilemap tile16_blockset_
void DrawTitleScreenBG2Canvas()
gui::Canvas tilemap_canvas_
void DrawInventoryItemIcons()
std::array< gfx::TileInfo, 4 > current_tile16_info
absl::Status Save() override
absl::Status Load() override
absl::Status Update() override
gui::Canvas screen_canvas_
void DrawTitleScreenBG1Canvas()
gui::Canvas title_bg1_canvas_
zelda3::OverworldMapScreen ow_map_screen_
void Initialize() override
int selected_title_tile16_
gui::Canvas current_tile_canvas_
void DrawDungeonMapsEditor()
Draw dungeon maps editor with enhanced ROM hacking features.
gui::Canvas title_bg2_canvas_
void DrawInventoryToolset()
gfx::Tilemap tile8_tilemap_
void DrawTitleScreenCompositeCanvas()
zelda3::TitleScreen title_screen_
void DrawOverworldMapEditor()
zelda3::Inventory inventory_
EditingMode current_mode_
zelda3::DungeonMapLabels dungeon_map_labels_
bool paste_button_pressed
gui::Canvas ow_tileset_canvas_
gui::Canvas title_blockset_canvas_
void DrawDungeonMapToolset()
gui::Canvas tilesheet_canvas_
void DrawNamingScreenEditor()
void DrawDungeonMapScreen(int i)
void DrawDungeonMapsTabs()
std::vector< zelda3::DungeonMap > dungeon_maps_
void DrawInventoryMenuEditor()
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Represents a bitmap image optimized for SNES ROM hacking.
const uint8_t * data() const
const SnesPalette & palette() const
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void set_data(const std::vector< uint8_t > &data)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
void Get16x16Tile(int tile_x, int tile_y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract a 16x16 tile from the bitmap (SNES metatile size)
RAII timer for automatic timing management.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
ImVector< ImVec2 > * mutable_points()
int GetTileIdFromMousePos()
bool DrawTileSelector(int size, int size_y=0)
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
bool IsMouseHovering() const
void DrawBitmapTable(const BitmapTable &gfx_bin)
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
const ImVector< ImVec2 > & points() const
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
void DrawText(const std::string &text, int x, int y)
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load inventory screen data from ROM.
auto & mutable_lw_tiles()
absl::Status SaveCustomMap(const std::string &file_path, bool use_dark_world)
Save map data to external binary file.
auto & mutable_dw_tiles()
absl::Status LoadCustomMap(const std::string &file_path)
Load custom map from external binary file.
absl::Status Save(Rom *rom)
Save changes back to ROM.
absl::Status RenderMapLayer(bool use_dark_world)
Render map tiles into bitmap.
absl::Status Create(Rom *rom)
Initialize and load overworld map data from ROM.
auto & composite_bitmap()
auto & mutable_bg1_buffer()
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load title screen data from ROM.
auto & mutable_bg2_buffer()
absl::Status RenderCompositeLayer(bool show_bg1, bool show_bg2)
Render composite layer with BG1 on top of BG2 with transparency.
absl::Status Save(Rom *rom)
absl::Status RenderBG2Layer()
Render BG2 tilemap into bitmap pixels Converts tile IDs from tiles_bg2_buffer_ into pixel data.
absl::Status RenderBG1Layer()
Render BG1 tilemap into bitmap pixels Converts tile IDs from tiles_bg1_buffer_ into pixel data.
#define ICON_MD_MORE_VERT
#define ICON_MD_INVENTORY
#define PRINT_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
const AgentUITheme & GetTheme()
void RenderTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
void ModifyTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset, int tile_id)
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
std::vector< uint8_t > SnesTo8bppSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
void EndCanvas(Canvas &canvas)
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
bool DrawTileSelector(const CanvasRuntime &rt, int size, int size_y, ImVec2 *out_selected_pos)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
IMGUI_API absl::Status InlinePaletteEditor(gfx::SnesPalette &palette, const std::string &title, ImGuiColorEditFlags flags)
Full inline palette editor with color picker and copy options.
bool InputTileInfo(const char *label, gfx::TileInfo *tile_info)
IMGUI_API bool DisplayPalette(gfx::SnesPalette &palette, bool loaded)
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
void DrawBitmap(const CanvasRuntime &rt, gfx::Bitmap &bitmap, int border_offset, float scale)
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
std::string HexByte(uint8_t byte, HexStringParams params)
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom, GameData *game_data, const std::vector< uint8_t > &gfx_data, bool bin_mode)
Load the dungeon map tile16 from the ROM.
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom)
Save the dungeon map tile16 to the ROM.
absl::StatusOr< std::vector< DungeonMap > > LoadDungeonMaps(Rom &rom, DungeonMapLabels &dungeon_map_labels)
Load the dungeon maps from the ROM.
absl::Status SaveDungeonMaps(Rom &rom, std::vector< DungeonMap > &dungeon_maps)
Save the dungeon maps to the ROM.
#define RETURN_IF_ERROR(expr)
PanelManager * panel_manager
PaletteGroup dungeon_main
auto mutable_palette(int i)
void CacheTile(int tile_id, const Bitmap &bitmap)
Cache a tile bitmap by copying it.
Bitmap * GetTile(int tile_id)
Get a cached tile by ID.
Pair tile_size
Size of individual tiles (8x8 or 16x16)
TileCache tile_cache
Smart tile cache with LRU eviction.
Pair map_size
Size of tilemap in tiles.
Bitmap atlas
Master bitmap containing all tiles.
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
std::optional< float > grid_step
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
gfx::PaletteGroupMap palette_groups