yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
entity_operations.cc
Go to the documentation of this file.
1#include "entity_operations.h"
2
3#include "absl/strings/str_format.h"
4#include "util/log.h"
5
6namespace yaze {
7namespace editor {
8
9absl::StatusOr<zelda3::OverworldEntrance*> InsertEntrance(
10 zelda3::Overworld* overworld, ImVec2 mouse_pos, int current_map,
11 bool is_hole) {
12 if (!overworld || !overworld->is_loaded()) {
13 return absl::FailedPreconditionError("Overworld not loaded");
14 }
15
16 // Snap to 16x16 grid and clamp to bounds (ZScream: EntranceMode.cs:86-87)
17 ImVec2 snapped_pos = ClampToOverworldBounds(SnapToEntityGrid(mouse_pos));
18
19 // Get parent map ID (ZScream: EntranceMode.cs:78-82)
20 auto* current_ow_map = overworld->overworld_map(current_map);
21 uint8_t map_id = GetParentMapId(current_ow_map, current_map);
22
23 if (is_hole) {
24 // Search for first deleted hole slot (ZScream: EntranceMode.cs:74-100)
25 auto& holes = overworld->holes();
26 for (size_t i = 0; i < holes.size(); ++i) {
27 if (holes[i].deleted) {
28 // Reuse deleted slot
29 holes[i].deleted = false;
30 holes[i].map_id_ = map_id;
31 holes[i].x_ = static_cast<int>(snapped_pos.x);
32 holes[i].y_ = static_cast<int>(snapped_pos.y);
33 holes[i].entrance_id_ = 0; // Default, user configures in popup
34 holes[i].is_hole_ = true;
35
36 // Update map properties (ZScream: EntranceMode.cs:90)
37 holes[i].UpdateMapProperties(map_id, overworld);
38
39 LOG_DEBUG("EntityOps",
40 "Inserted hole at slot %zu: pos=(%d,%d) map=0x%02X", i,
41 holes[i].x_, holes[i].y_, map_id);
42
43 return &holes[i];
44 }
45 }
46 return absl::ResourceExhaustedError(
47 "No space available for new hole. Delete one first.");
48
49 } else {
50 // Search for first deleted entrance slot (ZScream: EntranceMode.cs:104-130)
51 auto* entrances = overworld->mutable_entrances();
52 for (size_t i = 0; i < entrances->size(); ++i) {
53 if (entrances->at(i).deleted) {
54 // Reuse deleted slot
55 entrances->at(i).deleted = false;
56 entrances->at(i).map_id_ = map_id;
57 entrances->at(i).x_ = static_cast<int>(snapped_pos.x);
58 entrances->at(i).y_ = static_cast<int>(snapped_pos.y);
59 entrances->at(i).entrance_id_ = 0; // Default, user configures in popup
60 entrances->at(i).is_hole_ = false;
61
62 // Update map properties (ZScream: EntranceMode.cs:120)
63 entrances->at(i).UpdateMapProperties(map_id, overworld);
64
65 LOG_DEBUG("EntityOps",
66 "Inserted entrance at slot %zu: pos=(%d,%d) map=0x%02X", i,
67 entrances->at(i).x_, entrances->at(i).y_, map_id);
68
69 return &entrances->at(i);
70 }
71 }
72 return absl::ResourceExhaustedError(
73 "No space available for new entrance. Delete one first.");
74 }
75}
76
77absl::StatusOr<zelda3::OverworldExit*> InsertExit(zelda3::Overworld* overworld,
78 ImVec2 mouse_pos,
79 int current_map) {
80 if (!overworld || !overworld->is_loaded()) {
81 return absl::FailedPreconditionError("Overworld not loaded");
82 }
83
84 // Snap to 16x16 grid and clamp to bounds (ZScream: ExitMode.cs:71-72)
85 ImVec2 snapped_pos = ClampToOverworldBounds(SnapToEntityGrid(mouse_pos));
86
87 // Get parent map ID (ZScream: ExitMode.cs:63-67)
88 auto* current_ow_map = overworld->overworld_map(current_map);
89 uint8_t map_id = GetParentMapId(current_ow_map, current_map);
90
91 // Search for first deleted exit slot (ZScream: ExitMode.cs:59-124)
92 auto& exits = *overworld->mutable_exits();
93 for (size_t i = 0; i < exits.size(); ++i) {
94 if (exits[i].deleted_) {
95 // Reuse deleted slot
96 exits[i].deleted_ = false;
97 exits[i].map_id_ = map_id;
98 exits[i].x_ = static_cast<int>(snapped_pos.x);
99 exits[i].y_ = static_cast<int>(snapped_pos.y);
100
101 // Initialize with default values (ZScream: ExitMode.cs:95-112)
102 // User will configure room_id, scroll, camera in popup
103 exits[i].room_id_ = 0;
104 exits[i].x_scroll_ = 0;
105 exits[i].y_scroll_ = 0;
106 exits[i].x_camera_ = 0;
107 exits[i].y_camera_ = 0;
108 exits[i].x_player_ = static_cast<uint16_t>(snapped_pos.x);
109 exits[i].y_player_ = static_cast<uint16_t>(snapped_pos.y);
110 exits[i].scroll_mod_x_ = 0;
111 exits[i].scroll_mod_y_ = 0;
112 exits[i].door_type_1_ = 0;
113 exits[i].door_type_2_ = 0;
114
115 // Update map properties with overworld context for area size detection
116 exits[i].UpdateMapProperties(map_id, overworld);
117
118 LOG_DEBUG("EntityOps",
119 "Inserted exit at slot %zu: pos=(%d,%d) map=0x%02X", i,
120 exits[i].x_, exits[i].y_, map_id);
121
122 return &exits[i];
123 }
124 }
125
126 return absl::ResourceExhaustedError(
127 "No space available for new exit. Delete one first.");
128}
129
130absl::StatusOr<zelda3::Sprite*> InsertSprite(zelda3::Overworld* overworld,
131 ImVec2 mouse_pos, int current_map,
132 int game_state,
133 uint8_t sprite_id) {
134 if (!overworld || !overworld->is_loaded()) {
135 return absl::FailedPreconditionError("Overworld not loaded");
136 }
137
138 if (game_state < 0 || game_state > 2) {
139 return absl::InvalidArgumentError("Invalid game state (must be 0-2)");
140 }
141
142 // Snap to 16x16 grid and clamp to bounds (ZScream: SpriteMode.cs similar
143 // logic)
144 ImVec2 snapped_pos = ClampToOverworldBounds(SnapToEntityGrid(mouse_pos));
145
146 // Get parent map ID (ZScream: SpriteMode.cs:90-95)
147 auto* current_ow_map = overworld->overworld_map(current_map);
148 uint8_t map_id = GetParentMapId(current_ow_map, current_map);
149
150 // Calculate map position (ZScream uses mapHover for parent tracking)
151 // For sprites, we need the actual map coordinates within the 512x512 map
152 int map_local_x = static_cast<int>(snapped_pos.x) % 512;
153 int map_local_y = static_cast<int>(snapped_pos.y) % 512;
154
155 // Convert to game coordinates (0-63 for X/Y within map)
156 uint8_t game_x = static_cast<uint8_t>(map_local_x / 16);
157 uint8_t game_y = static_cast<uint8_t>(map_local_y / 16);
158
159 // Add new sprite to the game state array (ZScream: SpriteMode.cs:34-35)
160 auto& sprites = *overworld->mutable_sprites(game_state);
161
162 // Create new sprite
163 zelda3::Sprite new_sprite(
164 current_ow_map->current_graphics(), static_cast<uint8_t>(map_id),
165 sprite_id, // Sprite ID (user will configure in popup)
166 game_x, // X position in map coordinates
167 game_y, // Y position in map coordinates
168 static_cast<int>(snapped_pos.x), // Real X (world coordinates)
169 static_cast<int>(snapped_pos.y) // Real Y (world coordinates)
170 );
171
172 sprites.push_back(new_sprite);
173
174 // Return pointer to the newly added sprite
175 zelda3::Sprite* inserted_sprite = &sprites.back();
176
177 LOG_DEBUG(
178 "EntityOps",
179 "Inserted sprite at game_state=%d: pos=(%d,%d) map=0x%02X id=0x%02X",
180 game_state, inserted_sprite->x_, inserted_sprite->y_, map_id, sprite_id);
181
182 return inserted_sprite;
183}
184
185absl::StatusOr<zelda3::OverworldItem*> InsertItem(zelda3::Overworld* overworld,
186 ImVec2 mouse_pos,
187 int current_map,
188 uint8_t item_id) {
189 if (!overworld || !overworld->is_loaded()) {
190 return absl::FailedPreconditionError("Overworld not loaded");
191 }
192
193 // Snap to 16x16 grid and clamp to bounds (ZScream: ItemMode.cs similar logic)
194 ImVec2 snapped_pos = ClampToOverworldBounds(SnapToEntityGrid(mouse_pos));
195
196 // Get parent map ID (ZScream: ItemMode.cs:60-64)
197 auto* current_ow_map = overworld->overworld_map(current_map);
198 uint8_t map_id = GetParentMapId(current_ow_map, current_map);
199
200 // Calculate game coordinates (0-63 for X/Y within map)
201 // Following LoadItems logic in overworld.cc:840-854
202 int fake_id = current_map % 0x40;
203 int sy = fake_id / 8;
204 int sx = fake_id - (sy * 8);
205
206 // Calculate map-local coordinates
207 int map_local_x = static_cast<int>(snapped_pos.x) % 512;
208 int map_local_y = static_cast<int>(snapped_pos.y) % 512;
209
210 // Game coordinates (0-63 range)
211 uint8_t game_x = static_cast<uint8_t>(map_local_x / 16);
212 uint8_t game_y = static_cast<uint8_t>(map_local_y / 16);
213
214 // Add new item to the all_items array (ZScream: ItemMode.cs:92-108)
215 auto& items = *overworld->mutable_all_items();
216
217 // Create new item with calculated coordinates
218 items.emplace_back(item_id, // Item ID
219 static_cast<uint16_t>(map_id), // Room map ID
220 static_cast<int>(snapped_pos.x), // X (world coordinates)
221 static_cast<int>(snapped_pos.y), // Y (world coordinates)
222 false // Not deleted
223 );
224
225 // Set game coordinates
226 zelda3::OverworldItem* inserted_item = &items.back();
227 inserted_item->game_x_ = game_x;
228 inserted_item->game_y_ = game_y;
229
230 LOG_DEBUG("EntityOps",
231 "Inserted item: pos=(%d,%d) game=(%d,%d) map=0x%02X id=0x%02X",
232 inserted_item->x_, inserted_item->y_, game_x, game_y, map_id,
233 item_id);
234
235 return inserted_item;
236}
237
238} // namespace editor
239} // namespace yaze
Represents the full Overworld data, light and dark world.
Definition overworld.h:217
const std::vector< OverworldEntrance > & holes() const
Definition overworld.h:507
auto is_loaded() const
Definition overworld.h:528
auto overworld_map(int i) const
Definition overworld.h:473
auto mutable_sprites(int state)
Definition overworld.h:494
A class for managing sprites in the overworld and underworld.
Definition sprite.h:35
#define LOG_DEBUG(category, format,...)
Definition log.h:103
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::StatusOr< zelda3::OverworldEntrance * > InsertEntrance(zelda3::Overworld *overworld, ImVec2 mouse_pos, int current_map, bool is_hole)
Flat helper functions for entity insertion/manipulation.
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::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.