yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_entrance_camera.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstddef>
5
6#include "absl/strings/str_format.h"
10
11namespace yaze::editor {
12
13namespace {
14
15constexpr int kSpecialQuadrantRoom = 0x104;
16
17int AlignDown8(int value) {
18 return value & ~0x07;
19}
20
21void RestoreDirtyState(bool dirty, zelda3::RoomEntrance* entrance) {
22 if (dirty) {
23 entrance->MarkDirty();
24 } else {
25 entrance->ClearDirty();
26 }
27}
28
30 if (dirty) {
31 spawn->MarkDirty();
32 } else {
33 spawn->ClearDirty();
34 }
35}
36
37} // namespace
38
40 uint16_t player_x,
41 uint16_t player_y) {
42 const int room_base_x = (room_id & 0x0F) * 0x200;
43 const int room_base_y = (room_id >> 4) * 0x200;
44
45 const int camera_x = std::clamp(AlignDown8(static_cast<int>(player_x) - 0x78),
46 room_base_x, room_base_x + 0x100);
47 const int camera_y = std::clamp(AlignDown8(static_cast<int>(player_y) - 0x68),
48 room_base_y + 0x10, room_base_y + 0x110);
49
50 const int page_x = camera_x >> 8;
51 const bool right_half = camera_x - room_base_x >= 0x100;
52 const int page_y = camera_y >> 8;
53 const bool lower_half = camera_y - room_base_y >= 0x100;
54
56 derived.camera_x = static_cast<uint16_t>(camera_x);
57 derived.camera_y = static_cast<uint16_t>(camera_y);
58 derived.trigger_x = static_cast<uint16_t>((camera_x - room_base_x) + 0x7F);
59 derived.trigger_y = static_cast<uint16_t>((camera_y - room_base_y) + 0x77);
60 derived.boundaries = {
61 static_cast<uint8_t>(page_y),
62 static_cast<uint8_t>(lower_half ? page_y - 1 : page_y),
63 static_cast<uint8_t>(page_y),
64 static_cast<uint8_t>(lower_half ? page_y : page_y + 1),
65 static_cast<uint8_t>(page_x),
66 static_cast<uint8_t>(right_half ? page_x - 1 : page_x),
67 static_cast<uint8_t>(page_x),
68 static_cast<uint8_t>(right_half ? page_x : page_x + 1),
69 };
70 const int local_x = static_cast<int>(player_x) - room_base_x;
71 const int local_y = static_cast<int>(player_y) - room_base_y;
72 derived.quadrant = static_cast<uint8_t>((local_x >= 0x100 ? 0x10 : 0) |
73 (local_y >= 0x100 ? 0x02 : 0));
74 return derived;
75}
76
78 const DungeonEntranceCameraState& state) {
80 validation.quadrant_repair_safe = state.room_id != kSpecialQuadrantRoom;
81 if (state.room_id < 0 || state.room_id >= zelda3::kRoomCensusRoomCount) {
82 validation.repair_source_valid = false;
83 validation.errors.push_back(absl::StrFormat(
84 "Room 0x%X is outside the dungeon room table.", state.room_id));
85 return validation;
86 }
87
88 const int room_base_x = (state.room_id & 0x0F) * 0x200;
89 const int room_base_y = (state.room_id >> 4) * 0x200;
90 const int player_x = state.player_x;
91 const int player_y = state.player_y;
92 if (player_x < room_base_x || player_x > room_base_x + 0x1FF) {
93 validation.repair_source_valid = false;
94 validation.errors.push_back(absl::StrFormat(
95 "Player X 0x%04X is outside room X range 0x%04X-0x%04X.", player_x,
96 room_base_x, room_base_x + 0x1FF));
97 }
98 if (player_y < room_base_y || player_y > room_base_y + 0x1FF) {
99 validation.repair_source_valid = false;
100 validation.errors.push_back(absl::StrFormat(
101 "Player Y 0x%04X is outside room Y range 0x%04X-0x%04X.", player_y,
102 room_base_y, room_base_y + 0x1FF));
103 }
104
105 if (state.camera_x < room_base_x || state.camera_x > room_base_x + 0x100) {
106 validation.errors.push_back(
107 absl::StrFormat("Camera X 0x%04X is outside room range 0x%04X-0x%04X.",
108 state.camera_x, room_base_x, room_base_x + 0x100));
109 }
110 if (state.camera_y < room_base_y + 0x10 ||
111 state.camera_y > room_base_y + 0x110) {
112 validation.errors.push_back(absl::StrFormat(
113 "Camera Y 0x%04X is outside room range 0x%04X-0x%04X.", state.camera_y,
114 room_base_y + 0x10, room_base_y + 0x110));
115 }
116 if ((state.camera_x & 0x07) != 0 || (state.camera_y & 0x07) != 0) {
117 validation.errors.push_back(
118 "Camera coordinates must be aligned to an 8-pixel boundary.");
119 }
120 if (state.trigger_x < 0x007F || state.trigger_x > 0x017F) {
121 validation.errors.push_back(absl::StrFormat(
122 "Camera trigger X 0x%04X is outside safe range 0x007F-0x017F.",
123 state.trigger_x));
124 }
125 if (state.trigger_y < 0x0087 || state.trigger_y > 0x0187) {
126 validation.errors.push_back(absl::StrFormat(
127 "Camera trigger Y 0x%04X is outside safe range 0x0087-0x0187.",
128 state.trigger_y));
129 }
130 for (size_t index = 0; index < 4; ++index) {
131 if (state.boundaries[index] > 0x25) {
132 validation.errors.push_back(
133 absl::StrFormat("Vertical boundary %zu (0x%02X) exceeds page 0x25.",
134 index, state.boundaries[index]));
135 }
136 }
137 for (size_t index = 4; index < state.boundaries.size(); ++index) {
138 if (state.boundaries[index] > 0x1F) {
139 validation.errors.push_back(
140 absl::StrFormat("Horizontal boundary %zu (0x%02X) exceeds page 0x1F.",
141 index, state.boundaries[index]));
142 }
143 }
144
146 state.room_id, state.player_x, state.player_y);
147 if (state.camera_x != validation.expected.camera_x ||
148 state.camera_y != validation.expected.camera_y) {
149 validation.has_repairable_difference = true;
150 validation.differences.push_back(absl::StrFormat(
151 "Camera scroll differs (current %04X,%04X; derived %04X,%04X).",
152 state.camera_x, state.camera_y, validation.expected.camera_x,
153 validation.expected.camera_y));
154 }
155 if (state.trigger_x != validation.expected.trigger_x ||
156 state.trigger_y != validation.expected.trigger_y) {
157 validation.has_repairable_difference = true;
158 validation.differences.push_back(absl::StrFormat(
159 "Camera triggers differ (current %04X,%04X; derived %04X,%04X).",
160 state.trigger_x, state.trigger_y, validation.expected.trigger_x,
161 validation.expected.trigger_y));
162 }
163 if (state.boundaries != validation.expected.boundaries) {
164 validation.has_repairable_difference = true;
165 validation.differences.push_back(
166 "Camera boundary pages differ from the player-derived values.");
167 }
168 if (state.quadrant != validation.expected.quadrant) {
169 validation.has_repairable_difference =
170 validation.has_repairable_difference || validation.quadrant_repair_safe;
171 validation.differences.push_back(
172 state.room_id == kSpecialQuadrantRoom
173 ? "Quadrant differs from the ordinary-room formula; room 0x104 "
174 "is a known vanilla exception and will be preserved."
175 : absl::StrFormat("Quadrant differs (current %02X; derived %02X).",
176 state.quadrant, validation.expected.quadrant));
177 }
178 return validation;
179}
180
182 const DungeonEntranceCameraState& state) {
183 DungeonEntranceCameraState repaired = state;
184 const auto derived = DeriveDungeonEntranceCamera(
185 state.room_id, state.player_x, state.player_y);
186 repaired.camera_x = derived.camera_x;
187 repaired.camera_y = derived.camera_y;
188 repaired.trigger_x = derived.trigger_x;
189 repaired.trigger_y = derived.trigger_y;
190 repaired.boundaries = derived.boundaries;
191 if (state.room_id != kSpecialQuadrantRoom) {
192 repaired.quadrant = derived.quadrant;
193 }
194 return repaired;
195}
196
198 const zelda3::RoomEntrance& entrance) {
199 return {
201 .room_id = entrance.room_,
202 .player_x = entrance.x_position_,
203 .player_y = entrance.y_position_,
204 .camera_x = entrance.camera_x_,
205 .camera_y = entrance.camera_y_,
206 .trigger_x = entrance.camera_trigger_x_,
207 .trigger_y = entrance.camera_trigger_y_,
208 .boundaries = {entrance.camera_boundary_qn_, entrance.camera_boundary_fn_,
209 entrance.camera_boundary_qs_, entrance.camera_boundary_fs_,
210 entrance.camera_boundary_qw_, entrance.camera_boundary_fw_,
211 entrance.camera_boundary_qe_,
212 entrance.camera_boundary_fe_},
213 .quadrant = entrance.scroll_quadrant_,
214 .dirty = entrance.dirty(),
215 };
216}
217
219 const zelda3::DungeonSpawnPoint& spawn) {
220 return {
222 .room_id = spawn.room_id,
223 .player_x = spawn.x_coordinate,
224 .player_y = spawn.y_coordinate,
225 .camera_x = spawn.horizontal_scroll,
226 .camera_y = spawn.vertical_scroll,
227 .trigger_x = spawn.camera_trigger_x,
228 .trigger_y = spawn.camera_trigger_y,
229 .boundaries = spawn.camera_scroll_boundaries,
230 .quadrant = spawn.quadrant,
231 .dirty = spawn.dirty(),
232 };
233}
234
236 zelda3::RoomEntrance* entrance) {
237 if (entrance == nullptr) {
238 return;
239 }
240 entrance->camera_x_ = state.camera_x;
241 entrance->camera_y_ = state.camera_y;
242 entrance->camera_trigger_x_ = state.trigger_x;
243 entrance->camera_trigger_y_ = state.trigger_y;
244 entrance->camera_boundary_qn_ = state.boundaries[0];
245 entrance->camera_boundary_fn_ = state.boundaries[1];
246 entrance->camera_boundary_qs_ = state.boundaries[2];
247 entrance->camera_boundary_fs_ = state.boundaries[3];
248 entrance->camera_boundary_qw_ = state.boundaries[4];
249 entrance->camera_boundary_fw_ = state.boundaries[5];
250 entrance->camera_boundary_qe_ = state.boundaries[6];
251 entrance->camera_boundary_fe_ = state.boundaries[7];
252 entrance->scroll_quadrant_ = state.quadrant;
253 RestoreDirtyState(state.dirty, entrance);
254}
255
258 if (spawn == nullptr) {
259 return;
260 }
261 spawn->horizontal_scroll = state.camera_x;
262 spawn->vertical_scroll = state.camera_y;
263 spawn->camera_trigger_x = state.trigger_x;
264 spawn->camera_trigger_y = state.trigger_y;
266 spawn->quadrant = state.quadrant;
267 RestoreDirtyState(state.dirty, spawn);
268}
269
270} // namespace yaze::editor
std::array< uint8_t, 8 > camera_scroll_boundaries
Dungeon Room Entrance or Spawn Point.
void RestoreDirtyState(bool dirty, zelda3::RoomEntrance *entrance)
Editors are the view controllers for the application.
DungeonEntranceCameraState CaptureDungeonSpawnCamera(const zelda3::DungeonSpawnPoint &spawn)
DungeonEntranceCameraState BuildRepairedDungeonEntranceCamera(const DungeonEntranceCameraState &state)
DungeonEntranceCameraDerived DeriveDungeonEntranceCamera(int room_id, uint16_t player_x, uint16_t player_y)
void ApplyDungeonSpawnCameraState(const DungeonEntranceCameraState &state, zelda3::DungeonSpawnPoint *spawn)
DungeonEntranceCameraValidation ValidateDungeonEntranceCamera(const DungeonEntranceCameraState &state)
DungeonEntranceCameraState CaptureDungeonEntranceCamera(const zelda3::RoomEntrance &entrance)
void ApplyDungeonEntranceCameraState(const DungeonEntranceCameraState &state, zelda3::RoomEntrance *entrance)
constexpr int kRoomCensusRoomCount
Definition room_census.h:55