yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_exit.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <cstdlib>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/status/statusor.h"
10#include "rom/rom.h"
11#include "util/macro.h"
12#include "zelda3/common.h"
15
16namespace yaze::zelda3 {
17
18absl::StatusOr<std::vector<OverworldExit>> LoadExits(Rom* rom) {
19 const int NumberOfOverworldExits = 0x4F;
20 std::vector<OverworldExit> exits;
21 for (int i = 0; i < NumberOfOverworldExits; i++) {
22 const auto* rom_data = rom->data();
23
24 uint16_t exit_room_id;
25 uint16_t exit_map_id;
26 uint16_t exit_vram;
27 uint16_t exit_y_scroll;
28 uint16_t exit_x_scroll;
29 uint16_t exit_y_player;
30 uint16_t exit_x_player;
31 uint16_t exit_y_camera;
32 uint16_t exit_x_camera;
33 uint16_t exit_scroll_mod_y;
34 uint16_t exit_scroll_mod_x;
35 uint16_t exit_door_type_1;
36 uint16_t exit_door_type_2;
38 exit_room_id, (OWExitRoomId + (i * 2)), exit_map_id, OWExitMapId + i,
39 exit_vram, OWExitVram + (i * 2), exit_y_scroll, OWExitYScroll + (i * 2),
40 exit_x_scroll, OWExitXScroll + (i * 2), exit_y_player,
41 OWExitYPlayer + (i * 2), exit_x_player, OWExitXPlayer + (i * 2),
42 exit_y_camera, OWExitYCamera + (i * 2), exit_x_camera,
43 OWExitXCamera + (i * 2), exit_scroll_mod_y, OWExitUnk1 + i,
44 exit_scroll_mod_x, OWExitUnk2 + i, exit_door_type_1,
45 OWExitDoorType1 + (i * 2), exit_door_type_2,
46 OWExitDoorType2 + (i * 2)));
47
48 uint16_t player_y =
49 static_cast<uint16_t>((rom_data[OWExitYPlayer + (i * 2) + 1] << 8) +
50 rom_data[OWExitYPlayer + (i * 2)]);
51 uint16_t player_x =
52 static_cast<uint16_t>((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) +
53 rom_data[OWExitXPlayer + (i * 2)]);
54
55 exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll,
56 exit_x_scroll, player_y, player_x, exit_y_camera,
57 exit_x_camera, exit_scroll_mod_y, exit_scroll_mod_x,
58 exit_door_type_1, exit_door_type_2,
59 (player_x & player_y) == 0xFFFF);
60 }
61 return exits;
62}
63
64void OverworldExit::UpdateMapProperties(uint16_t map_id, const void* context) {
65 // Sync player position from drag system
66 // ZScream: ExitMode.cs:229-244 updates PlayerX/PlayerY, then calls
67 // UpdateMapStuff
68 x_player_ = static_cast<uint16_t>(x_);
69 y_player_ = static_cast<uint16_t>(y_);
70 map_id_ = map_id;
71
72 // FIX Bug 3: Query actual area size from overworld
73 // ZScream: ExitOW.cs:210-212
74 int area_size_x = 256;
75 int area_size_y = 256;
76
77 if (context != nullptr) {
78 const auto* overworld = static_cast<const Overworld*>(context);
79 auto area_size = overworld->overworld_map(map_id)->area_size();
80
81 // Calculate area dimensions based on size enum
82 if (area_size == AreaSizeEnum::LargeArea) {
83 area_size_x = area_size_y = 768;
84 } else if (area_size == AreaSizeEnum::WideArea) {
85 area_size_x = 768;
86 area_size_y = 256;
87 } else if (area_size == AreaSizeEnum::TallArea) {
88 area_size_x = 256;
89 area_size_y = 768;
90 }
91 }
92
93 // FIX Bug 5: Normalize map_id FIRST before using for calculations
94 // ZScream: ExitOW.cs:214
95 uint8_t normalized_map_id = map_id % 0x40;
96
97 // Calculate map grid position
98 // ZScream: ExitOW.cs:216-217
99 int mapX = normalized_map_id % 8;
100 int mapY = normalized_map_id / 8;
101
102 // Calculate game coordinates (map-local tile position)
103 // ZScream: ExitOW.cs:219-220
104 game_x_ = static_cast<int>((std::abs(x_ - (mapX * 512)) / 16));
105 game_y_ = static_cast<int>((std::abs(y_ - (mapY * 512)) / 16));
106
107 // Clamp to valid range based on area size
108 // ZScream: ExitOW.cs:222-234
109 int max_game_x = (area_size_x == 256) ? 31 : 63;
110 int max_game_y = (area_size_y == 256) ? 31 : 63;
111 game_x_ = std::clamp(game_x_, 0, max_game_x);
112 game_y_ = std::clamp(game_y_, 0, max_game_y);
113
114 // Map base coordinates in world space
115 // ZScream: ExitOW.cs:237-238 (mapx, mapy)
116 int mapx = (normalized_map_id & 7) << 9; // * 512
117 int mapy = (normalized_map_id & 56) << 6; // (map_id / 8) * 512
118
119 if (is_automatic_) {
120 // Auto-calculate scroll and camera from player position
121 // ZScream: ExitOW.cs:256-309
122
123 // Base scroll calculation (player centered in screen)
124 x_scroll_ = x_player_ - 120;
125 y_scroll_ = y_player_ - 80;
126
127 // Clamp scroll to map bounds using actual area size
128 if (x_scroll_ < mapx) {
129 x_scroll_ = mapx;
130 }
131
132 if (y_scroll_ < mapy) {
133 y_scroll_ = mapy;
134 }
135
136 if (x_scroll_ > mapx + area_size_x) {
137 x_scroll_ = mapx + area_size_x;
138 }
139
140 if (y_scroll_ > mapy + area_size_y + 32) {
141 y_scroll_ = mapy + area_size_y + 32;
142 }
143
144 // Camera position (offset from player)
145 x_camera_ = x_player_ + 0x07;
146 y_camera_ = y_player_ + 0x1F;
147
148 // Clamp camera to valid range
149 if (x_camera_ < mapx + 127) {
150 x_camera_ = mapx + 127;
151 }
152
153 if (y_camera_ < mapy + 111) {
154 y_camera_ = mapy + 111;
155 }
156
157 if (x_camera_ > mapx + 127 + area_size_x) {
158 x_camera_ = mapx + 127 + area_size_x;
159 }
160
161 if (y_camera_ > mapy + 143 + area_size_y) {
162 y_camera_ = mapy + 143 + area_size_y;
163 }
164 }
165
166 // Calculate VRAM location from scroll values
167 // ZScream: ExitOW.cs:312-315
168 int16_t vram_x_scroll = static_cast<int16_t>(x_scroll_ - mapx);
169 int16_t vram_y_scroll = static_cast<int16_t>(y_scroll_ - mapy);
170
171 map_pos_ = static_cast<uint16_t>(((vram_y_scroll & 0xFFF0) << 3) |
172 ((vram_x_scroll & 0xFFF0) >> 3));
173}
174
175absl::Status SaveExits(Rom* rom, const std::vector<OverworldExit>& exits) {
176 // ASM version 0x03 added SW support and the exit leading to Zora's Domain
177 // specifically needs to be updated because its camera values are incorrect.
178 // We only update it if it was a vanilla ROM though because we don't know if
179 // the user has already adjusted it or not.
180 uint8_t asm_version = (*rom)[OverworldCustomASMHasBeenApplied];
181 if (asm_version == 0x00) {
182 // Apply special fix for Zora's Domain exit (index 0x4D)
183 // TODO(scawful): Implement SpecialUpdatePosition for OverworldExit
184 // Similar to ZScream Save.cs:1034-1039
185 // if (all_exits_.size() > 0x4D) {
186 // all_exits_[0x4D].SpecialUpdatePosition();
187 // }
188 }
189
190 for (int i = 0; i < kNumOverworldExits; i++) {
191 RETURN_IF_ERROR(rom->WriteShort(OWExitRoomId + (i * 2), exits[i].room_id_));
192 RETURN_IF_ERROR(rom->WriteByte(OWExitMapId + i, exits[i].map_id_));
193 RETURN_IF_ERROR(rom->WriteShort(OWExitVram + (i * 2), exits[i].map_pos_));
195 rom->WriteShort(OWExitYScroll + (i * 2), exits[i].y_scroll_));
197 rom->WriteShort(OWExitXScroll + (i * 2), exits[i].x_scroll_));
199 rom->WriteShort(OWExitYPlayer + (i * 2), exits[i].y_player_));
201 rom->WriteShort(OWExitXPlayer + (i * 2), exits[i].x_player_));
203 rom->WriteShort(OWExitYCamera + (i * 2), exits[i].y_camera_));
205 rom->WriteShort(OWExitXCamera + (i * 2), exits[i].x_camera_));
206 RETURN_IF_ERROR(rom->WriteByte(OWExitUnk1 + i, exits[i].scroll_mod_y_));
207 RETURN_IF_ERROR(rom->WriteByte(OWExitUnk2 + i, exits[i].scroll_mod_x_));
209 rom->WriteShort(OWExitDoorType1 + (i * 2), exits[i].door_type_1_));
211 rom->WriteShort(OWExitDoorType2 + (i * 2), exits[i].door_type_2_));
212
213 if (exits[i].room_id_ == 0x0180) {
215 rom->WriteByte(OWExitDoorPosition + 0, exits[i].map_id_ & 0xFF));
216 } else if (exits[i].room_id_ == 0x0181) {
218 rom->WriteByte(OWExitDoorPosition + 2, exits[i].map_id_ & 0xFF));
219 } else if (exits[i].room_id_ == 0x0182) {
221 rom->WriteByte(OWExitDoorPosition + 4, exits[i].map_id_ & 0xFF));
222 }
223 }
224
225 return absl::OkStatus();
226}
227
228} // namespace yaze::zelda3
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
absl::Status ReadTransaction(T &var, int address, Args &&... args)
Definition rom.h:83
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:286
auto data() const
Definition rom.h:135
absl::Status WriteShort(int addr, uint16_t value)
Definition rom.cc:316
void UpdateMapProperties(uint16_t map_id, const void *context) override
Update exit properties when moved or map changes.
Represents the full Overworld data, light and dark world.
Definition overworld.h:217
auto overworld_map(int i) const
Definition overworld.h:473
Zelda 3 specific classes and functions.
Definition editor.h:35
constexpr int OWExitYScroll
constexpr int OWExitDoorType2
constexpr int OWExitYCamera
constexpr int OWExitDoorPosition
constexpr int OWExitXScroll
constexpr int OWExitRoomId
constexpr int OWExitXCamera
constexpr int OWExitUnk1
constexpr int OWExitYPlayer
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
constexpr int OWExitMapId
constexpr int OWExitUnk2
constexpr int OverworldCustomASMHasBeenApplied
Definition common.h:89
constexpr int OWExitDoorType1
constexpr int kNumOverworldExits
absl::Status SaveExits(Rom *rom, const std::vector< OverworldExit > &exits)
constexpr int OWExitXPlayer
constexpr int OWExitVram
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22