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:28
absl::Status ReadTransaction(T &var, int address, Args &&... args)
Definition rom.h:87
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:476
auto data() const
Definition rom.h:139
absl::Status WriteShort(int addr, uint16_t value)
Definition rom.cc:518
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:261
auto overworld_map(int i) const
Definition overworld.h:528
Zelda 3 specific classes and functions.
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