6#include "absl/strings/str_format.h"
25 if (!overworld || !overworld->
is_loaded()) {
26 return absl::FailedPreconditionError(
"Overworld not loaded");
38 auto& holes = overworld->
holes();
39 for (
size_t i = 0; i < holes.size(); ++i) {
40 if (holes[i].deleted) {
42 holes[i].deleted =
false;
43 holes[i].map_id_ = map_id;
44 holes[i].x_ =
static_cast<int>(snapped_pos.x);
45 holes[i].y_ =
static_cast<int>(snapped_pos.y);
46 holes[i].entrance_id_ = 0;
47 holes[i].is_hole_ =
true;
50 holes[i].UpdateMapProperties(map_id, overworld);
53 "Inserted hole at slot %zu: pos=(%d,%d) map=0x%02X", i,
54 holes[i].x_, holes[i].y_, map_id);
59 return absl::ResourceExhaustedError(
60 "No space available for new hole. Delete one first.");
65 for (
size_t i = 0; i < entrances->size(); ++i) {
66 if (entrances->at(i).deleted) {
68 entrances->at(i).deleted =
false;
69 entrances->at(i).map_id_ = map_id;
70 entrances->at(i).x_ =
static_cast<int>(snapped_pos.x);
71 entrances->at(i).y_ =
static_cast<int>(snapped_pos.y);
72 entrances->at(i).entrance_id_ = 0;
73 entrances->at(i).is_hole_ =
false;
76 entrances->at(i).UpdateMapProperties(map_id, overworld);
79 "Inserted entrance at slot %zu: pos=(%d,%d) map=0x%02X", i,
80 entrances->at(i).x_, entrances->at(i).y_, map_id);
82 return &entrances->at(i);
85 return absl::ResourceExhaustedError(
86 "No space available for new entrance. Delete one first.");
93 if (!overworld || !overworld->
is_loaded()) {
94 return absl::FailedPreconditionError(
"Overworld not loaded");
101 auto* current_ow_map = overworld->
overworld_map(current_map);
106 for (
size_t i = 0; i < exits.size(); ++i) {
107 if (exits[i].deleted_) {
109 exits[i].deleted_ =
false;
110 exits[i].map_id_ = map_id;
111 exits[i].x_ =
static_cast<int>(snapped_pos.x);
112 exits[i].y_ =
static_cast<int>(snapped_pos.y);
116 exits[i].room_id_ = 0;
117 exits[i].x_scroll_ = 0;
118 exits[i].y_scroll_ = 0;
119 exits[i].x_camera_ = 0;
120 exits[i].y_camera_ = 0;
121 exits[i].x_player_ =
static_cast<uint16_t
>(snapped_pos.x);
122 exits[i].y_player_ =
static_cast<uint16_t
>(snapped_pos.y);
123 exits[i].scroll_mod_x_ = 0;
124 exits[i].scroll_mod_y_ = 0;
125 exits[i].door_type_1_ = 0;
126 exits[i].door_type_2_ = 0;
129 exits[i].UpdateMapProperties(map_id, overworld);
132 "Inserted exit at slot %zu: pos=(%d,%d) map=0x%02X", i,
133 exits[i].x_, exits[i].y_, map_id);
139 return absl::ResourceExhaustedError(
140 "No space available for new exit. Delete one first.");
144 ImVec2 mouse_pos,
int current_map,
147 if (!overworld || !overworld->
is_loaded()) {
148 return absl::FailedPreconditionError(
"Overworld not loaded");
151 if (game_state < 0 || game_state > 2) {
152 return absl::InvalidArgumentError(
"Invalid game state (must be 0-2)");
160 auto* current_ow_map = overworld->
overworld_map(current_map);
165 int map_local_x =
static_cast<int>(snapped_pos.x) % 512;
166 int map_local_y =
static_cast<int>(snapped_pos.y) % 512;
169 uint8_t game_x =
static_cast<uint8_t
>(map_local_x / 16);
170 uint8_t game_y =
static_cast<uint8_t
>(map_local_y / 16);
177 current_ow_map->current_graphics(),
static_cast<uint8_t
>(map_id),
181 static_cast<int>(snapped_pos.x),
182 static_cast<int>(snapped_pos.y)
185 sprites.push_back(new_sprite);
192 "Inserted sprite at game_state=%d: pos=(%d,%d) map=0x%02X id=0x%02X",
193 game_state, inserted_sprite->
x_, inserted_sprite->
y_, map_id, sprite_id);
195 return inserted_sprite;
202 if (!overworld || !overworld->
is_loaded()) {
203 return absl::FailedPreconditionError(
"Overworld not loaded");
210 auto* current_ow_map = overworld->
overworld_map(current_map);
215 int fake_id = current_map % 0x40;
216 int sy = fake_id / 8;
217 int sx = fake_id - (sy * 8);
220 int map_local_x =
static_cast<int>(snapped_pos.x) % 512;
221 int map_local_y =
static_cast<int>(snapped_pos.y) % 512;
224 uint8_t game_x =
static_cast<uint8_t
>(map_local_x / 16);
225 uint8_t game_y =
static_cast<uint8_t
>(map_local_y / 16);
231 items.emplace_back(item_id,
232 static_cast<uint16_t
>(map_id),
233 static_cast<int>(snapped_pos.x),
234 static_cast<int>(snapped_pos.y),
240 inserted_item->
game_x_ = game_x;
241 inserted_item->
game_y_ = game_y;
244 "Inserted item: pos=(%d,%d) game=(%d,%d) map=0x%02X id=0x%02X",
245 inserted_item->
x_, inserted_item->
y_, game_x, game_y, map_id,
248 return inserted_item;
254 return absl::InvalidArgumentError(
"Overworld is null");
257 return absl::InvalidArgumentError(
"Item pointer is null");
261 auto it = std::find_if(items->begin(), items->end(),
263 return &item == item_ptr;
265 if (it == items->end()) {
266 return absl::NotFoundError(
"Item pointer not found in overworld item list");
270 return absl::OkStatus();
276 return absl::InvalidArgumentError(
"Overworld is null");
280 auto it = std::find_if(items->begin(), items->end(),
282 return MatchesItemIdentity(item, item_identity);
284 if (it == items->end()) {
285 return absl::NotFoundError(
"No matching item identity in overworld list");
289 return absl::OkStatus();
299 auto it = std::find_if(items->begin(), items->end(),
301 return !item.deleted &&
302 MatchesItemIdentity(item, item_identity);
304 if (it == items->end()) {
312 int offset_x,
int offset_y) {
314 return absl::InvalidArgumentError(
"Overworld is null");
319 return absl::NotFoundError(
"No matching item identity in overworld list");
324 ImVec2(
static_cast<float>(source_item->x_ + offset_x),
325 static_cast<float>(source_item->y_ + offset_y)));
326 duplicate.
x_ =
static_cast<int>(clamped_pos.x);
327 duplicate.
y_ =
static_cast<int>(clamped_pos.y);
332 items->push_back(duplicate);
333 return &items->back();
338 return absl::InvalidArgumentError(
"Item pointer is null");
340 const ImVec2 clamped_pos =
342 static_cast<float>(item->
y_ + delta_y)));
343 item->
x_ =
static_cast<int>(clamped_pos.x);
344 item->
y_ =
static_cast<int>(clamped_pos.y);
346 return absl::OkStatus();
357 if (!items || items->empty()) {
361 const int anchor_x = anchor_identity.
x_;
362 const int anchor_y = anchor_identity.
y_;
363 const uint16_t anchor_map = anchor_identity.
room_map_id_;
366 const int same_map_rank = (item.room_map_id_ == anchor_map) ? 0 : 1;
367 const std::int64_t dx =
static_cast<std::int64_t
>(item.x_) - anchor_x;
368 const std::int64_t dy =
static_cast<std::int64_t
>(item.y_) - anchor_y;
369 const std::int64_t dist2 = (dx * dx) + (dy * dy);
370 return std::pair<int, std::int64_t>(same_map_rank, dist2);
373 auto best_it = items->end();
374 std::pair<int, std::int64_t> best_rank = {2, 0};
375 for (
auto it = items->begin(); it != items->end(); ++it) {
379 const auto current_rank = rank(*it);
380 if (best_it == items->end() || current_rank < best_rank) {
382 best_rank = current_rank;
386 if (best_it == items->end()) {
void UpdateMapProperties(uint16_t room_map_id, const void *context=nullptr) override
Update entity properties based on map position.
Represents the full Overworld data, light and dark world.
const std::vector< OverworldEntrance > & holes() const
auto overworld_map(int i) const
auto mutable_sprites(int state)
A class for managing sprites in the overworld and underworld.
#define LOG_DEBUG(category, format,...)
bool MatchesItemIdentity(const zelda3::OverworldItem &lhs, const zelda3::OverworldItem &rhs)
absl::StatusOr< zelda3::OverworldItem * > InsertItem(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, uint8_t item_id)
Insert a new item at the specified position.
absl::Status RemoveItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity)
Remove an item by value identity instead of pointer identity.
absl::StatusOr< zelda3::OverworldEntrance * > InsertEntrance(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, bool is_hole)
Flat helper functions for entity insertion/manipulation.
absl::Status NudgeItem(zelda3::OverworldItem *item, int delta_x, int delta_y)
Move an item by pixel deltas with overworld bounds clamping.
zelda3::OverworldItem * FindNearestItemForSelection(zelda3::Overworld *overworld, const zelda3::OverworldItem &anchor_identity)
Find the best next item to keep selection continuity after deletion.
absl::Status RemoveItem(zelda3::Overworld *overworld, const zelda3::OverworldItem *item_ptr)
Remove an item from the overworld item list by pointer identity.
zelda3::OverworldItem * FindItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity)
Find a live item by value identity.
absl::StatusOr< zelda3::OverworldExit * > InsertExit(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map)
Insert a new exit at the specified position.
ImVec2 SnapToEntityGrid(ImVec2 pos)
Snap position to 16x16 grid (standard entity positioning)
absl::StatusOr< zelda3::OverworldItem * > DuplicateItemByIdentity(zelda3::Overworld *overworld, const zelda3::OverworldItem &item_identity, int offset_x, int offset_y)
Duplicate an existing item by identity with a positional offset.
absl::StatusOr< zelda3::Sprite * > InsertSprite(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, int game_state, uint8_t sprite_id)
Insert a new sprite at the specified position.
ImVec2 ClampToOverworldBounds(ImVec2 pos)
Clamp position to valid overworld bounds.
uint8_t GetParentMapId(const zelda3::OverworldMap *map, int current_map)
Helper to get parent map ID for multi-area maps.