2#define IMGUI_DEFINE_MATH_OPERATORS
9#include "absl/status/status.h"
14#include "imgui/imgui.h"
15#include "imgui/imgui_internal.h"
30 ImVec2 scaled_position) {
37 *ctx.
current_world, 0, scaled_position, ImVec2(0, 0), ImVec2(0, 0),
42 return target->map_id;
102 ImVec2 scaled_position) {
103 const auto hovered_map = MapFromCanvasPosition(
ctx_, scaled_position);
104 SetHoveredMap(
ctx_, hovered_map.value_or(-1));
107 ImGui::GetCurrentContext() ? ImGui::GetIO().MousePos : ImVec2(0, 0);
126 return absl::OkStatus();
133 SetHoveredMap(
ctx_, -1);
134 return absl::OkStatus();
137 const int large_map_size = 1024;
139 const auto hovered_map =
141 if (!hovered_map.has_value()) {
142 SetHoveredMap(
ctx_, -1);
143 return absl::OkStatus();
145 SetHoveredMap(
ctx_, *hovered_map);
156 bool should_build =
false;
164 ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
165 ImGui::IsMouseClicked(ImGuiMouseButton_Right);
179 if (current_highlighted_map < 0 ||
182 return absl::OkStatus();
187 bool use_v3_area_sizes =
191 if (use_v3_area_sizes) {
195 const int highlight_parent =
202 parent_map_x = highlight_parent % 8;
203 parent_map_y = highlight_parent / 8;
205 parent_map_x = (highlight_parent - 0x40) % 8;
206 parent_map_y = (highlight_parent - 0x40) / 8;
208 parent_map_x = (highlight_parent - 0x80) % 8;
209 parent_map_y = (highlight_parent - 0x80) / 8;
214 case AreaSizeEnum::LargeArea:
217 large_map_size, large_map_size);
219 case AreaSizeEnum::WideArea:
224 case AreaSizeEnum::TallArea:
229 case AreaSizeEnum::SmallArea:
242 const int highlight_parent =
248 parent_map_x = highlight_parent % 8;
249 parent_map_y = highlight_parent / 8;
251 parent_map_x = (highlight_parent - 0x40) % 8;
252 parent_map_y = (highlight_parent - 0x40) / 8;
254 parent_map_x = (highlight_parent - 0x80) % 8;
255 parent_map_y = (highlight_parent - 0x80) / 8;
260 large_map_size, large_map_size);
265 current_map_x = current_highlighted_map % 8;
266 current_map_y = current_highlighted_map / 8;
268 current_map_x = (current_highlighted_map - 0x40) % 8;
269 current_map_y = (current_highlighted_map - 0x40) / 8;
271 current_map_x = (current_highlighted_map - 0x80) % 8;
272 current_map_y = (current_highlighted_map - 0x80) / 8;
309 return absl::OkStatus();
317 switch (input.
mode) {
361 const ImGuiIO& io = ImGui::GetIO();
362 const float threshold = io.MouseDragThreshold;
370 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
377 input.
left_released = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
378 input.
left_dragged = io.MouseDragMaxDistanceSqr[ImGuiMouseButton_Left] >=
379 threshold * threshold;
383 input.
right_clicked = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
384 input.
shift = io.KeyShift;
399 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) &&
418 if (wheel.x == 0.0f && wheel.y == 0.0f) {
423 return ImVec2(0.0f, 0.0f);
427 const float snap = std::max(snap_px, 1.0f);
428 auto axis = [&](
float input,
float& rest) ->
float {
432 const float delta = -input * step_px;
433 if ((delta > 0.0f) != (rest > 0.0f) && rest != 0.0f) {
437 const float out = std::trunc(rest / snap) * snap;
446 if (old_scale <= 0.0f || new_scale <= 0.0f) {
449 const float ratio = new_scale / old_scale;
450 return ImVec2((scroll.x + anchor.x) * ratio - anchor.x,
451 (scroll.y + anchor.y) * ratio - anchor.y);
458 return ImVec2(kWorldSize * scale, kWorldSize * scale);
466 return ImVec2(std::round(std::clamp(scroll.x, 0.0f, max_x)),
467 std::round(std::clamp(scroll.y, 0.0f, max_y)));
476 if (new_scale == old_scale)
494 const ImGuiIO& io = ImGui::GetIO();
501 const bool over_entity =
503 const bool dragging_entity =
506 if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) {
509 }
else if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
511 !over_entity && !dragging_entity) {
533 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
539 const bool zoom_modifier =
542 ImVec2 wheel(io.MouseWheelH, io.MouseWheel);
544 wheel = ImVec2(wheel.y, 0.0f);
547 if (wheel.y != 0.0f) {
550 const int steps =
static_cast<int>(
570 if (delta.x != 0.0f || delta.y != 0.0f) {
572 target = ImVec2(base.x + delta.x, base.y + delta.y);
576 if (!zoom_modifier || io.MouseWheel == 0.0f) {
588 : ImVec2(std::max(0.0f, target->x),
589 std::max(0.0f, target->y));
590 ImGui::SetNextWindowScroll(clamped);
596 ImGuiWindow* window = ImGui::GetCurrentWindow();
622 ImGui::GetCurrentContext() ? ImGui::GetIO().MousePos : ImVec2(0, 0);
639 const bool special_world =
642 const float world_height = special_world ? kWorldSize * 0.5f : kWorldSize;
679 const int map_in_world = map_id % 0x40;
680 const float center_x =
683 const float center_y =
696 ImVec2((world_x * 16.0f + 8.0f) * scale -
viewport_size_.x * 0.5f,
749 int world_offset = (center_map / 64) * 64;
750 int local_index = center_map % 64;
751 int map_x = local_index % 8;
752 int map_y = local_index / 8;
759 static const int dx[] = {-1, 1, 0, 0};
760 static const int dy[] = {0, 0, -1, 1};
762 for (
int i = 0; i < 4; ++i) {
763 int nx = map_x + dx[i];
764 int ny = map_y + dy[i];
767 if (nx >= 0 && nx < 8 && ny >= 0 && ny < max_rows) {
768 int neighbor_index = world_offset + ny * 8 + nx;
770 if (neighbor_index >= 0 &&
771 neighbor_index < ctx_.overworld->inspected_map_count() &&
800 "Background preload of map %d failed: %s", map_to_preload,
801 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)
bool is_lyra_preview() const
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,...)
void SelectMapForEditing(const CanvasNavigationContext &ctx, const CanvasNavigationCallbacks &callbacks, int map_id, bool respect_pin)
void SetHoveredMap(const CanvasNavigationContext &ctx, int map_id)
std::optional< int > MapFromCanvasPosition(const CanvasNavigationContext &ctx, ImVec2 scaled_position)
void SelectMapFallback(const CanvasNavigationContext &ctx, int map_id, bool respect_pin)
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< 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.