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