2#define IMGUI_DEFINE_MATH_OPERATORS
9#include "absl/status/status.h"
14#include "imgui/imgui.h"
15#include "imgui/imgui_internal.h"
52 *ctx.
current_world, 0, scaled_position, ImVec2(0, 0), ImVec2(0, 0),
58 return target->map_id;
82 if (!project_terrain && !map) {
88 if (project_terrain) {
123 ImVec2 scaled_position) {
124 const auto hovered_map =
126 SetHoveredMap(
ctx_, hovered_map.value_or(-1));
129 ImGui::GetCurrentContext() ? ImGui::GetIO().MousePos : ImVec2(0, 0);
148 return absl::OkStatus();
155 SetHoveredMap(
ctx_, -1);
156 return absl::OkStatus();
159 const int large_map_size = 1024;
161 const auto hovered_map =
163 if (!hovered_map.has_value()) {
164 SetHoveredMap(
ctx_, -1);
165 return absl::OkStatus();
167 SetHoveredMap(
ctx_, *hovered_map);
178 const bool hovered_project_terrain =
179 IsProjectTerrainMap(
callbacks_, *hovered_map);
181 bool should_build =
false;
185 should_build = hovered_model && hovered_model->is_built();
189 ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
190 ImGui::IsMouseClicked(ImGuiMouseButton_Right);
193 if (!hovered_project_terrain && hovered_model && should_build) {
205 if (current_highlighted_map < 0 ||
207 return absl::OkStatus();
209 if (IsProjectTerrainMap(
callbacks_, current_highlighted_map)) {
210 const int local_map = current_highlighted_map % 0x40;
214 if (*hovered_map >= 0 && *hovered_map != current_highlighted_map) {
215 const int h_local = *hovered_map % 0x40;
223 return absl::OkStatus();
226 return absl::OkStatus();
231 bool use_v3_area_sizes =
235 if (use_v3_area_sizes) {
239 const int highlight_parent =
246 parent_map_x = highlight_parent % 8;
247 parent_map_y = highlight_parent / 8;
249 parent_map_x = (highlight_parent - 0x40) % 8;
250 parent_map_y = (highlight_parent - 0x40) / 8;
252 parent_map_x = (highlight_parent - 0x80) % 8;
253 parent_map_y = (highlight_parent - 0x80) / 8;
258 case AreaSizeEnum::LargeArea:
261 large_map_size, large_map_size);
263 case AreaSizeEnum::WideArea:
268 case AreaSizeEnum::TallArea:
273 case AreaSizeEnum::SmallArea:
286 const int highlight_parent =
292 parent_map_x = highlight_parent % 8;
293 parent_map_y = highlight_parent / 8;
295 parent_map_x = (highlight_parent - 0x40) % 8;
296 parent_map_y = (highlight_parent - 0x40) / 8;
298 parent_map_x = (highlight_parent - 0x80) % 8;
299 parent_map_y = (highlight_parent - 0x80) / 8;
304 large_map_size, large_map_size);
309 current_map_x = current_highlighted_map % 8;
310 current_map_y = current_highlighted_map / 8;
312 current_map_x = (current_highlighted_map - 0x40) % 8;
313 current_map_y = (current_highlighted_map - 0x40) / 8;
315 current_map_x = (current_highlighted_map - 0x80) % 8;
316 current_map_y = (current_highlighted_map - 0x80) / 8;
353 return absl::OkStatus();
361 switch (input.
mode) {
409 const ImGuiIO& io = ImGui::GetIO();
410 const float threshold = io.MouseDragThreshold;
418 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
425 input.
left_released = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
426 input.
left_dragged = io.MouseDragMaxDistanceSqr[ImGuiMouseButton_Left] >=
427 threshold * threshold;
431 input.
right_clicked = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
432 input.
shift = io.KeyShift;
447 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) &&
466 if (wheel.x == 0.0f && wheel.y == 0.0f) {
471 return ImVec2(0.0f, 0.0f);
475 const float snap = std::max(snap_px, 1.0f);
476 auto axis = [&](
float input,
float& rest) ->
float {
480 const float delta = -input * step_px;
481 if ((delta > 0.0f) != (rest > 0.0f) && rest != 0.0f) {
485 const float out = std::trunc(rest / snap) * snap;
494 if (old_scale <= 0.0f || new_scale <= 0.0f) {
497 const float ratio = new_scale / old_scale;
498 return ImVec2((scroll.x + anchor.x) * ratio - anchor.x,
499 (scroll.y + anchor.y) * ratio - anchor.y);
506 return ImVec2(kWorldSize * scale, kWorldSize * scale);
514 return ImVec2(std::round(std::clamp(scroll.x, 0.0f, max_x)),
515 std::round(std::clamp(scroll.y, 0.0f, max_y)));
524 if (new_scale == old_scale)
542 const ImGuiIO& io = ImGui::GetIO();
549 const bool over_entity =
551 const bool dragging_entity =
554 if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) {
557 }
else if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
559 !over_entity && !dragging_entity) {
581 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
587 const bool zoom_modifier =
590 ImVec2 wheel(io.MouseWheelH, io.MouseWheel);
592 wheel = ImVec2(wheel.y, 0.0f);
595 if (wheel.y != 0.0f) {
598 const int steps =
static_cast<int>(
618 if (delta.x != 0.0f || delta.y != 0.0f) {
620 target = ImVec2(base.x + delta.x, base.y + delta.y);
624 if (!zoom_modifier || io.MouseWheel == 0.0f) {
636 : ImVec2(std::max(0.0f, target->x),
637 std::max(0.0f, target->y));
638 ImGui::SetNextWindowScroll(clamped);
644 ImGuiWindow* window = ImGui::GetCurrentWindow();
670 ImGui::GetCurrentContext() ? ImGui::GetIO().MousePos : ImVec2(0, 0);
687 const int world_maps =
688 std::clamp(DisplayedMapCount(
ctx_,
callbacks_) - world * 0x40, 1, 64);
725 const int map_in_world = map_id % 0x40;
726 const float center_x =
729 const float center_y =
742 ImVec2((world_x * 16.0f + 8.0f) * scale -
viewport_size_.x * 0.5f,
791 IsProjectTerrainMap(
callbacks_, center_map)) {
798 int world_offset = (center_map / 64) * 64;
799 int local_index = center_map % 64;
800 int map_x = local_index % 8;
801 int map_y = local_index / 8;
809 static const int dx[] = {-1, 1, 0, 0};
810 static const int dy[] = {0, 0, -1, 1};
812 for (
int i = 0; i < 4; ++i) {
813 int nx = map_x + dx[i];
814 int ny = map_y + dy[i];
817 if (nx >= 0 && nx < 8 && ny >= 0 && ny < max_rows) {
818 int neighbor_index = world_offset + ny * 8 + nx;
820 if (neighbor_index >= 0 &&
821 neighbor_index < ctx_.overworld->inspected_map_count() &&
822 !IsProjectTerrainMap(
callbacks_, neighbor_index) &&
846 if (IsProjectTerrainMap(
callbacks_, map_to_preload) ||
856 "Background preload of map %d failed: %s", map_to_preload,
857 status.message().data());
void ScrollBlocksetCanvasToCurrentTile()
Scroll the blockset (tile16 selector) to show the currently selected tile16.
void SuspendHoverFollowUntilMouseMoves()
ImVec2 pan_anchor_scroll_
std::optional< int > MapUnderCursor() const
static constexpr float kHoverBuildDelay
absl::Status CheckForCurrentMap()
Detect which map the mouse is over, trigger lazy loading, draw the selection outline,...
void CenterOnMap(int map_id)
Center the viewport on map_id (same world grid).
void UpdateBlocksetSelectorState()
Push current tile count and selection into the blockset widget.
void ProcessPreloadQueue()
Process one map from the preload queue (call once per frame).
std::optional< int > pending_center_map_
void SetScaleAboutAnchor(float new_scale, ImVec2 anchor)
std::optional< int > TrackMapAtCanvasPosition(ImVec2 scaled_position)
void CenterOnWorldTile(int world_x, int world_y)
CanvasNavigationCallbacks callbacks_
CanvasNavigationContext ctx_
bool canvas_window_hovered_
std::optional< ImVec2 > pending_scroll_
bool canvas_item_hovered_
void QueueAdjacentMapsForPreload(int center_map)
Queue the 4-connected neighbors of center_map for lazy build.
static constexpr float kPreloadStartDelay
std::vector< int > preload_queue_
void Initialize(const CanvasNavigationContext &context, const CanvasNavigationCallbacks &callbacks)
Initialize with shared state and callbacks.
void HandleMapInteraction()
Click selection (see IsMapSelectClick) and double-click to open properties. The paint-tool right-clic...
void ZoomToFit()
Scale so the whole current world fits the viewport.
void ZoomOut()
Decrease canvas zoom by one step, keeping the view center fixed.
void CenterOverworldView()
Center the viewport on the current map.
std::optional< ImVec2 > hover_follow_suspended_at_
std::optional< int > left_press_map_
ImVec2 ContentSize() const
StickyWheelPan wheel_pan_
void ZoomBySteps(int steps, ImVec2 anchor)
bool SelectMapUnderCursor()
Make the map under the cursor the current map, ignoring the pin (the pin, if on, then holds this map)...
void BeginCanvasViewport()
void EndCanvasViewport(bool canvas_item_hovered)
ImVec2 ClampScroll(ImVec2 scroll) const
void ZoomIn()
Increase canvas zoom by one step, keeping the view center fixed.
void ResetOverworldView()
Reset scroll to top-left and scale to 1.0.
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
auto global_scale() const
auto hover_mouse_pos() const
void set_global_scale(float scale)
bool IsMouseHovering() const
void DrawOutline(int x, int y, int w, int h)
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
void set_current_world(int world)
int inspected_map_count() const
auto overworld_map(int i) const
void set_current_map(int i)
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
#define LOG_DEBUG(category, format,...)
std::optional< int > MapFromCanvasPosition(const CanvasNavigationContext &ctx, const CanvasNavigationCallbacks &callbacks, ImVec2 scaled_position)
void SelectMapForEditing(const CanvasNavigationContext &ctx, const CanvasNavigationCallbacks &callbacks, int map_id, bool respect_pin)
void SetHoveredMap(const CanvasNavigationContext &ctx, int map_id)
void SelectMapFallback(const CanvasNavigationContext &ctx, const CanvasNavigationCallbacks &callbacks, int map_id, bool respect_pin)
int DisplayedMapCount(const CanvasNavigationContext &ctx, const CanvasNavigationCallbacks &callbacks)
bool IsProjectTerrainMap(const CanvasNavigationCallbacks &callbacks, int map_id)
Editors are the view controllers for the application.
constexpr float kOverworldWheelPanPx
constexpr float kOverworldWheelIdleResetSec
constexpr unsigned int kOverworldMapSize
constexpr float kOverworldMaxZoom
constexpr float kOverworldMinZoom
constexpr float kOverworldWheelDeadzone
std::string FormatOverworldMapStatusSegment(int current_map, int hovered_map)
constexpr float kOverworldZoomStep
constexpr float kOverworldWheelZoomUnitsPerStep
constexpr float kOverworldPanSnapMapPx
std::optional< OverworldContextTarget > ResolveOverworldContextTarget(int world, int game_state, ImVec2 screen_position, ImVec2 canvas_origin, ImVec2 scrolling, float scale, int map_count=zelda3::kNumOverworldMaps)
ImVec2 ScrollForZoomAtAnchor(ImVec2 scroll, ImVec2 anchor, float old_scale, float new_scale)
bool IsMapSelectClick(const MapClickInput &input)
bool IsMacPlatform()
Check if running on macOS (native or web)
constexpr int kNumTile16Individual
constexpr int kSpecialWorldMapIdStart
constexpr int kNumOverworldMaps
AreaSizeEnum
Area size enumeration for v3+ ROMs.
#define RETURN_IF_ERROR(expr)
Callbacks for operations that remain in the OverworldEditor.
std::function< void()> refresh_overworld_map
std::function< absl::Status()> refresh_tile16_blockset
std::function< void(int, bool)> select_map_for_editing
std::function< bool()> is_entity_hovered
Returns true if an entity is currently hovered (for pan suppression).
std::function< int()> displayed_map_count
std::function< bool(int)> is_project_terrain_map
std::function< void(int)> ensure_map_texture
std::function< void()> open_map_properties
Opens (and focuses) the Map Properties window. Double-click target.
Shared state pointers that the navigation manager reads/writes.
std::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
std::unique_ptr< gui::TileSelectorWidget > * blockset_selector
gui::Canvas * ow_map_canvas
zelda3::Overworld * overworld
bool * is_dragging_entity
gfx::Tilemap * tile16_blockset
EditingMode * current_mode
ImVec2 Consume(ImVec2 wheel, float dt, float step_px, float snap_px)
Bitmap atlas
Master bitmap containing all tiles.