Refactor OverworldEditor and Tile16Editor for enhanced functionality and user experience
- Removed unnecessary build flags for Yaze minimal builds in release.yml. - Updated version number to 0.3.2 in yaze.h to reflect recent changes. - Enhanced OverworldEditor with new method to scroll the blockset canvas to the currently selected tile, improving navigation. - Implemented logic to handle area size checks for ZSCustomOverworld v3, ensuring compatibility with different ROM versions. - Refactored tile selection and drawing logic in Tile16Editor for improved accuracy and user interaction. - Updated context menu in OverworldEditor to include a refresh option for map changes, enhancing usability.
This commit is contained in:
@@ -1008,6 +1008,9 @@ void OverworldEditor::CheckForSelectRectangle() {
|
||||
current_tile16_ =
|
||||
overworld_.GetTileFromPosition(ow_map_canvas_.selected_tile_pos());
|
||||
ow_map_canvas_.set_selected_tile_pos(ImVec2(-1, -1));
|
||||
|
||||
// Scroll blockset canvas to show the selected tile
|
||||
ScrollBlocksetCanvasToCurrentTile();
|
||||
}
|
||||
|
||||
static std::vector<int> tile16_ids;
|
||||
@@ -1160,35 +1163,95 @@ absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||
|
||||
const int current_highlighted_map = current_map_;
|
||||
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map() ||
|
||||
overworld_.overworld_map(current_map_)->large_index() != 0) {
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
// Get area size for v3+ ROMs, otherwise use legacy logic
|
||||
if (use_v3_area_sizes) {
|
||||
using zelda3::AreaSizeEnum;
|
||||
auto area_size = overworld_.overworld_map(current_map_)->area_size();
|
||||
const int highlight_parent =
|
||||
overworld_.overworld_map(current_highlighted_map)->parent();
|
||||
const int parent_map_x = highlight_parent % 8;
|
||||
const int parent_map_y = highlight_parent / 8;
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize, large_map_size,
|
||||
large_map_size);
|
||||
} else {
|
||||
// Calculate map coordinates accounting for world offset
|
||||
int current_map_x, current_map_y;
|
||||
|
||||
// Calculate parent map coordinates accounting for world offset
|
||||
int parent_map_x;
|
||||
int parent_map_y;
|
||||
if (current_world_ == 0) {
|
||||
// Light World (0x00-0x3F)
|
||||
current_map_x = current_highlighted_map % 8;
|
||||
current_map_y = current_highlighted_map / 8;
|
||||
parent_map_x = highlight_parent % 8;
|
||||
parent_map_y = highlight_parent / 8;
|
||||
} else if (current_world_ == 1) {
|
||||
// Dark World (0x40-0x7F)
|
||||
current_map_x = (current_highlighted_map - 0x40) % 8;
|
||||
current_map_y = (current_highlighted_map - 0x40) / 8;
|
||||
parent_map_x = (highlight_parent - 0x40) % 8;
|
||||
parent_map_y = (highlight_parent - 0x40) / 8;
|
||||
} else {
|
||||
// Special World (0x80-0x9F) - use display coordinates based on current_world_
|
||||
// The special world maps are displayed in the same 8x8 grid as LW/DW
|
||||
current_map_x = (current_highlighted_map - 0x80) % 8;
|
||||
current_map_y = (current_highlighted_map - 0x80) / 8;
|
||||
// Special World (0x80-0x9F)
|
||||
parent_map_x = (highlight_parent - 0x80) % 8;
|
||||
parent_map_y = (highlight_parent - 0x80) / 8;
|
||||
}
|
||||
|
||||
// Draw outline based on area size
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea:
|
||||
// 2x2 grid (1024x1024)
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
large_map_size, large_map_size);
|
||||
break;
|
||||
case AreaSizeEnum::WideArea:
|
||||
// 2x1 grid (1024x512) - horizontal
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
large_map_size, kOverworldMapSize);
|
||||
break;
|
||||
case AreaSizeEnum::TallArea:
|
||||
// 1x2 grid (512x1024) - vertical
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
kOverworldMapSize, large_map_size);
|
||||
break;
|
||||
case AreaSizeEnum::SmallArea:
|
||||
default:
|
||||
// 1x1 grid (512x512)
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
kOverworldMapSize, kOverworldMapSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Legacy logic for vanilla and v2 ROMs
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map() ||
|
||||
overworld_.overworld_map(current_map_)->large_index() != 0) {
|
||||
const int highlight_parent =
|
||||
overworld_.overworld_map(current_highlighted_map)->parent();
|
||||
const int parent_map_x = highlight_parent % 8;
|
||||
const int parent_map_y = highlight_parent / 8;
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize, large_map_size,
|
||||
large_map_size);
|
||||
} else {
|
||||
// Calculate map coordinates accounting for world offset
|
||||
int current_map_x;
|
||||
int current_map_y;
|
||||
if (current_world_ == 0) {
|
||||
// Light World (0x00-0x3F)
|
||||
current_map_x = current_highlighted_map % 8;
|
||||
current_map_y = current_highlighted_map / 8;
|
||||
} else if (current_world_ == 1) {
|
||||
// Dark World (0x40-0x7F)
|
||||
current_map_x = (current_highlighted_map - 0x40) % 8;
|
||||
current_map_y = (current_highlighted_map - 0x40) / 8;
|
||||
} else {
|
||||
// Special World (0x80-0x9F) - use display coordinates based on current_world_
|
||||
// The special world maps are displayed in the same 8x8 grid as LW/DW
|
||||
current_map_x = (current_highlighted_map - 0x80) % 8;
|
||||
current_map_y = (current_highlighted_map - 0x80) / 8;
|
||||
}
|
||||
ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
|
||||
current_map_y * kOverworldMapSize,
|
||||
kOverworldMapSize, kOverworldMapSize);
|
||||
}
|
||||
ow_map_canvas_.DrawOutline(current_map_x * kOverworldMapSize,
|
||||
current_map_y * kOverworldMapSize,
|
||||
kOverworldMapSize, kOverworldMapSize);
|
||||
}
|
||||
|
||||
// Ensure current map has texture created for rendering
|
||||
@@ -1341,6 +1404,9 @@ absl::Status OverworldEditor::DrawTile16Selector() {
|
||||
if (id != current_tile16_ && id >= 0 && id < 512) {
|
||||
current_tile16_ = id;
|
||||
RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
|
||||
|
||||
// Scroll blockset canvas to show the selected tile
|
||||
ScrollBlocksetCanvasToCurrentTile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2053,7 +2119,19 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) {
|
||||
}
|
||||
|
||||
// Handle multi-area maps (large, wide, tall) with safe coordination
|
||||
RefreshMultiAreaMapsSafely(map_index, map);
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
if (use_v3_area_sizes) {
|
||||
// Use v3 multi-area coordination
|
||||
RefreshMultiAreaMapsSafely(map_index, map);
|
||||
} else {
|
||||
// Legacy logic: only handle large maps for vanilla/v2
|
||||
if (map->is_large_map()) {
|
||||
RefreshMultiAreaMapsSafely(map_index, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2166,17 +2244,22 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index,
|
||||
status = sibling_map->BuildTiles16Gfx(*overworld_.mutable_tiles16(),
|
||||
overworld_.tiles16().size());
|
||||
if (status.ok()) {
|
||||
status = sibling_map->BuildBitmap(
|
||||
overworld_.GetMapTiles(current_world_));
|
||||
// Load palette for the sibling map
|
||||
status = sibling_map->LoadPalette();
|
||||
if (status.ok()) {
|
||||
maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
|
||||
maps_bmp_[sibling].set_modified(false);
|
||||
status = sibling_map->BuildBitmap(
|
||||
overworld_.GetMapTiles(current_world_));
|
||||
if (status.ok()) {
|
||||
maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
|
||||
maps_bmp_[sibling].SetPalette(overworld_.current_area_palette());
|
||||
maps_bmp_[sibling].set_modified(false);
|
||||
|
||||
// Update texture if it exists
|
||||
if (maps_bmp_[sibling].texture()) {
|
||||
core::Renderer::Get().UpdateBitmap(&maps_bmp_[sibling]);
|
||||
} else {
|
||||
EnsureMapTexture(sibling);
|
||||
// Update texture if it exists
|
||||
if (maps_bmp_[sibling].texture()) {
|
||||
core::Renderer::Get().UpdateBitmap(&maps_bmp_[sibling]);
|
||||
} else {
|
||||
EnsureMapTexture(sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2206,39 +2289,136 @@ absl::Status OverworldEditor::RefreshMapPalette() {
|
||||
overworld_.mutable_overworld_map(current_map_)->LoadPalette());
|
||||
const auto current_map_palette = overworld_.current_area_palette();
|
||||
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
||||
if (i >= 2)
|
||||
sibling_index += 6;
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
maps_bmp_[sibling_index].SetPalette(current_map_palette);
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
if (use_v3_area_sizes) {
|
||||
// Use v3 area size system
|
||||
using zelda3::AreaSizeEnum;
|
||||
auto area_size = overworld_.overworld_map(current_map_)->area_size();
|
||||
|
||||
if (area_size != AreaSizeEnum::SmallArea) {
|
||||
// Get all sibling maps that need palette updates
|
||||
std::vector<int> sibling_maps;
|
||||
int parent_id = overworld_.overworld_map(current_map_)->parent();
|
||||
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea:
|
||||
// 2x2 grid: parent, parent+1, parent+8, parent+9
|
||||
sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
|
||||
break;
|
||||
case AreaSizeEnum::WideArea:
|
||||
// 2x1 grid: parent, parent+1
|
||||
sibling_maps = {parent_id, parent_id + 1};
|
||||
break;
|
||||
case AreaSizeEnum::TallArea:
|
||||
// 1x2 grid: parent, parent+8
|
||||
sibling_maps = {parent_id, parent_id + 8};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Update palette for all siblings
|
||||
for (int sibling_index : sibling_maps) {
|
||||
if (sibling_index < 0 || sibling_index >= zelda3::kNumOverworldMaps) {
|
||||
continue;
|
||||
}
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
maps_bmp_[sibling_index].SetPalette(current_map_palette);
|
||||
}
|
||||
} else {
|
||||
// Small area - only update current map
|
||||
maps_bmp_[current_map_].SetPalette(current_map_palette);
|
||||
}
|
||||
} else {
|
||||
// Legacy logic for vanilla and v2 ROMs
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
||||
// We need to update the map and its siblings if it's a large map
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
||||
if (i >= 2)
|
||||
sibling_index += 6;
|
||||
RETURN_IF_ERROR(
|
||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||
maps_bmp_[sibling_index].SetPalette(current_map_palette);
|
||||
}
|
||||
}
|
||||
maps_bmp_[current_map_].SetPalette(current_map_palette);
|
||||
}
|
||||
|
||||
maps_bmp_[current_map_].SetPalette(current_map_palette);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void OverworldEditor::RefreshMapProperties() {
|
||||
const auto& current_ow_map = *overworld_.mutable_overworld_map(current_map_);
|
||||
if (current_ow_map.is_large_map()) {
|
||||
// We need to copy the properties from the parent map to the children
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = current_ow_map.parent() + i;
|
||||
if (i >= 2) {
|
||||
sibling_index += 6;
|
||||
|
||||
// Check if ZSCustomOverworld v3 is present
|
||||
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||
bool use_v3_area_sizes = (asm_version >= 3 && asm_version != 0xFF);
|
||||
|
||||
if (use_v3_area_sizes) {
|
||||
// Use v3 area size system
|
||||
using zelda3::AreaSizeEnum;
|
||||
auto area_size = current_ow_map.area_size();
|
||||
|
||||
if (area_size != AreaSizeEnum::SmallArea) {
|
||||
// Get all sibling maps that need property updates
|
||||
std::vector<int> sibling_maps;
|
||||
int parent_id = current_ow_map.parent();
|
||||
|
||||
switch (area_size) {
|
||||
case AreaSizeEnum::LargeArea:
|
||||
// 2x2 grid: parent+1, parent+8, parent+9 (skip parent itself)
|
||||
sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
|
||||
break;
|
||||
case AreaSizeEnum::WideArea:
|
||||
// 2x1 grid: parent+1 (skip parent itself)
|
||||
sibling_maps = {parent_id + 1};
|
||||
break;
|
||||
case AreaSizeEnum::TallArea:
|
||||
// 1x2 grid: parent+8 (skip parent itself)
|
||||
sibling_maps = {parent_id + 8};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy properties from parent map to all siblings
|
||||
for (int sibling_index : sibling_maps) {
|
||||
if (sibling_index < 0 || sibling_index >= zelda3::kNumOverworldMaps) {
|
||||
continue;
|
||||
}
|
||||
auto& map = *overworld_.mutable_overworld_map(sibling_index);
|
||||
map.set_area_graphics(current_ow_map.area_graphics());
|
||||
map.set_area_palette(current_ow_map.area_palette());
|
||||
map.set_sprite_graphics(game_state_,
|
||||
current_ow_map.sprite_graphics(game_state_));
|
||||
map.set_sprite_palette(game_state_,
|
||||
current_ow_map.sprite_palette(game_state_));
|
||||
map.set_message_id(current_ow_map.message_id());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy logic for vanilla and v2 ROMs
|
||||
if (current_ow_map.is_large_map()) {
|
||||
// We need to copy the properties from the parent map to the children
|
||||
for (int i = 1; i < 4; i++) {
|
||||
int sibling_index = current_ow_map.parent() + i;
|
||||
if (i >= 2) {
|
||||
sibling_index += 6;
|
||||
}
|
||||
auto& map = *overworld_.mutable_overworld_map(sibling_index);
|
||||
map.set_area_graphics(current_ow_map.area_graphics());
|
||||
map.set_area_palette(current_ow_map.area_palette());
|
||||
map.set_sprite_graphics(game_state_,
|
||||
current_ow_map.sprite_graphics(game_state_));
|
||||
map.set_sprite_palette(game_state_,
|
||||
current_ow_map.sprite_palette(game_state_));
|
||||
map.set_message_id(current_ow_map.message_id());
|
||||
}
|
||||
auto& map = *overworld_.mutable_overworld_map(sibling_index);
|
||||
map.set_area_graphics(current_ow_map.area_graphics());
|
||||
map.set_area_palette(current_ow_map.area_palette());
|
||||
map.set_sprite_graphics(game_state_,
|
||||
current_ow_map.sprite_graphics(game_state_));
|
||||
map.set_sprite_palette(game_state_,
|
||||
current_ow_map.sprite_palette(game_state_));
|
||||
map.set_message_id(current_ow_map.message_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3011,6 +3191,18 @@ void OverworldEditor::SetupOverworldCanvasContextMenu() {
|
||||
ow_map_canvas_.AddContextMenuItem(overlay_item);
|
||||
}
|
||||
|
||||
// Map editing controls
|
||||
gui::Canvas::ContextMenuItem refresh_map_item;
|
||||
refresh_map_item.label = "Refresh Map Changes";
|
||||
refresh_map_item.callback = [this]() {
|
||||
RefreshOverworldMap();
|
||||
auto status = RefreshTile16Blockset();
|
||||
if (!status.ok()) {
|
||||
util::logf("Failed to refresh tile16 blockset: %s", status.message().data());
|
||||
}
|
||||
};
|
||||
ow_map_canvas_.AddContextMenuItem(refresh_map_item);
|
||||
|
||||
// Canvas controls
|
||||
gui::Canvas::ContextMenuItem reset_pos_item;
|
||||
reset_pos_item.label = "Reset Canvas Position";
|
||||
@@ -3028,6 +3220,39 @@ void OverworldEditor::SetupOverworldCanvasContextMenu() {
|
||||
ow_map_canvas_.AddContextMenuItem(zoom_fit_item);
|
||||
}
|
||||
|
||||
void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() {
|
||||
// Calculate the position of the current tile in the blockset canvas
|
||||
// Blockset is arranged in an 8-tile-per-row grid, each tile is 16x16 pixels
|
||||
constexpr int kTilesPerRow = 8;
|
||||
constexpr int kTileDisplaySize = 32; // Each tile displayed at 32x32 (16x16 at 2x scale)
|
||||
|
||||
// Calculate tile position in canvas coordinates (absolute position in the grid)
|
||||
int tile_col = current_tile16_ % kTilesPerRow;
|
||||
int tile_row = current_tile16_ / kTilesPerRow;
|
||||
float tile_x = static_cast<float>(tile_col * kTileDisplaySize);
|
||||
float tile_y = static_cast<float>(tile_row * kTileDisplaySize);
|
||||
|
||||
// Get the canvas dimensions
|
||||
ImVec2 canvas_size = blockset_canvas_.canvas_size();
|
||||
|
||||
// Calculate the scroll position to center the tile in the viewport
|
||||
float scroll_x = tile_x - (canvas_size.x / 2.0F) + (kTileDisplaySize / 2.0F);
|
||||
float scroll_y = tile_y - (canvas_size.y / 2.0F) + (kTileDisplaySize / 2.0F);
|
||||
|
||||
// Clamp scroll to valid ranges (don't scroll beyond bounds)
|
||||
if (scroll_x < 0) scroll_x = 0;
|
||||
if (scroll_y < 0) scroll_y = 0;
|
||||
|
||||
// Update the blockset canvas scrolling position first
|
||||
blockset_canvas_.set_scrolling(ImVec2(-1, -scroll_y));
|
||||
|
||||
// Set the points to draw the white outline box around the current tile
|
||||
// Points are in canvas coordinates (not screen coordinates)
|
||||
blockset_canvas_.mutable_points()->clear();
|
||||
blockset_canvas_.mutable_points()->push_back(ImVec2(tile_x, tile_y));
|
||||
blockset_canvas_.mutable_points()->push_back(ImVec2(tile_x + kTileDisplaySize, tile_y + kTileDisplaySize));
|
||||
}
|
||||
|
||||
void OverworldEditor::DrawOverworldProperties() {
|
||||
static bool init_properties = false;
|
||||
|
||||
|
||||
@@ -211,6 +211,11 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
void HandleMapInteraction();
|
||||
void SetupOverworldCanvasContextMenu();
|
||||
|
||||
/**
|
||||
* @brief Scroll the blockset canvas to show the current selected tile16
|
||||
*/
|
||||
void ScrollBlocksetCanvasToCurrentTile();
|
||||
|
||||
// Scratch space canvas methods
|
||||
absl::Status DrawScratchSpace();
|
||||
absl::Status SaveCurrentSelectionToScratch(int slot);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
@@ -251,22 +252,8 @@ absl::Status Tile16Editor::Update() {
|
||||
}
|
||||
|
||||
void Tile16Editor::DrawTile16Editor() {
|
||||
if (BeginTable("#Tile16EditorTable", 2, ImGuiTableFlags_Resizable)) {
|
||||
TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed, 280);
|
||||
TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
// Blockset selection column
|
||||
TableNextColumn();
|
||||
status_ = UpdateBlockset();
|
||||
|
||||
// Editor column
|
||||
TableNextColumn();
|
||||
status_ = UpdateTile16Edit();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
// REFACTORED: Single unified table layout in UpdateTile16Edit
|
||||
status_ = UpdateTile16Edit();
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::UpdateBlockset() {
|
||||
@@ -308,7 +295,8 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
||||
selected_tile, grid_x, grid_y);
|
||||
}
|
||||
}
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true, 2.0f);
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true,
|
||||
blockset_canvas_.GetGlobalScale());
|
||||
blockset_canvas_.DrawGrid();
|
||||
blockset_canvas_.DrawOverlay();
|
||||
EndChild();
|
||||
@@ -690,7 +678,7 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
// Modern header with improved styling
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 4));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 4));
|
||||
|
||||
|
||||
// Header section with better visual hierarchy
|
||||
ImGui::BeginGroup();
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.9f, 1.0f, 1.0f), "Tile16 Editor");
|
||||
@@ -700,16 +688,16 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("Palette: %d", current_palette_);
|
||||
|
||||
|
||||
// Show actual palette slot for debugging
|
||||
if (show_debug_info) {
|
||||
ImGui::SameLine();
|
||||
int actual_slot = GetActualPaletteSlotForCurrentTile16();
|
||||
ImGui::TextDisabled("(Slot: %d)", actual_slot);
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
|
||||
// Modern button styling for controls
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 180);
|
||||
if (ImGui::Button("Debug Info", ImVec2(80, 0))) {
|
||||
@@ -719,49 +707,84 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
if (ImGui::Button("Advanced", ImVec2(80, 0))) {
|
||||
show_advanced_controls = !show_advanced_controls;
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Modern 3-column layout with improved spacing
|
||||
// REFACTORED: Improved 3-column layout with better space utilization
|
||||
if (ImGui::BeginTable("##Tile16EditLayout", 3,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch, 0.5f);
|
||||
ImGui::TableSetupColumn("Tile16 Editor", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
||||
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("Tile16 Blockset",
|
||||
ImGuiTableColumnFlags_WidthStretch, 0.35f);
|
||||
ImGui::TableSetupColumn("Tile8 Source", ImGuiTableColumnFlags_WidthStretch,
|
||||
0.35f);
|
||||
ImGui::TableSetupColumn("Editor & Controls",
|
||||
ImGuiTableColumnFlags_WidthStretch, 0.30f);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Tile8 selector column - modern design
|
||||
// ========== COLUMN 1: Tile16 Blockset ==========
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile8 Source");
|
||||
|
||||
// Modern palette group selector with better styling
|
||||
const char* palette_group_names[] = {
|
||||
"OW Main", "OW Aux", "OW Anim", "Dungeon", "Sprites", "Armor", "Sword"};
|
||||
|
||||
ImGui::SetNextItemWidth(120);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.2f, 0.2f, 0.25f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.25f, 0.25f, 0.3f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.3f, 0.3f, 0.35f, 1.0f));
|
||||
|
||||
if (ImGui::Combo("##PaletteGroup", ¤t_palette_group_, palette_group_names, 7)) {
|
||||
RETURN_IF_ERROR(RefreshAllPalettes());
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// CRITICAL FIX: Use proper scrollable child window with fixed height
|
||||
ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile16 Blockset");
|
||||
|
||||
// Blockset canvas with scrolling
|
||||
if (BeginChild("##BlocksetScrollable",
|
||||
ImVec2(0, ImGui::GetContentRegionAvail().y), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
|
||||
blockset_canvas_.DrawBackground();
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
|
||||
// Handle tile selection from blockset
|
||||
bool tile_selected = false;
|
||||
blockset_canvas_.DrawTileSelector(32.0f);
|
||||
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left) &&
|
||||
blockset_canvas_.IsMouseHovering()) {
|
||||
tile_selected = true;
|
||||
}
|
||||
|
||||
if (tile_selected) {
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
ImVec2 canvas_pos = blockset_canvas_.zero_point();
|
||||
ImVec2 mouse_pos =
|
||||
ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y);
|
||||
|
||||
int grid_x = static_cast<int>(mouse_pos.x /
|
||||
(32 * blockset_canvas_.GetGlobalScale()));
|
||||
int grid_y = static_cast<int>(mouse_pos.y /
|
||||
(32 * blockset_canvas_.GetGlobalScale()));
|
||||
int selected_tile = grid_x + grid_y * 8;
|
||||
|
||||
if (selected_tile != current_tile16_ && selected_tile >= 0) {
|
||||
RETURN_IF_ERROR(SetCurrentTile(selected_tile));
|
||||
util::logf("Selected Tile16 from blockset: %d", selected_tile);
|
||||
}
|
||||
}
|
||||
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, true, 2);
|
||||
blockset_canvas_.DrawGrid();
|
||||
blockset_canvas_.DrawOverlay();
|
||||
}
|
||||
EndChild();
|
||||
ImGui::EndGroup();
|
||||
|
||||
// ========== COLUMN 2: Tile8 Source ==========
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Tile8 Source");
|
||||
|
||||
tile8_source_canvas_.set_draggable(false);
|
||||
|
||||
// Use direct ImGui child window for proper scrolling
|
||||
if (BeginChild("##Tile8SourceScrollable",
|
||||
ImVec2(0, 32 * 8 * 4), true,
|
||||
// Scrollable tile8 source
|
||||
if (BeginChild("##Tile8SourceScrollable", ImVec2(0, 0), true,
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
|
||||
tile8_source_canvas_.DrawBackground();
|
||||
@@ -777,19 +800,18 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
}
|
||||
|
||||
if (tile8_selected) {
|
||||
// Get mouse position relative to canvas more accurately
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
ImVec2 canvas_pos = tile8_source_canvas_.zero_point();
|
||||
ImVec2 mouse_pos =
|
||||
ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y);
|
||||
|
||||
// Account for the 4x scale when calculating tile position
|
||||
// Account for dynamic zoom when calculating tile position
|
||||
int tile_x = static_cast<int>(
|
||||
mouse_pos.x /
|
||||
(8 * 4)); // 8 pixel tile * 4x scale = 32 pixels per tile
|
||||
int tile_y = static_cast<int>(mouse_pos.y / (8 * 4));
|
||||
|
||||
// Calculate tiles per row based on bitmap width (should be 16 for 128px wide bitmap)
|
||||
// Calculate tiles per row based on bitmap width
|
||||
int tiles_per_row = current_gfx_bmp_.width() / 8;
|
||||
int new_tile8 = tile_x + (tile_y * tiles_per_row);
|
||||
|
||||
@@ -802,15 +824,16 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
}
|
||||
}
|
||||
|
||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4.0F);
|
||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 2, 2, 4);
|
||||
tile8_source_canvas_.DrawGrid();
|
||||
tile8_source_canvas_.DrawOverlay();
|
||||
|
||||
} // End tile8 source scrollable child
|
||||
}
|
||||
EndChild();
|
||||
ImGui::EndGroup();
|
||||
// Tile16 editor column - compact and focused
|
||||
|
||||
// ========== COLUMN 3: Tile16 Editor + Controls ==========
|
||||
TableNextColumn();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// Fixed size container to prevent canvas expansion
|
||||
if (ImGui::BeginChild("##Tile16FixedCanvas", ImVec2(90, 90), true,
|
||||
@@ -820,9 +843,9 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
tile16_edit_canvas_.DrawBackground(ImVec2(64, 64));
|
||||
tile16_edit_canvas_.DrawContextMenu();
|
||||
|
||||
// Draw current tile16 bitmap at 4x scale for clarity (16x16 pixels -> 64x64 display)
|
||||
// Draw current tile16 bitmap with dynamic zoom
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4.0F);
|
||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 2, 2, 4);
|
||||
}
|
||||
|
||||
// Handle tile8 painting with improved hover preview
|
||||
@@ -832,21 +855,23 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
|
||||
// Create a display tile that shows the current palette selection
|
||||
gfx::Bitmap display_tile;
|
||||
|
||||
|
||||
// Get the original pixel data (already has sheet offsets from ProcessGraphicsBuffer)
|
||||
std::vector<uint8_t> tile_data = current_gfx_individual_[current_tile8_].vector();
|
||||
|
||||
std::vector<uint8_t> tile_data =
|
||||
current_gfx_individual_[current_tile8_].vector();
|
||||
|
||||
// The pixel data already contains the correct indices for the 256-color palette
|
||||
// We don't need to remap - just use it as-is
|
||||
display_tile.Create(8, 8, 8, tile_data);
|
||||
|
||||
|
||||
// Apply the complete 256-color palette
|
||||
if (overworld_palette_.size() >= 256) {
|
||||
display_tile.SetPalette(overworld_palette_);
|
||||
} else {
|
||||
display_tile.SetPalette(current_gfx_individual_[current_tile8_].palette());
|
||||
display_tile.SetPalette(
|
||||
current_gfx_individual_[current_tile8_].palette());
|
||||
}
|
||||
|
||||
|
||||
// Apply flips if needed
|
||||
if (x_flip || y_flip) {
|
||||
auto& data = display_tile.mutable_data();
|
||||
@@ -867,26 +892,27 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Render the display tile
|
||||
core::Renderer::Get().RenderBitmap(&display_tile);
|
||||
|
||||
// CRITICAL FIX: Handle tile painting with simple click instead of click+drag
|
||||
// Draw the preview first
|
||||
tile16_edit_canvas_.DrawTilePainter(display_tile, 8, 4.0F);
|
||||
tile16_edit_canvas_.DrawTilePainter(
|
||||
display_tile, 8, 4);
|
||||
|
||||
// Check for simple click to paint tile8 to tile16
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
// Get mouse position relative to tile16 canvas
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
ImVec2 canvas_pos = tile16_edit_canvas_.zero_point();
|
||||
ImVec2 mouse_pos = ImVec2(io.MousePos.x - canvas_pos.x,
|
||||
io.MousePos.y - canvas_pos.y);
|
||||
|
||||
// Convert canvas coordinates to tile16 coordinates (0-15 range)
|
||||
// The canvas is 64x64 display pixels showing a 16x16 tile at 4x scale
|
||||
int tile_x = static_cast<int>(mouse_pos.x / 4.0F);
|
||||
int tile_y = static_cast<int>(mouse_pos.y / 4.0F);
|
||||
// Convert canvas coordinates to tile16 coordinates with dynamic zoom
|
||||
int tile_x = static_cast<int>(mouse_pos.x /
|
||||
4);
|
||||
int tile_y = static_cast<int>(mouse_pos.y /
|
||||
4);
|
||||
|
||||
// Clamp to valid range
|
||||
tile_x = std::max(0, std::min(15, tile_x));
|
||||
@@ -895,22 +921,22 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
util::logf("Tile16 canvas click: (%.2f, %.2f) -> Tile16: (%d, %d)",
|
||||
mouse_pos.x, mouse_pos.y, tile_x, tile_y);
|
||||
|
||||
// Pass the display tile to draw
|
||||
RETURN_IF_ERROR(
|
||||
DrawToCurrentTile16(ImVec2(tile_x, tile_y), &display_tile));
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Right-click to pick tile8 from tile16
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
// Get mouse position relative to tile16 canvas
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
ImVec2 canvas_pos = tile16_edit_canvas_.zero_point();
|
||||
ImVec2 mouse_pos = ImVec2(io.MousePos.x - canvas_pos.x,
|
||||
io.MousePos.y - canvas_pos.y);
|
||||
|
||||
// Convert canvas coordinates to tile16 coordinates (0-15 range)
|
||||
int tile_x = static_cast<int>(mouse_pos.x / 4.0F);
|
||||
int tile_y = static_cast<int>(mouse_pos.y / 4.0F);
|
||||
// Convert with dynamic zoom
|
||||
int tile_x = static_cast<int>(mouse_pos.x /
|
||||
4);
|
||||
int tile_y = static_cast<int>(mouse_pos.y /
|
||||
4);
|
||||
|
||||
// Clamp to valid range
|
||||
tile_x = std::max(0, std::min(15, tile_x));
|
||||
@@ -922,164 +948,156 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
}
|
||||
}
|
||||
|
||||
tile16_edit_canvas_.DrawGrid(8.0F); // 8x8 grid
|
||||
tile16_edit_canvas_.DrawGrid(8.0F); // Scale grid with zoom
|
||||
tile16_edit_canvas_.DrawOverlay();
|
||||
|
||||
} // End fixed canvas child window
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// Compact preview below canvas
|
||||
if (current_tile16_bmp_.is_active()) {
|
||||
auto* texture = current_tile16_bmp_.texture();
|
||||
if (texture) {
|
||||
Text("Preview:");
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(32.0F, 32.0F));
|
||||
}
|
||||
}
|
||||
|
||||
// Controls column - clean and organized
|
||||
TableNextColumn();
|
||||
Text("Controls");
|
||||
|
||||
// Essential tile8 controls at the top
|
||||
Text("Tile8 Options:");
|
||||
Checkbox("X Flip", &x_flip);
|
||||
SameLine();
|
||||
Checkbox("Y Flip", &y_flip);
|
||||
Checkbox("Priority", &priority_tile);
|
||||
|
||||
// Show current tile8 selection
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
Text("Tile8: %d", current_tile8_);
|
||||
SameLine();
|
||||
auto* tile8_texture = current_gfx_individual_[current_tile8_].texture();
|
||||
if (tile8_texture) {
|
||||
ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(16, 16));
|
||||
}
|
||||
}
|
||||
|
||||
Separator();
|
||||
|
||||
// Modern palette selector with enhanced UX
|
||||
ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.9f, 1.0f), "Palette Selection");
|
||||
|
||||
// Show debug info if enabled
|
||||
if (show_debug_info) {
|
||||
int actual_slot = GetActualPaletteSlotForCurrentTile16();
|
||||
ImGui::TextDisabled("Button %d → Slot %d", current_palette_, actual_slot);
|
||||
// === Compact Controls Section ===
|
||||
|
||||
// Tile8 info and preview
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size()) &&
|
||||
current_gfx_individual_[current_tile8_].is_active()) {
|
||||
Text("Tile8: %02X", current_tile8_);
|
||||
SameLine();
|
||||
auto* tile8_texture = current_gfx_individual_[current_tile8_].texture();
|
||||
if (tile8_texture) {
|
||||
ImGui::Image((ImTextureID)(intptr_t)tile8_texture, ImVec2(24, 24));
|
||||
}
|
||||
}
|
||||
|
||||
// Modern palette grid with improved styling
|
||||
// Tile8 transform options in compact form
|
||||
Checkbox("X Flip", &x_flip);
|
||||
SameLine();
|
||||
Checkbox("Y Flip", &y_flip);
|
||||
SameLine();
|
||||
Checkbox("Priority", &priority_tile);
|
||||
|
||||
Separator();
|
||||
|
||||
// Palette selector - more compact
|
||||
Text("Palette:");
|
||||
if (show_debug_info) {
|
||||
SameLine();
|
||||
int actual_slot = GetActualPaletteSlotForCurrentTile16();
|
||||
ImGui::TextDisabled("(Slot %d)", actual_slot);
|
||||
}
|
||||
|
||||
// Compact palette grid
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// Calculate optimal button size based on available width
|
||||
float available_width = ImGui::GetContentRegionAvail().x;
|
||||
float button_size = std::min(28.0f, (available_width - 24.0f) / 4.0f);
|
||||
|
||||
float button_size = std::min(32.0f, (available_width - 16.0f) / 4.0f);
|
||||
|
||||
for (int row = 0; row < 2; ++row) {
|
||||
for (int col = 0; col < 4; ++col) {
|
||||
if (col > 0) ImGui::SameLine();
|
||||
|
||||
if (col > 0)
|
||||
ImGui::SameLine();
|
||||
|
||||
int i = row * 4 + col;
|
||||
bool is_current = (current_palette_ == i);
|
||||
|
||||
|
||||
// Modern button styling with better visual hierarchy
|
||||
ImGui::PushID(i);
|
||||
|
||||
|
||||
if (is_current) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.7f, 0.3f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.8f, 0.4f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.6f, 0.2f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImVec4(0.2f, 0.7f, 0.3f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(0.3f, 0.8f, 0.4f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(0.1f, 0.6f, 0.2f, 1.0f));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.35f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.45f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25f, 0.25f, 0.3f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImVec4(0.3f, 0.3f, 0.35f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(0.4f, 0.4f, 0.45f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(0.25f, 0.25f, 0.3f, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
// Add border for better definition
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, is_current ?
|
||||
ImVec4(0.4f, 0.9f, 0.5f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 0.3f));
|
||||
|
||||
if (ImGui::Button(absl::StrFormat("%d", i).c_str(), ImVec2(button_size, button_size))) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border,
|
||||
is_current ? ImVec4(0.4f, 0.9f, 0.5f, 1.0f)
|
||||
: ImVec4(0.5f, 0.5f, 0.5f, 0.3f));
|
||||
|
||||
if (ImGui::Button(absl::StrFormat("%d", i).c_str(),
|
||||
ImVec2(button_size, button_size))) {
|
||||
if (current_palette_ != i) {
|
||||
current_palette_ = i;
|
||||
auto status = RefreshAllPalettes();
|
||||
if (!status.ok()) {
|
||||
util::logf("Failed to refresh palettes: %s", status.message().data());
|
||||
util::logf("Failed to refresh palettes: %s",
|
||||
status.message().data());
|
||||
} else {
|
||||
util::logf("Palette successfully changed to %d", current_palette_);
|
||||
util::logf("Palette successfully changed to %d",
|
||||
current_palette_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopStyleColor(4); // 3 button colors + 1 border color
|
||||
ImGui::PopStyleVar(1); // border size
|
||||
ImGui::PopID();
|
||||
|
||||
// Enhanced tooltip with debug information
|
||||
|
||||
// Simplified tooltip
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("Palette Button %d", i);
|
||||
|
||||
if (show_debug_info) {
|
||||
// Show what actual palette slot this maps to for different sheets
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Sheet Mappings:");
|
||||
ImGui::Text(" Sheet 0,3,4: Slot %d", GetActualPaletteSlot(i, 0));
|
||||
ImGui::Text(" Sheet 1,2: Slot %d", GetActualPaletteSlot(i, 1));
|
||||
ImGui::Text(" Sheet 5,6: Slot %d", GetActualPaletteSlot(i, 5));
|
||||
ImGui::Text(" Sheet 7: Slot %d", GetActualPaletteSlot(i, 7));
|
||||
ImGui::Text("Palette %d → Slots:", i);
|
||||
ImGui::Text(" S0,3,4: %d", GetActualPaletteSlot(i, 0));
|
||||
ImGui::Text(" S1,2: %d", GetActualPaletteSlot(i, 1));
|
||||
ImGui::Text(" S5,6: %d", GetActualPaletteSlot(i, 5));
|
||||
ImGui::Text(" S7: %d", GetActualPaletteSlot(i, 7));
|
||||
} else {
|
||||
ImGui::Text("Palette %d", i);
|
||||
if (is_current) {
|
||||
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "✓ Active");
|
||||
} else {
|
||||
ImGui::Text("Click to activate");
|
||||
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "Active");
|
||||
}
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
Separator();
|
||||
|
||||
// Essential actions
|
||||
Text("Actions:");
|
||||
if (Button("Clear", ImVec2(50, 0))) {
|
||||
// Compact action buttons
|
||||
if (Button("Clear", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(ClearTile16());
|
||||
}
|
||||
SameLine();
|
||||
if (Button("Copy", ImVec2(50, 0))) {
|
||||
|
||||
if (Button("Copy", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(CopyTile16ToClipboard(current_tile16_));
|
||||
}
|
||||
if (Button("Paste", ImVec2(50, 0))) {
|
||||
|
||||
if (Button("Paste", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(PasteTile16FromClipboard());
|
||||
}
|
||||
|
||||
Separator();
|
||||
|
||||
// Save/Discard changes
|
||||
Text("Changes:");
|
||||
if (Button("Save", ImVec2(50, 0))) {
|
||||
// Save/Discard - full width buttons
|
||||
if (Button("Save Changes", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(CommitChangesToOverworld());
|
||||
}
|
||||
HOVER_HINT("Apply changes to overworld and regenerate blockset");
|
||||
SameLine();
|
||||
if (Button("Discard", ImVec2(50, 0))) {
|
||||
|
||||
if (Button("Discard Changes", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(DiscardChanges());
|
||||
}
|
||||
HOVER_HINT("Reload tile16 from ROM, discarding local changes");
|
||||
|
||||
bool can_undo = !undo_stack_.empty();
|
||||
Separator();
|
||||
|
||||
bool can_undo = !undo_stack_.empty();
|
||||
if (!can_undo)
|
||||
BeginDisabled();
|
||||
if (Button("Undo", ImVec2(50, 0))) {
|
||||
if (Button("Undo", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(Undo());
|
||||
}
|
||||
if (!can_undo)
|
||||
@@ -1087,61 +1105,55 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
|
||||
// Advanced controls (collapsible)
|
||||
if (show_advanced_controls) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.9f, 1.0f, 1.0f), "Advanced Controls");
|
||||
|
||||
ImGui::BeginGroup();
|
||||
if (ImGui::Button("Palette Settings", ImVec2(120, 0))) {
|
||||
Separator();
|
||||
Text("Advanced:");
|
||||
|
||||
if (Button("Palette Settings", ImVec2(-1, 0))) {
|
||||
show_palette_settings_ = !show_palette_settings_;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Manual Edit", ImVec2(120, 0))) {
|
||||
|
||||
if (Button("Manual Edit", ImVec2(-1, 0))) {
|
||||
ImGui::OpenPopup("ManualTile8Editor");
|
||||
}
|
||||
|
||||
if (ImGui::Button("Refresh Blockset", ImVec2(120, 0))) {
|
||||
|
||||
if (Button("Refresh Blockset", ImVec2(-1, 0))) {
|
||||
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Scratch space in compact form
|
||||
ImGui::Text("Scratch Space:");
|
||||
Text("Scratch:");
|
||||
DrawScratchSpace();
|
||||
|
||||
// Manual tile8 editor popup
|
||||
DrawManualTile8Inputs();
|
||||
}
|
||||
|
||||
// Debug information panel
|
||||
|
||||
// Compact debug information panel
|
||||
if (show_debug_info) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.6f, 1.0f), "Debug Information");
|
||||
|
||||
// Current tile16 info
|
||||
ImGui::BeginGroup();
|
||||
ImGui::Text("Current Tile16: %d (0x%02X)", current_tile16_, current_tile16_);
|
||||
ImGui::Text("Current Tile8: %d", current_tile8_);
|
||||
ImGui::Text("Selected Palette Button: %d", current_palette_);
|
||||
|
||||
Separator();
|
||||
Text("Debug:");
|
||||
ImGui::TextDisabled("T16:%02X T8:%d Pal:%d", current_tile16_,
|
||||
current_tile8_, current_palette_);
|
||||
|
||||
if (current_tile8_ >= 0) {
|
||||
int sheet_index = GetSheetIndexForTile8(current_tile8_);
|
||||
int actual_slot = GetActualPaletteSlot(current_palette_, sheet_index);
|
||||
ImGui::Text("Tile8 Sheet Index: %d", sheet_index);
|
||||
ImGui::Text("Actual Palette Slot: %d", actual_slot);
|
||||
ImGui::TextDisabled("Sheet:%d Slot:%d", sheet_index, actual_slot);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Palette mapping table
|
||||
if (ImGui::CollapsingHeader("Palette Mapping Reference")) {
|
||||
if (ImGui::BeginTable("##PaletteMapping", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Button");
|
||||
ImGui::TableSetupColumn("Sheet 0,3,4");
|
||||
ImGui::TableSetupColumn("Sheet 1,2");
|
||||
ImGui::TableSetupColumn("Sheet 5,6");
|
||||
ImGui::TableSetupColumn("Sheet 7");
|
||||
ImGui::TableSetupColumn("Default");
|
||||
|
||||
// Compact palette mapping table
|
||||
if (ImGui::CollapsingHeader("Palette Map",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::BeginChild("##PaletteMappingScroll", ImVec2(0, 120), true);
|
||||
if (ImGui::BeginTable("##PalMap", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("Btn", ImGuiTableColumnFlags_WidthFixed, 30);
|
||||
ImGui::TableSetupColumn("S0,3-4", ImGuiTableColumnFlags_WidthFixed,
|
||||
50);
|
||||
ImGui::TableSetupColumn("S1-2", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -1150,53 +1162,55 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
ImGui::Text("%d", GetActualPaletteSlot(i, 0));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", GetActualPaletteSlot(i, 1));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", GetActualPaletteSlot(i, 5));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", GetActualPaletteSlot(i, 7));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", GetActualPaletteSlot(i, 0));
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
// Color comparison section
|
||||
if (ImGui::CollapsingHeader("Color Comparison")) {
|
||||
|
||||
// Color preview - compact
|
||||
if (ImGui::CollapsingHeader("Colors")) {
|
||||
if (overworld_palette_.size() >= 256) {
|
||||
ImGui::Text("Overworld Palette Size: %zu colors", overworld_palette_.size());
|
||||
|
||||
// Show first few colors of current palette slot
|
||||
int actual_slot = GetActualPaletteSlotForCurrentTile16();
|
||||
ImGui::Text("Current Palette Slot Colors (starting at slot %d):", actual_slot);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
for (int i = 0; i < 16 && (actual_slot + i) < static_cast<int>(overworld_palette_.size()); ++i) {
|
||||
ImGui::Text("Slot %d:", actual_slot);
|
||||
|
||||
for (int i = 0;
|
||||
i < 8 &&
|
||||
(actual_slot + i) < static_cast<int>(overworld_palette_.size());
|
||||
++i) {
|
||||
int color_index = actual_slot + i;
|
||||
auto color = overworld_palette_[color_index];
|
||||
ImVec4 display_color = color.rgb();
|
||||
|
||||
ImGui::ColorButton(absl::StrFormat("##color%d", i).c_str(),
|
||||
display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(16, 16));
|
||||
|
||||
ImGui::ColorButton(absl::StrFormat("##c%d", i).c_str(),
|
||||
display_color, ImGuiColorEditFlags_NoTooltip,
|
||||
ImVec2(20, 20));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Slot %d: 0x%04X", color_index, color.snes());
|
||||
ImGui::SetTooltip("%d:0x%04X", color_index, color.snes());
|
||||
}
|
||||
|
||||
if ((i + 1) % 8 != 0) ImGui::SameLine();
|
||||
|
||||
if ((i + 1) % 4 != 0)
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
} else {
|
||||
ImGui::TextDisabled("Overworld palette not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
EndTable();
|
||||
}
|
||||
|
||||
// Draw palette settings if enabled
|
||||
// Draw palette settings and canvas popups
|
||||
DrawPaletteSettings();
|
||||
|
||||
// Show canvas popup windows if opened from context menu
|
||||
blockset_canvas_.ShowAdvancedCanvasProperties();
|
||||
blockset_canvas_.ShowScalingControls();
|
||||
tile8_source_canvas_.ShowAdvancedCanvasProperties();
|
||||
tile8_source_canvas_.ShowScalingControls();
|
||||
tile16_edit_canvas_.ShowAdvancedCanvasProperties();
|
||||
tile16_edit_canvas_.ShowScalingControls();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1249,7 +1263,7 @@ absl::Status Tile16Editor::LoadTile8() {
|
||||
|
||||
try {
|
||||
tile_bitmap.Create(8, 8, 8, tile_data);
|
||||
|
||||
|
||||
// Set default palette using the same system as overworld
|
||||
if (overworld_palette_.size() >= 256) {
|
||||
// Use complete 256-color palette (same as overworld system)
|
||||
@@ -1948,37 +1962,38 @@ int Tile16Editor::GetPaletteSlotForSheet(int sheet_index) const {
|
||||
}
|
||||
|
||||
// NEW: Get the actual palette slot for a given palette button and sheet index
|
||||
int Tile16Editor::GetActualPaletteSlot(int palette_button, int sheet_index) const {
|
||||
int Tile16Editor::GetActualPaletteSlot(int palette_button,
|
||||
int sheet_index) const {
|
||||
// Map palette buttons 0-7 to actual 256-color palette slots based on sheet type
|
||||
// Based on the correct 256-color palette structure from SetColorsPalette()
|
||||
// The 256-color palette is organized as a 16x16 grid (16 colors per row)
|
||||
|
||||
|
||||
switch (sheet_index) {
|
||||
case 0: // Main blockset -> AUX1 region (right side, rows 2-4, cols 9-15)
|
||||
case 3:
|
||||
case 4:
|
||||
case 0: // Main blockset -> AUX1 region (right side, rows 2-4, cols 9-15)
|
||||
case 3:
|
||||
case 4:
|
||||
// AUX1 palette: Row 2-4, cols 9-15 = slots 41-47, 57-63, 73-79
|
||||
// Use row 2, col 9 + palette_button offset
|
||||
return 41 + palette_button; // Row 2, col 9 = slot 41
|
||||
|
||||
case 5:
|
||||
case 6: // Area graphics -> AUX2 region (right side, rows 5-7, cols 9-15)
|
||||
|
||||
case 5:
|
||||
case 6: // Area graphics -> AUX2 region (right side, rows 5-7, cols 9-15)
|
||||
// AUX2 palette: Row 5-7, cols 9-15 = slots 89-95, 105-111, 121-127
|
||||
// Use row 5, col 9 + palette_button offset
|
||||
// Use row 5, col 9 + palette_button offset
|
||||
return 89 + palette_button; // Row 5, col 9 = slot 89
|
||||
|
||||
case 1:
|
||||
case 2: // Main graphics -> MAIN region (left side, rows 2-6, cols 1-7)
|
||||
|
||||
case 1:
|
||||
case 2: // Main graphics -> MAIN region (left side, rows 2-6, cols 1-7)
|
||||
// MAIN palette: Row 2-6, cols 1-7 = slots 33-39, 49-55, 65-71, 81-87, 97-103
|
||||
// Use row 2, col 1 + palette_button offset
|
||||
return 33 + palette_button; // Row 2, col 1 = slot 33
|
||||
|
||||
case 7: // Animated tiles -> ANIMATED region (row 7, cols 1-7)
|
||||
|
||||
case 7: // Animated tiles -> ANIMATED region (row 7, cols 1-7)
|
||||
// ANIMATED palette: Row 7, cols 1-7 = slots 113-119
|
||||
return 113 + palette_button; // Row 7, col 1 = slot 113
|
||||
|
||||
default:
|
||||
return 33 + palette_button; // Default to MAIN region
|
||||
|
||||
default:
|
||||
return 33 + palette_button; // Default to MAIN region
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1986,10 +2001,10 @@ int Tile16Editor::GetActualPaletteSlot(int palette_button, int sheet_index) cons
|
||||
int Tile16Editor::GetSheetIndexForTile8(int tile8_id) const {
|
||||
// Determine which graphics sheet a tile8 belongs to based on its position
|
||||
// This is based on the 256-tile per sheet organization
|
||||
|
||||
|
||||
constexpr int kTilesPerSheet = 256; // 16x16 tiles per sheet
|
||||
int sheet_index = tile8_id / kTilesPerSheet;
|
||||
|
||||
|
||||
// Clamp to valid sheet range (0-7)
|
||||
return std::min(7, std::max(0, sheet_index));
|
||||
}
|
||||
@@ -1998,17 +2013,17 @@ int Tile16Editor::GetSheetIndexForTile8(int tile8_id) const {
|
||||
int Tile16Editor::GetActualPaletteSlotForCurrentTile16() const {
|
||||
// For the current tile16, we need to determine which sheet the tile8s belong to
|
||||
// and use the most appropriate palette region
|
||||
|
||||
if (current_tile8_ >= 0 && current_tile8_ < static_cast<int>(current_gfx_individual_.size())) {
|
||||
|
||||
if (current_tile8_ >= 0 &&
|
||||
current_tile8_ < static_cast<int>(current_gfx_individual_.size())) {
|
||||
int sheet_index = GetSheetIndexForTile8(current_tile8_);
|
||||
return GetActualPaletteSlot(current_palette_, sheet_index);
|
||||
}
|
||||
|
||||
|
||||
// Default to sheet 0 (main blockset) if no tile8 selected
|
||||
return GetActualPaletteSlot(current_palette_, 0);
|
||||
}
|
||||
|
||||
|
||||
// Helper methods for palette management
|
||||
absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) {
|
||||
if (tile8_id < 0 ||
|
||||
@@ -2046,15 +2061,16 @@ absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) {
|
||||
current_palette_ = 0;
|
||||
}
|
||||
|
||||
// Use the same palette system as the overworld (complete 256-color palette)
|
||||
if (display_palette.size() >= 256) {
|
||||
// Apply complete 256-color palette (same as overworld system)
|
||||
// The pixel data already contains correct color indices for the 256-color palette
|
||||
current_gfx_individual_[tile8_id].SetPalette(display_palette);
|
||||
} else {
|
||||
// For smaller palettes, use SetPaletteWithTransparent with current palette
|
||||
current_gfx_individual_[tile8_id].SetPaletteWithTransparent(display_palette, current_palette_);
|
||||
}
|
||||
// // Use the same palette system as the overworld (complete 256-color palette)
|
||||
// if (display_palette.size() >= 256) {
|
||||
// // Apply complete 256-color palette (same as overworld system)
|
||||
// // The pixel data already contains correct color indices for the 256-color palette
|
||||
// current_gfx_individual_[tile8_id].SetPalette(display_palette);
|
||||
// } else {
|
||||
// For smaller palettes, use SetPaletteWithTransparent with current palette
|
||||
current_gfx_individual_[tile8_id].SetPaletteWithTransparent(display_palette,
|
||||
current_palette_);
|
||||
// }
|
||||
|
||||
current_gfx_individual_[tile8_id].set_modified(true);
|
||||
Renderer::Get().UpdateBitmap(¤t_gfx_individual_[tile8_id]);
|
||||
@@ -2104,13 +2120,15 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
|
||||
// CRITICAL FIX: Use the same palette system as the overworld
|
||||
// The overworld system applies the complete 256-color palette to the main graphics bitmap
|
||||
// Individual tile8 graphics use the same palette but with proper color mapping
|
||||
|
||||
|
||||
if (current_gfx_bmp_.is_active()) {
|
||||
// Apply the complete 256-color palette to the source bitmap (same as overworld)
|
||||
current_gfx_bmp_.SetPalette(display_palette);
|
||||
current_gfx_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_gfx_bmp_);
|
||||
util::logf("Applied complete 256-color palette to source bitmap (same as overworld)");
|
||||
util::logf(
|
||||
"Applied complete 256-color palette to source bitmap (same as "
|
||||
"overworld)");
|
||||
}
|
||||
|
||||
// Update current tile16 being edited with complete 256-color palette
|
||||
@@ -2133,7 +2151,8 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
|
||||
}
|
||||
|
||||
util::logf(
|
||||
"Successfully refreshed all palettes in tile16 editor using complete 256-color palette "
|
||||
"Successfully refreshed all palettes in tile16 editor using complete "
|
||||
"256-color palette "
|
||||
"(same as overworld system)");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ class Tile16Editor : public gfx::GfxContext {
|
||||
// Canvas for editing the selected tile - optimized for 2x2 grid of 8x8 tiles (16x16 total)
|
||||
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas",
|
||||
ImVec2(64, 64), // Fixed 64x64 display size (16x16 pixels at 4x scale)
|
||||
gui::CanvasGridSize::k8x8, 4.0F}; // 8x8 grid with 4x scale for clarity
|
||||
gui::CanvasGridSize::k8x8, 8.0F}; // 8x8 grid with 4x scale for clarity
|
||||
gfx::Bitmap current_tile16_bmp_;
|
||||
|
||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||
|
||||
@@ -384,8 +384,8 @@ void Canvas::DrawContextMenu() {
|
||||
refresh_graphics_ = false;
|
||||
util::logf("Applied interactive palette changes to canvas");
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::Text("Load ROM palettes first using Enhanced Palette Manager");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user