Refactor tile selection and painting logic in OverworldEditor and Tile16Editor
- Updated tile selection handling in Tile16Editor to prevent conflicts with dragging, ensuring a smoother user experience. - Improved click detection for tile selection and painting, enhancing accuracy and responsiveness. - Added logging for tile interactions to facilitate debugging and user feedback. - Implemented critical fixes to ensure consistent behavior during tile editing operations.
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/asar_wrapper.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/core/features.h"
|
||||
#include "app/core/performance_monitor.h"
|
||||
#include "app/core/window.h"
|
||||
@@ -20,6 +19,7 @@
|
||||
#include "app/editor/overworld/tile16_editor.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/tilemap.h"
|
||||
#include "app/gui/canvas.h"
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
@@ -733,7 +734,8 @@ void OverworldEditor::DrawOverworldMaps() {
|
||||
int map_y = (yy * kOverworldMapSize * scale);
|
||||
|
||||
// Check if the map has a texture, if not, ensure it gets loaded
|
||||
if (!maps_bmp_[world_index].texture() && maps_bmp_[world_index].is_active()) {
|
||||
if (!maps_bmp_[world_index].texture() &&
|
||||
maps_bmp_[world_index].is_active()) {
|
||||
EnsureMapTexture(world_index);
|
||||
}
|
||||
|
||||
@@ -745,11 +747,14 @@ void OverworldEditor::DrawOverworldMaps() {
|
||||
// Draw a placeholder for maps that haven't loaded yet
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = ow_map_canvas_.zero_point();
|
||||
ImVec2 placeholder_pos = ImVec2(canvas_pos.x + map_x, canvas_pos.y + map_y);
|
||||
ImVec2 placeholder_size = ImVec2(kOverworldMapSize * scale, kOverworldMapSize * scale);
|
||||
ImVec2 placeholder_pos =
|
||||
ImVec2(canvas_pos.x + map_x, canvas_pos.y + map_y);
|
||||
ImVec2 placeholder_size =
|
||||
ImVec2(kOverworldMapSize * scale, kOverworldMapSize * scale);
|
||||
|
||||
// Draw a subtle loading indicator
|
||||
draw_list->AddRectFilled(placeholder_pos,
|
||||
draw_list->AddRectFilled(
|
||||
placeholder_pos,
|
||||
ImVec2(placeholder_pos.x + placeholder_size.x,
|
||||
placeholder_pos.y + placeholder_size.y),
|
||||
IM_COL32(32, 32, 32, 128)); // Dark gray with transparency
|
||||
@@ -787,8 +792,11 @@ void OverworldEditor::DrawOverworldEdits() {
|
||||
}
|
||||
|
||||
// Validate tile16_blockset_ before calling GetTilemapData
|
||||
if (!tile16_blockset_.atlas.is_active() || tile16_blockset_.atlas.vector().empty()) {
|
||||
util::logf("Error: tile16_blockset_ is not properly initialized (active: %s, size: %zu)",
|
||||
if (!tile16_blockset_.atlas.is_active() ||
|
||||
tile16_blockset_.atlas.vector().empty()) {
|
||||
util::logf(
|
||||
"Error: tile16_blockset_ is not properly initialized (active: %s, "
|
||||
"size: %zu)",
|
||||
tile16_blockset_.atlas.is_active() ? "true" : "false",
|
||||
tile16_blockset_.atlas.vector().size());
|
||||
return; // Skip drawing if blockset is invalid
|
||||
@@ -796,7 +804,10 @@ void OverworldEditor::DrawOverworldEdits() {
|
||||
|
||||
// Validate current_tile16_ before proceeding
|
||||
if (current_tile16_ < 0 || current_tile16_ >= 512) {
|
||||
util::logf("ERROR: DrawOverworldEdits - Invalid current_tile16_=%d (should be 0-511)", current_tile16_);
|
||||
util::logf(
|
||||
"ERROR: DrawOverworldEdits - Invalid current_tile16_=%d (should be "
|
||||
"0-511)",
|
||||
current_tile16_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -830,7 +841,9 @@ void OverworldEditor::RenderUpdatedMapBitmap(
|
||||
|
||||
// Bounds checking to prevent crashes
|
||||
if (current_map_ < 0 || current_map_ >= static_cast<int>(maps_bmp_.size())) {
|
||||
util::logf("ERROR: RenderUpdatedMapBitmap - Invalid current_map_ %d (maps_bmp_.size()=%zu)",
|
||||
util::logf(
|
||||
"ERROR: RenderUpdatedMapBitmap - Invalid current_map_ %d "
|
||||
"(maps_bmp_.size()=%zu)",
|
||||
current_map_, maps_bmp_.size());
|
||||
return; // Invalid map index, skip rendering
|
||||
}
|
||||
@@ -851,8 +864,11 @@ void OverworldEditor::RenderUpdatedMapBitmap(
|
||||
|
||||
// Validate bitmap state before writing
|
||||
if (!current_bitmap.is_active() || current_bitmap.size() == 0) {
|
||||
util::logf("ERROR: RenderUpdatedMapBitmap - Bitmap %d is not active or has no data (active=%s, size=%zu)",
|
||||
current_map_, current_bitmap.is_active() ? "true" : "false", current_bitmap.size());
|
||||
util::logf(
|
||||
"ERROR: RenderUpdatedMapBitmap - Bitmap %d is not active or has no "
|
||||
"data (active=%s, size=%zu)",
|
||||
current_map_, current_bitmap.is_active() ? "true" : "false",
|
||||
current_bitmap.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -862,16 +878,22 @@ void OverworldEditor::RenderUpdatedMapBitmap(
|
||||
(start_position.y + y) * kOverworldMapSize + (start_position.x + x);
|
||||
|
||||
// Bounds check for pixel index
|
||||
if (pixel_index < 0 || pixel_index >= static_cast<int>(current_bitmap.size())) {
|
||||
util::logf("ERROR: RenderUpdatedMapBitmap - pixel_index %d out of bounds (bitmap size=%zu)",
|
||||
if (pixel_index < 0 ||
|
||||
pixel_index >= static_cast<int>(current_bitmap.size())) {
|
||||
util::logf(
|
||||
"ERROR: RenderUpdatedMapBitmap - pixel_index %d out of bounds "
|
||||
"(bitmap size=%zu)",
|
||||
pixel_index, current_bitmap.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bounds check for tile data
|
||||
int tile_data_index = y * kTile16Size + x;
|
||||
if (tile_data_index < 0 || tile_data_index >= static_cast<int>(tile_data.size())) {
|
||||
util::logf("ERROR: RenderUpdatedMapBitmap - tile_data_index %d out of bounds (tile_data size=%zu)",
|
||||
if (tile_data_index < 0 ||
|
||||
tile_data_index >= static_cast<int>(tile_data.size())) {
|
||||
util::logf(
|
||||
"ERROR: RenderUpdatedMapBitmap - tile_data_index %d out of bounds "
|
||||
"(tile_data size=%zu)",
|
||||
tile_data_index, tile_data.size());
|
||||
continue;
|
||||
}
|
||||
@@ -926,7 +948,10 @@ void OverworldEditor::CheckForOverworldEdits() {
|
||||
// Number of tiles per local map (since each tile is 16x16)
|
||||
constexpr int tiles_per_local_map = local_map_size / kTile16Size;
|
||||
|
||||
util::logf("CheckForOverworldEdits: About to fill rectangle with current_tile16_=%d", current_tile16_);
|
||||
util::logf(
|
||||
"CheckForOverworldEdits: About to fill rectangle with "
|
||||
"current_tile16_=%d",
|
||||
current_tile16_);
|
||||
|
||||
// Apply the current selected tile to each position in the rectangle
|
||||
for (int y = start_y; y <= end_y; y += kTile16Size) {
|
||||
@@ -944,22 +969,29 @@ void OverworldEditor::CheckForOverworldEdits() {
|
||||
int index_y = local_map_y * tiles_per_local_map + tile16_y;
|
||||
|
||||
// Bounds check for the selected world array
|
||||
if (index_x >= 0 && index_x < 0x200 && index_y >= 0 && index_y < 0x200) {
|
||||
if (index_x >= 0 && index_x < 0x200 && index_y >= 0 &&
|
||||
index_y < 0x200) {
|
||||
// CRITICAL FIX: Set the tile to the currently selected tile16, not read from canvas
|
||||
selected_world[index_x][index_y] = current_tile16_;
|
||||
|
||||
// CRITICAL FIX: Also update the bitmap directly like single tile drawing
|
||||
ImVec2 tile_position(x, y);
|
||||
auto tile_data = gfx::GetTilemapData(tile16_blockset_, current_tile16_);
|
||||
auto tile_data =
|
||||
gfx::GetTilemapData(tile16_blockset_, current_tile16_);
|
||||
if (!tile_data.empty()) {
|
||||
RenderUpdatedMapBitmap(tile_position, tile_data);
|
||||
util::logf("CheckForOverworldEdits: Updated bitmap at position (%d,%d) with tile16_id=%d",
|
||||
util::logf(
|
||||
"CheckForOverworldEdits: Updated bitmap at position (%d,%d) "
|
||||
"with tile16_id=%d",
|
||||
x, y, current_tile16_);
|
||||
} else {
|
||||
util::logf("ERROR: Failed to get tile data for tile16_id=%d", current_tile16_);
|
||||
util::logf("ERROR: Failed to get tile data for tile16_id=%d",
|
||||
current_tile16_);
|
||||
}
|
||||
} else {
|
||||
util::logf("ERROR: Rectangle selection position [%d,%d] out of bounds", index_x, index_y);
|
||||
util::logf(
|
||||
"ERROR: Rectangle selection position [%d,%d] out of bounds",
|
||||
index_x, index_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -967,7 +999,8 @@ void OverworldEditor::CheckForOverworldEdits() {
|
||||
// Clear the rectangle selection after applying
|
||||
ow_map_canvas_.mutable_selected_tiles()->clear();
|
||||
ow_map_canvas_.mutable_points()->clear();
|
||||
util::logf("CheckForOverworldEdits: Rectangle selection applied and cleared");
|
||||
util::logf(
|
||||
"CheckForOverworldEdits: Rectangle selection applied and cleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1280,30 +1313,26 @@ absl::Status OverworldEditor::DrawTile16Selector() {
|
||||
blockset_canvas_.DrawContextMenu();
|
||||
blockset_canvas_.DrawBitmap(tile16_blockset_.atlas, /*border_offset=*/2,
|
||||
map_blockset_loaded_, /*scale=*/2);
|
||||
|
||||
// Fixed tile interaction detection - handle single clicks properly
|
||||
bool tile_selected = false;
|
||||
|
||||
// First, call DrawTileSelector to handle the visual feedback
|
||||
blockset_canvas_.DrawTileSelector(32.0f);
|
||||
|
||||
// Handle both single click (select) and double click (edit) properly
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && blockset_canvas_.IsMouseHovering()) {
|
||||
// Call DrawTileSelector after event detection for visual feedback
|
||||
if (blockset_canvas_.DrawTileSelector(32.0f)) {
|
||||
tile_selected = true;
|
||||
show_tile16_editor_ = true;
|
||||
}
|
||||
|
||||
// Check for double click to open tile16 editor
|
||||
bool open_tile16_editor = false;
|
||||
if (blockset_canvas_.IsMouseHovering() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
// Then check for single click (if not double-click)
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
|
||||
blockset_canvas_.IsMouseHovering()) {
|
||||
tile_selected = true;
|
||||
open_tile16_editor = true;
|
||||
}
|
||||
|
||||
if (tile_selected) {
|
||||
// Get mouse position relative to canvas
|
||||
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);
|
||||
ImVec2 mouse_pos =
|
||||
ImVec2(io.MousePos.x - canvas_pos.x, io.MousePos.y - canvas_pos.y);
|
||||
|
||||
// Calculate grid position (32x32 tiles in blockset)
|
||||
int grid_x = static_cast<int>(mouse_pos.x / 32);
|
||||
@@ -1312,15 +1341,7 @@ absl::Status OverworldEditor::DrawTile16Selector() {
|
||||
|
||||
if (id != current_tile16_ && id >= 0 && id < 512) {
|
||||
current_tile16_ = id;
|
||||
// CRITICAL FIX: Always sync tile16 editor with overworld editor selection
|
||||
RETURN_IF_ERROR(tile16_editor_.SetCurrentTile(id));
|
||||
|
||||
if (open_tile16_editor) {
|
||||
show_tile16_editor_ = true;
|
||||
util::logf("Opened Tile16 editor for tile %d (double-click)", id);
|
||||
} else {
|
||||
util::logf("Selected Tile16: %d (single-click)", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1697,8 +1718,8 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
util::logf("Loading overworld tileset (deferred textures).");
|
||||
{
|
||||
core::ScopedTimer tileset_timer("CreateBitmapWithoutTexture_Tileset");
|
||||
Renderer::Get().CreateBitmapWithoutTexture(0x80, 0x2000, 0x08,
|
||||
overworld_.tile16_blockset_data(),
|
||||
Renderer::Get().CreateBitmapWithoutTexture(
|
||||
0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
|
||||
tile16_blockset_bmp_, palette_);
|
||||
}
|
||||
map_blockset_loaded_ = true;
|
||||
@@ -1718,13 +1739,18 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
// Non-essential maps will be created on-demand when accessed
|
||||
constexpr int kEssentialMapsPerWorld = 8;
|
||||
constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
|
||||
constexpr int kDarkWorldEssential = zelda3::kDarkWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
constexpr int kSpecialWorldEssential = zelda3::kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
constexpr int kDarkWorldEssential =
|
||||
zelda3::kDarkWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
constexpr int kSpecialWorldEssential =
|
||||
zelda3::kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
|
||||
|
||||
util::logf("Creating bitmaps for essential maps only (first %d maps per world)", kEssentialMapsPerWorld);
|
||||
util::logf(
|
||||
"Creating bitmaps for essential maps only (first %d maps per world)",
|
||||
kEssentialMapsPerWorld);
|
||||
|
||||
std::vector<gfx::Bitmap*> maps_to_texture;
|
||||
maps_to_texture.reserve(kEssentialMapsPerWorld * 3); // 8 maps per world * 3 worlds
|
||||
maps_to_texture.reserve(kEssentialMapsPerWorld *
|
||||
3); // 8 maps per world * 3 worlds
|
||||
|
||||
{
|
||||
core::ScopedTimer maps_timer("CreateEssentialOverworldMaps");
|
||||
@@ -1736,7 +1762,8 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
is_essential = true;
|
||||
} else if (i >= zelda3::kDarkWorldMapIdStart && i < kDarkWorldEssential) {
|
||||
is_essential = true;
|
||||
} else if (i >= zelda3::kSpecialWorldMapIdStart && i < kSpecialWorldEssential) {
|
||||
} else if (i >= zelda3::kSpecialWorldMapIdStart &&
|
||||
i < kSpecialWorldEssential) {
|
||||
is_essential = true;
|
||||
}
|
||||
|
||||
@@ -1750,7 +1777,8 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
maps_bmp_[i].SetPalette(palette);
|
||||
maps_to_texture.push_back(&maps_bmp_[i]);
|
||||
} catch (const std::bad_alloc& e) {
|
||||
std::cout << "Error allocating map " << i << ": " << e.what() << std::endl;
|
||||
std::cout << "Error allocating map " << i << ": " << e.what()
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1760,7 +1788,8 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
|
||||
// Phase 3: Create textures only for currently visible maps
|
||||
// Only create textures for the first few maps initially
|
||||
const int initial_texture_count = std::min(4, static_cast<int>(maps_to_texture.size()));
|
||||
const int initial_texture_count =
|
||||
std::min(4, static_cast<int>(maps_to_texture.size()));
|
||||
{
|
||||
core::ScopedTimer initial_textures_timer("CreateInitialTextures");
|
||||
for (int i = 0; i < initial_texture_count; ++i) {
|
||||
@@ -1837,7 +1866,8 @@ void OverworldEditor::ProcessDeferredTextures() {
|
||||
if (is_current_world || processed < textures_per_frame / 2) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
processed++;
|
||||
it = deferred_map_textures_.erase(it); // Remove immediately after processing
|
||||
it = deferred_map_textures_.erase(
|
||||
it); // Remove immediately after processing
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
@@ -1849,7 +1879,8 @@ void OverworldEditor::ProcessDeferredTextures() {
|
||||
// Second pass: process remaining textures if we still have capacity
|
||||
if (processed < textures_per_frame) {
|
||||
it = deferred_map_textures_.begin();
|
||||
while (it != deferred_map_textures_.end() && processed < textures_per_frame) {
|
||||
while (it != deferred_map_textures_.end() &&
|
||||
processed < textures_per_frame) {
|
||||
if (*it && !(*it)->texture()) {
|
||||
Renderer::Get().RenderBitmap(*it);
|
||||
processed++;
|
||||
@@ -1862,7 +1893,8 @@ void OverworldEditor::ProcessDeferredTextures() {
|
||||
|
||||
// Third pass: process deferred map refreshes for visible maps
|
||||
if (processed < textures_per_frame) {
|
||||
for (int i = 0; i < zelda3::kNumOverworldMaps && processed < textures_per_frame; ++i) {
|
||||
for (int i = 0;
|
||||
i < zelda3::kNumOverworldMaps && processed < textures_per_frame; ++i) {
|
||||
if (maps_bmp_[i].modified() && maps_bmp_[i].is_active()) {
|
||||
// Check if this map is visible
|
||||
bool is_visible = (i == current_map_) || (i / 0x40 == current_world_);
|
||||
@@ -1908,7 +1940,8 @@ void OverworldEditor::EnsureMapTexture(int map_index) {
|
||||
|
||||
// Remove from deferred list if it was there
|
||||
std::lock_guard<std::mutex> lock(deferred_textures_mutex_);
|
||||
auto it = std::find(deferred_map_textures_.begin(), deferred_map_textures_.end(), &bitmap);
|
||||
auto it = std::find(deferred_map_textures_.begin(),
|
||||
deferred_map_textures_.end(), &bitmap);
|
||||
if (it != deferred_map_textures_.end()) {
|
||||
deferred_map_textures_.erase(it);
|
||||
}
|
||||
@@ -1979,21 +2012,25 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) {
|
||||
// Rebuild tileset only if graphics changed
|
||||
auto status = map->BuildTileset();
|
||||
if (!status.ok()) {
|
||||
util::logf("Failed to build tileset for map %d: %s", map_index, status.message().data());
|
||||
util::logf("Failed to build tileset for map %d: %s", map_index,
|
||||
status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild tiles16 graphics
|
||||
status = map->BuildTiles16Gfx(*overworld_.mutable_tiles16(), overworld_.tiles16().size());
|
||||
status = map->BuildTiles16Gfx(*overworld_.mutable_tiles16(),
|
||||
overworld_.tiles16().size());
|
||||
if (!status.ok()) {
|
||||
util::logf("Failed to build tiles16 graphics for map %d: %s", map_index, status.message().data());
|
||||
util::logf("Failed to build tiles16 graphics for map %d: %s", map_index,
|
||||
status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild bitmap
|
||||
status = map->BuildBitmap(overworld_.GetMapTiles(current_world_));
|
||||
if (!status.ok()) {
|
||||
util::logf("Failed to build bitmap for map %d: %s", map_index, status.message().data());
|
||||
util::logf("Failed to build bitmap for map %d: %s", map_index,
|
||||
status.message().data());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2003,7 +2040,8 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) {
|
||||
|
||||
// Validate surface synchronization to help debug crashes
|
||||
if (!maps_bmp_[map_index].ValidateDataSurfaceSync()) {
|
||||
util::logf("Warning: Surface synchronization issue detected for map %d", map_index);
|
||||
util::logf("Warning: Surface synchronization issue detected for map %d",
|
||||
map_index);
|
||||
}
|
||||
|
||||
// Update texture on main thread
|
||||
@@ -2026,7 +2064,8 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) {
|
||||
* by using a non-recursive approach with explicit map list processing.
|
||||
* It respects the ZScream area size logic and prevents infinite recursion.
|
||||
*/
|
||||
void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap* map) {
|
||||
void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index,
|
||||
zelda3::OverworldMap* map) {
|
||||
using zelda3::AreaSizeEnum;
|
||||
|
||||
// Skip if this is already a processed sibling to avoid double-processing
|
||||
@@ -2040,9 +2079,11 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::Overworl
|
||||
return; // No siblings to coordinate
|
||||
}
|
||||
|
||||
util::logf("RefreshMultiAreaMapsSafely: Processing %s area map %d (parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large" :
|
||||
(area_size == AreaSizeEnum::WideArea) ? "wide" : "tall",
|
||||
util::logf(
|
||||
"RefreshMultiAreaMapsSafely: Processing %s area map %d (parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large"
|
||||
: (area_size == AreaSizeEnum::WideArea) ? "wide"
|
||||
: "tall",
|
||||
map_index, map->parent());
|
||||
|
||||
// Determine all maps that are part of this multi-area structure
|
||||
@@ -2056,7 +2097,8 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::Overworl
|
||||
// Parent is top-left (quadrant 0), siblings are:
|
||||
// +1 (top-right, quadrant 1), +8 (bottom-left, quadrant 2), +9 (bottom-right, quadrant 3)
|
||||
sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
|
||||
util::logf("RefreshMultiAreaMapsSafely: Large area siblings: %d, %d, %d, %d",
|
||||
util::logf(
|
||||
"RefreshMultiAreaMapsSafely: Large area siblings: %d, %d, %d, %d",
|
||||
parent_id, parent_id + 1, parent_id + 8, parent_id + 9);
|
||||
break;
|
||||
}
|
||||
@@ -2107,9 +2149,12 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::Overworl
|
||||
bool needs_refresh = maps_bmp_[sibling].modified();
|
||||
|
||||
if ((is_current_map || is_current_world) && needs_refresh) {
|
||||
util::logf("RefreshMultiAreaMapsSafely: Refreshing %s area sibling map %d (parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large" :
|
||||
(area_size == AreaSizeEnum::WideArea) ? "wide" : "tall",
|
||||
util::logf(
|
||||
"RefreshMultiAreaMapsSafely: Refreshing %s area sibling map %d "
|
||||
"(parent: %d)",
|
||||
(area_size == AreaSizeEnum::LargeArea) ? "large"
|
||||
: (area_size == AreaSizeEnum::WideArea) ? "wide"
|
||||
: "tall",
|
||||
sibling, parent_id);
|
||||
|
||||
// Direct refresh without calling RefreshChildMapOnDemand to avoid recursion
|
||||
@@ -2119,9 +2164,11 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::Overworl
|
||||
|
||||
auto status = sibling_map->BuildTileset();
|
||||
if (status.ok()) {
|
||||
status = sibling_map->BuildTiles16Gfx(*overworld_.mutable_tiles16(), overworld_.tiles16().size());
|
||||
status = sibling_map->BuildTiles16Gfx(*overworld_.mutable_tiles16(),
|
||||
overworld_.tiles16().size());
|
||||
if (status.ok()) {
|
||||
status = sibling_map->BuildBitmap(overworld_.GetMapTiles(current_world_));
|
||||
status = sibling_map->BuildBitmap(
|
||||
overworld_.GetMapTiles(current_world_));
|
||||
if (status.ok()) {
|
||||
maps_bmp_[sibling].set_data(sibling_map->bitmap_data());
|
||||
maps_bmp_[sibling].set_modified(false);
|
||||
@@ -2137,7 +2184,9 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index, zelda3::Overworl
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
util::logf("RefreshMultiAreaMapsSafely: Failed to refresh sibling map %d: %s",
|
||||
util::logf(
|
||||
"RefreshMultiAreaMapsSafely: Failed to refresh sibling map %d: "
|
||||
"%s",
|
||||
sibling, status.message().data());
|
||||
}
|
||||
}
|
||||
@@ -3649,7 +3698,8 @@ void OverworldEditor::UpdateScratchBitmapTile(int tile_x, int tile_y,
|
||||
|
||||
scratch_slot.scratch_bitmap.set_modified(true);
|
||||
// Use batch operations for texture updates
|
||||
scratch_slot.scratch_bitmap.QueueTextureUpdate(core::Renderer::Get().renderer());
|
||||
scratch_slot.scratch_bitmap.QueueTextureUpdate(
|
||||
core::Renderer::Get().renderer());
|
||||
scratch_slot.in_use = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -707,19 +707,30 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
ImVec2(tile8_source_canvas_.width(), tile8_source_canvas_.height()),
|
||||
true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||
|
||||
// Enable dragging for scrolling behavior
|
||||
tile8_source_canvas_.set_draggable(true);
|
||||
// CRITICAL FIX: Don't use draggable mode as it conflicts with tile selection
|
||||
tile8_source_canvas_.set_draggable(false);
|
||||
tile8_source_canvas_.DrawBackground();
|
||||
tile8_source_canvas_.DrawContextMenu();
|
||||
|
||||
// Tile8 selection with improved feedback
|
||||
bool tile8_selected = false;
|
||||
tile8_source_canvas_.DrawTileSelector(32.0F);
|
||||
|
||||
if (tile8_source_canvas_.WasClicked() ||
|
||||
tile8_source_canvas_.WasDoubleClicked()) {
|
||||
auto tile_pos = tile8_source_canvas_.GetLastClickPosition();
|
||||
int tile_x = static_cast<int>(tile_pos.x / 32);
|
||||
int tile_y = static_cast<int>(tile_pos.y / 32);
|
||||
// Check for clicks properly
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
tile8_selected = true;
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
int tiles_per_row = current_gfx_bmp_.width() / 8;
|
||||
int new_tile8 = tile_x + (tile_y * tiles_per_row);
|
||||
@@ -791,22 +802,28 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
tile_to_paint = &flipped_tile;
|
||||
}
|
||||
|
||||
// Paint with 8x8 tile size at 4x scale (32 display pixels per 8x8 tile)
|
||||
// Always use the flipped tile for consistency between preview and actual drawing
|
||||
if (tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 8, 4.0F)) {
|
||||
ImVec2 click_pos = tile16_edit_canvas_.drawn_tile_position();
|
||||
// CRITICAL FIX: Handle tile painting with simple click instead of click+drag
|
||||
// Draw the preview first
|
||||
tile16_edit_canvas_.DrawTilePainter(*tile_to_paint, 8, 4.0F);
|
||||
|
||||
// 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>(click_pos.x / 4.0F);
|
||||
int tile_y = static_cast<int>(click_pos.y / 4.0F);
|
||||
int tile_x = static_cast<int>(mouse_pos.x / 4.0F);
|
||||
int tile_y = static_cast<int>(mouse_pos.y / 4.0F);
|
||||
|
||||
// Clamp to valid range
|
||||
tile_x = std::max(0, std::min(15, tile_x));
|
||||
tile_y = std::max(0, std::min(15, tile_y));
|
||||
|
||||
util::logf("Canvas click: (%.2f, %.2f) -> Tile16: (%d, %d)",
|
||||
click_pos.x, click_pos.y, tile_x, tile_y);
|
||||
util::logf("Tile16 canvas click: (%.2f, %.2f) -> Tile16: (%d, %d)",
|
||||
mouse_pos.x, mouse_pos.y, tile_x, tile_y);
|
||||
|
||||
// Pass the flipped tile if we created one, otherwise pass nullptr to use original with flips
|
||||
const gfx::Bitmap* tile_to_draw =
|
||||
|
||||
Reference in New Issue
Block a user