Refactor map properties handling in Overworld Editor for improved clarity and performance

- Rearranged include statements for better organization and reduced redundancy.
- Enhanced the DrawSimplifiedMapSettings method by improving formatting and readability.
- Streamlined the logic for handling world and map changes, ensuring more efficient updates.
- Updated the DrawMapPropertiesPanel and related methods to improve layout and user interaction.
- Refactored overlay handling logic to enhance maintainability and clarity in the Overworld class.
This commit is contained in:
scawful
2025-09-27 19:42:32 -04:00
parent 0460a6f638
commit 7815f8cc85
5 changed files with 873 additions and 574 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -444,10 +444,7 @@ void OverworldEditor::DrawOverworldMapSettings() {
// World selector (always present)
TableNextColumn();
ImGui::SetNextItemWidth(120.f);
if (ImGui::Combo("##world", &current_world_, kWorldList.data(), 3)) {
// Update current map when world changes
RefreshOverworldMap();
}
ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
// Area Graphics (always present)
TableNextColumn();
@@ -2170,18 +2167,7 @@ void OverworldEditor::DrawMapPropertiesPanel() {
Text("World");
TableNextColumn();
ImGui::SetNextItemWidth(100.f);
if (ImGui::Combo("##world", &current_world_, kWorldList.data(), 3)) {
// Update current map based on world change
if (current_map_ >= 0x40 && current_world_ == 0) {
current_map_ -= 0x40;
} else if (current_map_ < 0x40 && current_world_ == 1) {
current_map_ += 0x40;
} else if (current_map_ < 0x80 && current_world_ == 2) {
current_map_ += 0x80;
} else if (current_map_ >= 0x80 && current_world_ != 2) {
current_map_ -= 0x80;
}
}
ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
TableNextColumn();
Text("Area Graphics");

View File

@@ -39,7 +39,12 @@ absl::Status Overworld::Load(Rom* rom) {
map_parent_[map_index] = overworld_maps_[map_index].parent();
}
FetchLargeMaps();
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version >= 3) {
AssignMapSizes(overworld_maps_);
} else {
FetchLargeMaps();
}
LoadTileTypes();
RETURN_IF_ERROR(LoadEntrances());
RETURN_IF_ERROR(LoadHoles());
@@ -105,6 +110,90 @@ void Overworld::FetchLargeMaps() {
}
}
/**
* @brief Loads all maps from ROM to see what size they are.
* @param maps The maps to update (passed by reference)
*/
void Overworld::AssignMapSizes(std::vector<OverworldMap>& maps) {
std::vector<bool> map_checked(kNumOverworldMaps, false);
int xx = 0;
int yy = 0;
int world = 0;
while (true) {
int i = world + xx + (yy * 8);
if (i >= static_cast<int>(map_checked.size())) {
break;
}
if (!map_checked[i]) {
switch (maps[i].area_size()) {
case AreaSizeEnum::SmallArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::SmallArea);
break;
case AreaSizeEnum::LargeArea:
map_checked[i] = true;
maps[i].SetAsLargeMap(i, 0);
if (i + 1 < static_cast<int>(maps.size())) {
map_checked[i + 1] = true;
maps[i + 1].SetAsLargeMap(i, 1);
}
if (i + 8 < static_cast<int>(maps.size())) {
map_checked[i + 8] = true;
maps[i + 8].SetAsLargeMap(i, 2);
}
if (i + 9 < static_cast<int>(maps.size())) {
map_checked[i + 9] = true;
maps[i + 9].SetAsLargeMap(i, 3);
}
xx++;
break;
case AreaSizeEnum::WideArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::WideArea);
if (i + 1 < static_cast<int>(maps.size())) {
map_checked[i + 1] = true;
maps[i + 1].SetAreaSize(AreaSizeEnum::WideArea);
}
xx++;
break;
case AreaSizeEnum::TallArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::TallArea);
if (i + 8 < static_cast<int>(maps.size())) {
map_checked[i + 8] = true;
maps[i + 8].SetAreaSize(AreaSizeEnum::TallArea);
}
break;
}
}
xx++;
if (xx >= 8) {
xx = 0;
yy += 1;
if (yy >= 8) {
yy = 0;
world += 0x40;
}
}
}
}
absl::StatusOr<uint16_t> Overworld::GetTile16ForTile32(
int index, int quadrant, int dimension, const uint32_t* map32address) {
ASSIGN_OR_RETURN(
@@ -443,7 +532,8 @@ absl::Status Overworld::LoadItems() {
// Determine max number of overworld maps based on actual ASM version
// Only use expanded maps (0xA0) if v3+ ASM is actually applied
int max_ow = (asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80;
int max_ow =
(asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80;
ASSIGN_OR_RETURN(uint32_t pointer_snes,
rom()->ReadLong(zelda3::overworldItemsAddress));
@@ -745,16 +835,16 @@ absl::Status Overworld::SaveOverworldMaps() {
absl::Status Overworld::SaveLargeMaps() {
util::logf("Saving Large Maps");
// Check if this is a v3+ ROM to use expanded transition system
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF);
if (use_expanded_transitions) {
// Use new v3+ complex transition system with neighbor awareness
return SaveLargeMapsExpanded();
}
// Original vanilla/v2 logic preserved
std::vector<uint8_t> checked_map;
@@ -1073,123 +1163,143 @@ absl::Status Overworld::SaveLargeMaps() {
return absl::OkStatus();
}
absl::Status Overworld::SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4) {
absl::Status Overworld::SaveSmallAreaTransitions(
int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_4) {
// Set basic transition targets
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2),
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2),
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_north + (i * 2),
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_west + (i * 2),
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
// byScreen1 = Transitioning right
uint16_t by_screen1_small = 0x0060;
// Check west neighbor for transition adjustments
if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1];
// Transition from bottom right quadrant of large area to small area
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) {
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
west_neighbor.large_index() == 3) {
by_screen1_small = 0xF060;
}
// Transition from bottom quadrant of tall area to small area
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) {
// Transition from bottom quadrant of tall area to small area
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
west_neighbor.large_index() == 2) {
by_screen1_small = 0xF060;
}
}
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
RETURN_IF_ERROR(
rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
// byScreen2 = Transitioning left
uint16_t by_screen2_small = 0x0040;
// Check east neighbor for transition adjustments
if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 1];
// Transition from bottom left quadrant of large area to small area
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) {
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
east_neighbor.large_index() == 2) {
by_screen2_small = 0xF040;
}
// Transition from bottom quadrant of tall area to small area
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) {
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
east_neighbor.large_index() == 2) {
by_screen2_small = 0xF040;
}
}
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
RETURN_IF_ERROR(
rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
// byScreen3 = Transitioning down
uint16_t by_screen3_small = 0x1800;
// Check north neighbor for transition adjustments
if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8];
// Transition from bottom right quadrant of large area to small area
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) {
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
north_neighbor.large_index() == 3) {
by_screen3_small = 0x17C0;
}
// Transition from right quadrant of wide area to small area
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) {
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
north_neighbor.large_index() == 1) {
by_screen3_small = 0x17C0;
}
}
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
RETURN_IF_ERROR(
rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
// byScreen4 = Transitioning up
uint16_t by_screen4_small = 0x1000;
// Check south neighbor for transition adjustments
if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 8];
// Transition from top right quadrant of large area to small area
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) {
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
south_neighbor.large_index() == 1) {
by_screen4_small = 0x0FC0;
}
// Transition from right quadrant of wide area to small area
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) {
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
south_neighbor.large_index() == 1) {
by_screen4_small = 0x0FC0;
}
}
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
RETURN_IF_ERROR(
rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
return absl::OkStatus();
}
absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4) {
absl::Status Overworld::SaveLargeAreaTransitions(
int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_4) {
// Set transition targets for all 4 quadrants
const uint16_t offsets[] = {0, 2, 16, 18};
for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
}
// Complex neighbor-aware transition calculations for large areas
// byScreen1 = Transitioning right
std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
// Check west neighbor
if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1];
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (west_neighbor.large_index()) {
case 1: // From bottom right to bottom left of large area
@@ -1210,18 +1320,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
}
}
}
for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_large[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_large[j]));
}
// byScreen2 = Transitioning left
std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
// Check east neighbor
if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 2];
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (east_neighbor.large_index()) {
case 0: // From bottom left to bottom right of large area
@@ -1242,18 +1353,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
}
}
}
for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_large[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_large[j]));
}
// byScreen3 = Transitioning down
std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
// Check north neighbor
if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8];
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (north_neighbor.large_index()) {
case 2: // From bottom right to top right of large area
@@ -1274,18 +1386,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
}
}
}
for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_large[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_large[j]));
}
// byScreen4 = Transitioning up
std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
// Check south neighbor
if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 16];
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (south_neighbor.large_index()) {
case 0: // From top right to bottom right of large area
@@ -1306,79 +1419,90 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
}
}
}
for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_large[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_large[j]));
}
return absl::OkStatus();
}
absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4) {
absl::Status Overworld::SaveWideAreaTransitions(
int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_4) {
// Set transition targets for both quadrants
const uint16_t offsets[] = {0, 2};
for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
}
// byScreen1 = Transitioning right
std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
// Check west neighbor
if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1];
// From bottom right of large to left of wide
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) {
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
west_neighbor.large_index() == 3) {
by_screen1_wide[0] = 0xF060;
}
// From bottom of tall to left of wide
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) {
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
west_neighbor.large_index() == 2) {
by_screen1_wide[0] = 0xF060;
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_wide[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_wide[j]));
}
// byScreen2 = Transitioning left
std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
// Check east neighbor
if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 2];
// From bottom left of large to right of wide
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) {
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
east_neighbor.large_index() == 2) {
by_screen2_wide[1] = 0xF080;
}
// From bottom of tall to right of wide
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) {
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
east_neighbor.large_index() == 2) {
by_screen2_wide[1] = 0xF080;
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_wide[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_wide[j]));
}
// byScreen3 = Transitioning down
// byScreen3 = Transitioning down
std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
// Check north neighbor
if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8];
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (north_neighbor.large_index()) {
case 2: // From bottom right of large to right of wide
@@ -1399,18 +1523,19 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par
}
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_wide[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_wide[j]));
}
// byScreen4 = Transitioning up
std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
// Check south neighbor
if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 8];
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (south_neighbor.large_index()) {
case 0: // From top right of large to right of wide
@@ -1434,37 +1559,42 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par
}
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_wide[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_wide[j]));
}
return absl::OkStatus();
}
absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4) {
absl::Status Overworld::SaveTallAreaTransitions(
int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_4) {
// Set transition targets for both quadrants
const uint16_t offsets[] = {0, 16};
for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_north + (i * 2) + offset,
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
RETURN_IF_ERROR(
rom()->WriteShort(transition_target_west + (i * 2) + offset,
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
}
// byScreen1 = Transitioning right
std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
// Check west neighbor
if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1];
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (west_neighbor.large_index()) {
case 1: // From bottom right of large to bottom of tall
@@ -1485,18 +1615,19 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
}
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_tall[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_tall[j]));
}
// byScreen2 = Transitioning left
std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
// Check east neighbor
if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 1];
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (east_neighbor.large_index()) {
case 0: // From bottom left of large to bottom of tall
@@ -1517,51 +1648,58 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
}
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_tall[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_tall[j]));
}
// byScreen3 = Transitioning down
std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
// Check north neighbor
if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8];
// From bottom right of large to top of tall
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) {
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
north_neighbor.large_index() == 3) {
by_screen3_tall[0] = 0x17C0;
}
// From right of wide to top of tall
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) {
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
north_neighbor.large_index() == 1) {
by_screen3_tall[0] = 0x17C0;
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_tall[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_tall[j]));
}
// byScreen4 = Transitioning up
std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
// Check south neighbor
if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 16];
// From top right of large to bottom of tall
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) {
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
south_neighbor.large_index() == 1) {
by_screen4_tall[1] = 0x1FC0;
}
// From right of wide to bottom of tall
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) {
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
south_neighbor.large_index() == 1) {
by_screen4_tall[1] = 0x1FC0;
}
}
for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_tall[j]));
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_tall[j]));
}
return absl::OkStatus();
@@ -1569,7 +1707,7 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
absl::Status Overworld::SaveLargeMapsExpanded() {
util::logf("Saving Large Maps (v3+ Expanded)");
// Use expanded memory locations for v3+
int transition_target_north = zelda3::transition_target_northExpanded;
int transition_target_west = zelda3::transition_target_westExpanded;
@@ -1585,7 +1723,8 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
// Process all overworld maps (0xA0 for v3)
for (int i = 0; i < kNumOverworldMaps; ++i) {
// Skip if this map was already processed as part of a multi-area structure
if (std::find(checked_map.begin(), checked_map.end(), i) != checked_map.end()) {
if (std::find(checked_map.begin(), checked_map.end(), i) !=
checked_map.end()) {
continue;
}
@@ -1599,20 +1738,20 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
// Handle transitions based on area size
switch (overworld_maps_[i].area_size()) {
case AreaSizeEnum::SmallArea:
RETURN_IF_ERROR(SaveSmallAreaTransitions(i, parent_x_pos, parent_y_pos,
transition_target_north, transition_target_west,
transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2,
screen_change_3, screen_change_4));
RETURN_IF_ERROR(SaveSmallAreaTransitions(
i, parent_x_pos, parent_y_pos, transition_target_north,
transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_3,
screen_change_4));
checked_map.emplace_back(i);
break;
case AreaSizeEnum::LargeArea:
RETURN_IF_ERROR(SaveLargeAreaTransitions(i, parent_x_pos, parent_y_pos,
transition_target_north, transition_target_west,
transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2,
screen_change_3, screen_change_4));
RETURN_IF_ERROR(SaveLargeAreaTransitions(
i, parent_x_pos, parent_y_pos, transition_target_north,
transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_3,
screen_change_4));
// Mark all 4 quadrants as processed
checked_map.emplace_back(i);
checked_map.emplace_back(i + 1);
@@ -1621,22 +1760,22 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
break;
case AreaSizeEnum::WideArea:
RETURN_IF_ERROR(SaveWideAreaTransitions(i, parent_x_pos, parent_y_pos,
transition_target_north, transition_target_west,
transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2,
screen_change_3, screen_change_4));
RETURN_IF_ERROR(SaveWideAreaTransitions(
i, parent_x_pos, parent_y_pos, transition_target_north,
transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_3,
screen_change_4));
// Mark both horizontal quadrants as processed
checked_map.emplace_back(i);
checked_map.emplace_back(i + 1);
break;
case AreaSizeEnum::TallArea:
RETURN_IF_ERROR(SaveTallAreaTransitions(i, parent_x_pos, parent_y_pos,
transition_target_north, transition_target_west,
transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2,
screen_change_3, screen_change_4));
RETURN_IF_ERROR(SaveTallAreaTransitions(
i, parent_x_pos, parent_y_pos, transition_target_north,
transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_3,
screen_change_4));
// Mark both vertical quadrants as processed
checked_map.emplace_back(i);
checked_map.emplace_back(i + 8);
@@ -2170,7 +2309,7 @@ absl::Status Overworld::SaveMap16Tiles() {
absl::Status Overworld::SaveEntrances() {
util::logf("Saving Entrances");
// Use expanded entrance tables if available
if (expanded_entrances_) {
for (int i = 0; i < kNumOverworldEntrances; i++) {
@@ -2206,10 +2345,10 @@ absl::Status Overworld::SaveEntrances() {
absl::Status Overworld::SaveExits() {
util::logf("Saving Exits");
// ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically
// ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically
// needs to be updated because its camera values are incorrect.
// We only update it if it was a vanilla ROM though because we don't know if the
// We only update it if it was a vanilla ROM though because we don't know if the
// user has already adjusted it or not.
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version == 0x00) {
@@ -2219,7 +2358,7 @@ absl::Status Overworld::SaveExits() {
// all_exits_[0x4D].SpecialUpdatePosition();
// }
}
for (int i = 0; i < kNumOverworldExits; i++) {
RETURN_IF_ERROR(
rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_));
@@ -2360,24 +2499,24 @@ absl::Status Overworld::SaveItems() {
absl::Status Overworld::SaveMapOverlays() {
util::logf("Saving Map Overlays");
// Generate the new overlay code that handles interactive overlays
std::vector<uint8_t> new_overlay_code = {
0xC2, 0x30, // REP #$30
0xA5, 0x8A, // LDA $8A
0x0A, 0x18, // ASL : CLC
0x65, 0x8A, // ADC $8A
0xAA, // TAX
0xBF, 0x00, 0x00, 0x00, // LDA, X
0x85, 0x00, // STA $00
0xBF, 0x00, 0x00, 0x00, // LDA, X +2
0x85, 0x02, // STA $02
0x4B, // PHK
0xF4, 0x00, 0x00, // This position +3 ?
0xDC, 0x00, 0x00, // JML [$00 00]
0xE2, 0x30, // SEP #$30
0xAB, // PLB
0x6B, // RTL
0xC2, 0x30, // REP #$30
0xA5, 0x8A, // LDA $8A
0x0A, 0x18, // ASL : CLC
0x65, 0x8A, // ADC $8A
0xAA, // TAX
0xBF, 0x00, 0x00, 0x00, // LDA, X
0x85, 0x00, // STA $00
0xBF, 0x00, 0x00, 0x00, // LDA, X +2
0x85, 0x02, // STA $02
0x4B, // PHK
0xF4, 0x00, 0x00, // This position +3 ?
0xDC, 0x00, 0x00, // JML [$00 00]
0xE2, 0x30, // SEP #$30
0xAB, // PLB
0x6B, // RTL
};
// Write overlay code to ROM
@@ -2399,7 +2538,7 @@ absl::Status Overworld::SaveMapOverlays() {
constexpr int kExpandedOverlaySpace = 0x120000;
int pos = kExpandedOverlaySpace;
int ptr_pos = kOverlayCodeStart + 32;
for (int i = 0; i < kNumOverworldMaps; i++) {
int snes_addr = PcToSnes(pos);
RETURN_IF_ERROR(rom()->WriteLong(ptr_pos, snes_addr & 0xFFFFFF));
@@ -2412,7 +2551,8 @@ absl::Status Overworld::SaveMapOverlays() {
if (t + 2 < overlay_data.size()) {
// Generate LDA/STA sequence for each overlay tile
RETURN_IF_ERROR(rom()->WriteByte(pos, 0xA9)); // LDA #$
RETURN_IF_ERROR(rom()->WriteShort(pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
RETURN_IF_ERROR(rom()->WriteShort(
pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
pos += 3;
RETURN_IF_ERROR(rom()->WriteByte(pos, 0x8D)); // STA $xxxx
@@ -2431,37 +2571,46 @@ absl::Status Overworld::SaveMapOverlays() {
absl::Status Overworld::SaveOverworldTilesType() {
util::logf("Saving Overworld Tiles Types");
for (int i = 0; i < kNumTileTypes; i++) {
RETURN_IF_ERROR(rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i]));
RETURN_IF_ERROR(
rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i]));
}
return absl::OkStatus();
}
absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette,
bool enable_mosaic, bool enable_gfx_groups,
bool enable_subscreen_overlay, bool enable_animated) {
absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color,
bool enable_main_palette,
bool enable_mosaic,
bool enable_gfx_groups,
bool enable_subscreen_overlay,
bool enable_animated) {
util::logf("Applying Custom Overworld ASM");
// Set the enable/disable settings
uint8_t enable_value = enable_bg_color ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value));
enable_value = enable_main_palette ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value));
enable_value = enable_mosaic ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicEnabled, enable_value));
enable_value = enable_gfx_groups ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value));
enable_value = enable_animated ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value));
enable_value = enable_subscreen_overlay ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value));
// Write the main palette table
for (int i = 0; i < kNumOverworldMaps; i++) {
@@ -2474,18 +2623,20 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
const auto& mosaic = overworld_maps_[i].mosaic_expanded();
// .... udlr bit format
uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) | // up
(mosaic[1] ? 0x04 : 0x00) | // down
(mosaic[1] ? 0x04 : 0x00) | // down
(mosaic[2] ? 0x02 : 0x00) | // left
(mosaic[3] ? 0x01 : 0x00); // right
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte));
}
// Write the main and animated gfx tiles table
for (int i = 0; i < kNumOverworldMaps; i++) {
for (int j = 0; j < 8; j++) {
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j,
overworld_maps_[i].custom_tileset(j)));
RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j,
overworld_maps_[i].custom_tileset(j)));
}
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXArray + i,
overworld_maps_[i].animated_gfx()));
@@ -2493,8 +2644,9 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
// Write the subscreen overlay table
for (int i = 0; i < kNumOverworldMaps; i++) {
RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2),
overworld_maps_[i].subscreen_overlay()));
RETURN_IF_ERROR(
rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2),
overworld_maps_[i].subscreen_overlay()));
}
return absl::OkStatus();
@@ -2502,11 +2654,12 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
absl::Status Overworld::SaveAreaSpecificBGColors() {
util::logf("Saving Area Specific Background Colors");
// Write area-specific background colors if enabled
for (int i = 0; i < kNumOverworldMaps; i++) {
uint16_t bg_color = overworld_maps_[i].area_specific_bg_color();
RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color));
RETURN_IF_ERROR(rom()->WriteShort(
OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color));
}
return absl::OkStatus();

View File

@@ -190,6 +190,7 @@ class Overworld {
absl::Status SaveMapProperties();
absl::Status SaveMusic();
absl::Status SaveAreaSizes();
void AssignMapSizes(std::vector<OverworldMap>& maps);
auto rom() const { return rom_; }
auto mutable_rom() { return rom_; }

View File

@@ -14,20 +14,20 @@
namespace yaze {
namespace zelda3 {
OverworldMap::OverworldMap(int index, Rom *rom)
OverworldMap::OverworldMap(int index, Rom* rom)
: index_(index), parent_(index), rom_(rom) {
LoadAreaInfo();
// Use ASM version byte as source of truth
// Load parent ID from ROM data if available (for custom ASM versions)
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version != 0xFF && asm_version >= 0x03) {
// For v3+, parent ID is stored in expanded table
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
}
if (asm_version != 0xFF) {
// Custom overworld ASM is applied - use custom logic
if (asm_version == 0x00) {
// Special case: version 0 means flag-enabled vanilla mode
if (asm_version == 0x03) {
LoadCustomOverworldData();
} else {
// Custom overworld ASM applied - set up custom tileset
SetupCustomTileset(asm_version);
}
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
@@ -38,21 +38,28 @@ OverworldMap::OverworldMap(int index, Rom *rom)
}
absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
std::vector<gfx::Tile16> &tiles16,
OverworldBlockset &world_blockset) {
std::vector<gfx::Tile16>& tiles16,
OverworldBlockset& world_blockset) {
game_state_ = game_state;
world_ = world;
if (large_map_) {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// For large maps in vanilla ROMs, we need to handle special world graphics
// This ensures proper rendering of special overworld areas like Zora's Domain
if (large_map_ && asm_version == 0xFF) {
if (parent_ != index_ && !initialized_) {
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
index_ != 0x88) {
// Most special world areas use the special graphics group
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
(parent_ - kSpecialWorldMapIdStart)];
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
} else if (index_ == 0x88) {
// Triforce room has special hardcoded values
area_graphics_ = 0x51;
area_palette_ = 0x00;
} else {
// Fallback to standard area graphics
area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
}
@@ -74,6 +81,11 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
void OverworldMap::LoadAreaInfo() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// ZSCustomOverworld ASM Version System:
// 0x00-0x02: Legacy versions with limited features
// 0x03: Current version with full area size expansion and custom data support
// 0xFF: Pure vanilla ROM (no ASM applied)
// Load message ID and area size based on ASM version
if (asm_version < 3 || asm_version == 0xFF) {
// v2 and vanilla: use original message table
@@ -83,6 +95,7 @@ void OverworldMap::LoadAreaInfo() {
// Load area size for v2/vanilla
if (index_ < 0x80) {
// For LW and DW, check the screen size byte
// Note: v2 had a bug where large/small values were swapped
uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
switch (size_byte) {
case 0:
@@ -101,6 +114,7 @@ void OverworldMap::LoadAreaInfo() {
}
} else {
// For SW, use hardcoded values for v2 compatibility
// Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas
area_size_ =
(index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
? AreaSizeEnum::LargeArea
@@ -108,6 +122,7 @@ void OverworldMap::LoadAreaInfo() {
}
} else {
// v3: use expanded message table and area size table
// All area sizes are now stored in the expanded table, supporting all size types
message_id_ =
(*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] |
((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8);
@@ -172,10 +187,10 @@ void OverworldMap::LoadAreaInfo() {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
}
} else {
// Special World (SW) areas
// Special World (SW) areas (index >= 0x80)
// Message ID already loaded above based on ASM version
// For v3, use expanded sprite tables
// For v3, use expanded sprite tables with full customization support
if (asm_version >= 3 && asm_version != 0xFF) {
sprite_graphics_[0] =
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
@@ -214,20 +229,24 @@ void OverworldMap::LoadAreaInfo() {
area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_];
// For v2/vanilla, use original palette table and handle special cases
// These hardcoded cases are needed for vanilla compatibility
if (asm_version < 3 || asm_version == 0xFF) {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
// Handle special world area cases
// Handle special world area cases based on ZScream documentation
if (index_ == 0x88 || index_ == 0x93) {
// Triforce room - special graphics and palette
area_graphics_ = 0x51;
area_palette_ = 0x00;
} else if (index_ == 0x80) {
// Master Sword area - use special graphics group
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
(parent_ - kSpecialWorldMapIdStart)];
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
} else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
index_ == 0x8A) {
// Zora's Domain areas - use special sprite graphics
// Zora's Domain areas - use special sprite graphics and area graphics
// Note: These are the large area maps that were causing crashes
sprite_graphics_[0] = 0x0E;
sprite_graphics_[1] = 0x0E;
sprite_graphics_[2] = 0x0E;
@@ -253,7 +272,7 @@ void OverworldMap::LoadAreaInfo() {
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
} else {
// Default case
// Default case - use basic graphics
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
}
@@ -578,7 +597,7 @@ void OverworldMap::LoadAreaGraphics() {
namespace palette_internal {
absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette &current,
absl::Status SetColorsPalette(Rom& rom, int index, gfx::SnesPalette& current,
gfx::SnesPalette main, gfx::SnesPalette animated,
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
@@ -699,7 +718,7 @@ absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette &current,
} // namespace palette_internal
absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
const gfx::PaletteGroup &palette_group, int index, int previous_index,
const gfx::PaletteGroup& palette_group, int index, int previous_index,
int limit) {
if (index == 255) {
index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
@@ -713,10 +732,10 @@ absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
absl::Status OverworldMap::LoadPalette() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
int previous_pal_id = 0;
int previous_spr_pal_id = 0;
if (index_ > 0) {
// Load previous palette ID based on ASM version
if (asm_version < 3 || asm_version == 0xFF) {
@@ -725,7 +744,7 @@ absl::Status OverworldMap::LoadPalette() {
// v3 uses expanded palette table
previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
}
previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
}
@@ -751,12 +770,12 @@ absl::Status OverworldMap::LoadPalette() {
pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4)];
}
if (pal2 == 0xFF) {
pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4) + 1];
}
if (pal3 == 0xFF) {
pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4) + 2];
@@ -769,11 +788,14 @@ absl::Status OverworldMap::LoadPalette() {
GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
// Set background color based on world type and area-specific settings
bool use_area_specific_bg = (*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00;
bool use_area_specific_bg =
(*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00;
if (use_area_specific_bg) {
// Use area-specific background color from custom array
area_specific_bg_color_ = (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] |
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1] << 8);
area_specific_bg_color_ =
(*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] |
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1]
<< 8);
// Convert 15-bit SNES color to palette color
bgr = gfx::SnesColor(area_specific_bg_color_);
} else {
@@ -806,15 +828,16 @@ absl::Status OverworldMap::LoadPalette() {
if (pal4 == 0xFF) {
pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
}
if (pal4 == 0xFF) {
pal4 = 0; // Fallback to 0 if still 0xFF
}
if (pal5 == 0xFF) {
pal5 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
pal5 =
(*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
}
if (pal5 == 0xFF) {
pal5 = 0; // Fallback to 0 if still 0xFF
}
@@ -840,7 +863,7 @@ absl::Status OverworldMap::LoadPalette() {
absl::Status OverworldMap::LoadOverlay() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// Load overlays based on ROM version
if (asm_version == 0xFF) {
// Vanilla ROM - load overlay from overlay pointers
@@ -855,15 +878,15 @@ absl::Status OverworldMap::LoadOverlay() {
}
absl::Status OverworldMap::LoadVanillaOverlayData() {
// Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements)
int address = (kOverlayPointersBank << 16) +
((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
(*rom_)[kOverlayPointers + (index_ * 2)];
// Convert SNES address to PC address
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
// Check if custom overlay code is present
if ((*rom_)[kOverlayData1] == 0x6B) {
// Use custom overlay data pointer
@@ -872,7 +895,7 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
(*rom_)[kOverlayData2 + (index_ * 3)];
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
}
// Validate address
if (address >= rom_->size()) {
has_overlay_ = false;
@@ -880,15 +903,15 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
overlay_data_.clear();
return absl::OkStatus();
}
// Parse overlay data (interactive overlays)
overlay_data_.clear();
uint8_t b = (*rom_)[address];
// Parse overlay commands until we hit END (0x60)
while (b != 0x60 && address < rom_->size()) {
overlay_data_.push_back(b);
// Handle different overlay commands
switch (b) {
case 0xA9: // LDA #$
@@ -957,28 +980,28 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
address++;
break;
}
if (address < rom_->size()) {
b = (*rom_)[address];
} else {
break;
}
}
// Add the END command if we found it
if (b == 0x60) {
overlay_data_.push_back(0x60);
}
// Set overlay ID based on map index (simplified)
overlay_id_ = index_;
has_overlay_ = !overlay_data_.empty();
return absl::OkStatus();
}
void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
int size, uint8_t *all_gfx) {
int size, uint8_t* all_gfx) {
// Ensure we don't go out of bounds
int max_offset = static_graphics_offset * size + size;
if (max_offset > rom_->graphics_buffer().size()) {
@@ -988,7 +1011,7 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
}
return;
}
for (int i = 0; i < size; i++) {
auto byte = all_gfx[i + (static_graphics_offset * size)];
switch (index) {
@@ -1004,8 +1027,9 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
}
absl::Status OverworldMap::BuildTileset() {
if (current_gfx_.size() == 0) current_gfx_.resize(0x10000, 0x00);
if (current_gfx_.size() == 0)
current_gfx_.resize(0x10000, 0x00);
// Process the 8 main graphics sheets (slots 0-7)
for (int i = 0; i < 8; i++) {
if (static_graphics_[i] != 0) {
@@ -1013,7 +1037,7 @@ absl::Status OverworldMap::BuildTileset() {
rom_->graphics_buffer().data());
}
}
// Process sprite graphics (slots 8-15)
for (int i = 8; i < 16; i++) {
if (static_graphics_[i] != 0) {
@@ -1021,19 +1045,20 @@ absl::Status OverworldMap::BuildTileset() {
rom_->graphics_buffer().data());
}
}
// Process animated graphics if available (slot 16)
if (static_graphics_[16] != 0) {
ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000,
rom_->graphics_buffer().data());
}
return absl::OkStatus();
}
absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16,
absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16,
int count) {
if (current_blockset_.size() == 0) current_blockset_.resize(0x100000, 0x00);
if (current_blockset_.size() == 0)
current_blockset_.resize(0x100000, 0x00);
const int offsets[] = {0x00, 0x08, 0x400, 0x408};
auto yy = 0;
@@ -1077,7 +1102,7 @@ absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16,
return absl::OkStatus();
}
absl::Status OverworldMap::BuildBitmap(OverworldBlockset &world_blockset) {
absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
if (bitmap_data_.size() != 0) {
bitmap_data_.clear();
}