refactor overworld entrance load/save to flat functions
This commit is contained in:
@@ -1692,10 +1692,6 @@ absl::Status OverworldEditor::DrawAreaGraphics() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// DrawTileSelector() removed - replaced by individual card system in Update()
|
||||
// DrawOverworldEntrances(), DrawOverworldExits(), DrawOverworldItems(), DrawOverworldSprites()
|
||||
// removed - moved to OverworldEntityRenderer
|
||||
|
||||
absl::Status OverworldEditor::Save() {
|
||||
if (core::FeatureFlags::get().overworld.kSaveOverworldMaps) {
|
||||
RETURN_IF_ERROR(overworld_.CreateTile32Tilemap());
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
#include "zelda3/overworld/overworld_item.h"
|
||||
#include "zelda3/overworld/overworld_map.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -78,12 +79,12 @@ absl::Status Overworld::Load(Rom* rom) {
|
||||
|
||||
{
|
||||
gfx::ScopedTimer entrances_timer("LoadEntrances");
|
||||
RETURN_IF_ERROR(LoadEntrances());
|
||||
ASSIGN_OR_RETURN(all_entrances_, LoadEntrances(rom_));
|
||||
}
|
||||
|
||||
{
|
||||
gfx::ScopedTimer holes_timer("LoadHoles");
|
||||
RETURN_IF_ERROR(LoadHoles());
|
||||
ASSIGN_OR_RETURN(all_holes_, LoadHoles(rom_));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -686,66 +687,6 @@ void Overworld::LoadTileTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status Overworld::LoadEntrances() {
|
||||
int ow_entrance_map_ptr = kOverworldEntranceMap;
|
||||
int ow_entrance_pos_ptr = kOverworldEntrancePos;
|
||||
int ow_entrance_id_ptr = kOverworldEntranceEntranceId;
|
||||
int num_entrances = 129;
|
||||
|
||||
// Check if expanded entrance data is actually present in ROM
|
||||
// The flag position should contain 0xB8 for vanilla, something else for expanded
|
||||
if (rom()->data()[kOverworldEntranceExpandedFlagPos] != 0xB8) {
|
||||
// ROM has expanded entrance data - use expanded addresses
|
||||
ow_entrance_map_ptr = kOverworldEntranceMapExpanded;
|
||||
ow_entrance_pos_ptr = kOverworldEntrancePosExpanded;
|
||||
ow_entrance_id_ptr = kOverworldEntranceEntranceIdExpanded;
|
||||
expanded_entrances_ = true;
|
||||
num_entrances = 256; // Expanded entrance count
|
||||
}
|
||||
// Otherwise use vanilla addresses (already set above)
|
||||
|
||||
for (int i = 0; i < num_entrances; i++) {
|
||||
ASSIGN_OR_RETURN(auto map_id,
|
||||
rom()->ReadWord(ow_entrance_map_ptr + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto map_pos,
|
||||
rom()->ReadWord(ow_entrance_pos_ptr + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto entrance_id, rom()->ReadByte(ow_entrance_id_ptr + i));
|
||||
int p = map_pos >> 1;
|
||||
int x = (p % 64);
|
||||
int y = (p >> 6);
|
||||
bool deleted = false;
|
||||
if (map_pos == 0xFFFF) {
|
||||
deleted = true;
|
||||
}
|
||||
all_entrances_.emplace_back(
|
||||
(x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
|
||||
(y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
|
||||
deleted);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Overworld::LoadHoles() {
|
||||
constexpr int kNumHoles = 0x13;
|
||||
for (int i = 0; i < kNumHoles; i++) {
|
||||
ASSIGN_OR_RETURN(auto map_id,
|
||||
rom()->ReadWord(kOverworldHoleArea + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto map_pos,
|
||||
rom()->ReadWord(kOverworldHolePos + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto entrance_id,
|
||||
rom()->ReadByte(kOverworldHoleEntrance + i));
|
||||
int p = (map_pos + 0x400) >> 1;
|
||||
int x = (p % 64);
|
||||
int y = (p >> 6);
|
||||
all_holes_.emplace_back(
|
||||
(x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
|
||||
(y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
|
||||
(uint16_t)(map_pos + 0x400), true);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Overworld::LoadExits() {
|
||||
const int NumberOfOverworldExits = 0x4F;
|
||||
std::vector<OverworldExit> exits;
|
||||
@@ -2572,58 +2513,8 @@ absl::Status Overworld::SaveMap16Tiles() {
|
||||
}
|
||||
|
||||
absl::Status Overworld::SaveEntrances() {
|
||||
util::logf("Saving Entrances");
|
||||
|
||||
auto write_entrance = [&](int index, uint32_t map_addr, uint32_t pos_addr,
|
||||
uint32_t id_addr) -> absl::Status {
|
||||
// Mirrors ZeldaFullEditor/Save.cs::SaveOWEntrances (see lines ~1081-1085)
|
||||
// where MapID and MapPos are written as 16-bit words and EntranceID as a byte.
|
||||
RETURN_IF_ERROR(
|
||||
rom()->WriteShort(map_addr, all_entrances_[index].map_id_));
|
||||
RETURN_IF_ERROR(
|
||||
rom()->WriteShort(pos_addr, all_entrances_[index].map_pos_));
|
||||
RETURN_IF_ERROR(
|
||||
rom()->WriteByte(id_addr, all_entrances_[index].entrance_id_));
|
||||
return absl::OkStatus();
|
||||
};
|
||||
|
||||
// Always keep the legacy tables in sync for pure vanilla ROMs so e.g. Hyrule
|
||||
// Magic expects them. ZScream does the same in SaveOWEntrances.
|
||||
for (int i = 0; i < kNumOverworldEntrances; ++i) {
|
||||
RETURN_IF_ERROR(write_entrance(i, kOverworldEntranceMap + (i * 2),
|
||||
kOverworldEntrancePos + (i * 2),
|
||||
kOverworldEntranceEntranceId + i));
|
||||
}
|
||||
|
||||
if (expanded_entrances_) {
|
||||
// For ZS v3+ ROMs, mirror writes into the expanded tables the way
|
||||
// ZeldaFullEditor does when the ASM patch is active.
|
||||
for (int i = 0; i < kNumOverworldEntrances; ++i) {
|
||||
RETURN_IF_ERROR(write_entrance(i,
|
||||
kOverworldEntranceMapExpanded + (i * 2),
|
||||
kOverworldEntrancePosExpanded + (i * 2),
|
||||
kOverworldEntranceEntranceIdExpanded + i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < kNumOverworldHoles; ++i) {
|
||||
RETURN_IF_ERROR(
|
||||
rom()->WriteShort(kOverworldHoleArea + (i * 2), all_holes_[i].map_id_));
|
||||
|
||||
// ZeldaFullEditor/Data/Overworld.cs::LoadHoles() adds 0x400 when loading
|
||||
// (see lines ~1006-1014). SaveOWEntrances subtracts it before writing
|
||||
// (Save.cs lines ~1088-1092). We replicate that here so vanilla ROMs
|
||||
// receive the expected values.
|
||||
uint16_t rom_map_pos =
|
||||
static_cast<uint16_t>(all_holes_[i].map_pos_ >= 0x400
|
||||
? all_holes_[i].map_pos_ - 0x400
|
||||
: all_holes_[i].map_pos_);
|
||||
RETURN_IF_ERROR(
|
||||
rom()->WriteShort(kOverworldHolePos + (i * 2), rom_map_pos));
|
||||
RETURN_IF_ERROR(rom()->WriteByte(kOverworldHoleEntrance + i,
|
||||
all_holes_[i].entrance_id_));
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(::yaze::zelda3::SaveEntrances(rom_, all_entrances_, expanded_entrances_));
|
||||
RETURN_IF_ERROR(SaveHoles(rom_, all_holes_));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ constexpr int kMap32TileBRExpanded = 0x1F8000;
|
||||
constexpr int kMap32TileCountExpanded = 0x0067E0;
|
||||
constexpr int kMap32ExpandedFlagPos = 0x01772E; // 0x04
|
||||
constexpr int kMap16ExpandedFlagPos = 0x02FD28; // 0x0F
|
||||
constexpr int kOverworldEntranceExpandedFlagPos = 0x0DB895; // 0xB8
|
||||
|
||||
constexpr int overworldSpritesBeginingExpanded = 0x141438;
|
||||
constexpr int overworldSpritesZeldaExpanded = 0x141578;
|
||||
@@ -139,8 +138,6 @@ class Overworld {
|
||||
absl::Status Load(Rom *rom);
|
||||
absl::Status LoadOverworldMaps();
|
||||
void LoadTileTypes();
|
||||
absl::Status LoadEntrances();
|
||||
absl::Status LoadHoles();
|
||||
|
||||
absl::Status LoadExits();
|
||||
absl::Status LoadItems();
|
||||
|
||||
122
src/zelda3/overworld/overworld_entrance.cc
Normal file
122
src/zelda3/overworld/overworld_entrance.cc
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "zelda3/overworld/overworld_entrance.h"
|
||||
#include <vector>
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/rom.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze::zelda3 {
|
||||
|
||||
absl::StatusOr<std::vector<OverworldEntrance>> LoadEntrances(Rom* rom) {
|
||||
std::vector<OverworldEntrance> entrances;
|
||||
int ow_entrance_map_ptr = kOverworldEntranceMap;
|
||||
int ow_entrance_pos_ptr = kOverworldEntrancePos;
|
||||
int ow_entrance_id_ptr = kOverworldEntranceEntranceId;
|
||||
int num_entrances = 129;
|
||||
|
||||
// Check if expanded entrance data is actually present in ROM
|
||||
// The flag position should contain 0xB8 for vanilla, something else for expanded
|
||||
if (rom->data()[kOverworldEntranceExpandedFlagPos] != 0xB8) {
|
||||
// ROM has expanded entrance data - use expanded addresses
|
||||
ow_entrance_map_ptr = kOverworldEntranceMapExpanded;
|
||||
ow_entrance_pos_ptr = kOverworldEntrancePosExpanded;
|
||||
ow_entrance_id_ptr = kOverworldEntranceEntranceIdExpanded;
|
||||
num_entrances = 256; // Expanded entrance count
|
||||
}
|
||||
// Otherwise use vanilla addresses (already set above)
|
||||
|
||||
for (int i = 0; i < num_entrances; i++) {
|
||||
ASSIGN_OR_RETURN(auto map_id, rom->ReadWord(ow_entrance_map_ptr + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto map_pos,
|
||||
rom->ReadWord(ow_entrance_pos_ptr + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto entrance_id, rom->ReadByte(ow_entrance_id_ptr + i));
|
||||
int p = map_pos >> 1;
|
||||
int x = (p % 64);
|
||||
int y = (p >> 6);
|
||||
bool deleted = false;
|
||||
if (map_pos == 0xFFFF) {
|
||||
deleted = true;
|
||||
}
|
||||
entrances.emplace_back(
|
||||
(x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
|
||||
(y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
|
||||
deleted);
|
||||
}
|
||||
|
||||
return entrances;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::vector<OverworldEntrance>> LoadHoles(Rom* rom) {
|
||||
constexpr int kNumHoles = 0x13;
|
||||
std::vector<OverworldEntrance> holes;
|
||||
for (int i = 0; i < kNumHoles; i++) {
|
||||
ASSIGN_OR_RETURN(auto map_id, rom->ReadWord(kOverworldHoleArea + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto map_pos, rom->ReadWord(kOverworldHolePos + (i * 2)));
|
||||
ASSIGN_OR_RETURN(auto entrance_id,
|
||||
rom->ReadByte(kOverworldHoleEntrance + i));
|
||||
int p = (map_pos + 0x400) >> 1;
|
||||
int x = (p % 64);
|
||||
int y = (p >> 6);
|
||||
holes.emplace_back(
|
||||
(x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
|
||||
(y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
|
||||
(uint16_t)(map_pos + 0x400), true);
|
||||
}
|
||||
return holes;
|
||||
}
|
||||
|
||||
absl::Status SaveEntrances(Rom* rom,
|
||||
const std::vector<OverworldEntrance>& entrances, bool expanded_entrances) {
|
||||
|
||||
auto write_entrance = [&](int index, uint32_t map_addr, uint32_t pos_addr,
|
||||
uint32_t id_addr) -> absl::Status {
|
||||
// Mirrors ZeldaFullEditor/Save.cs::SaveOWEntrances (see lines ~1081-1085)
|
||||
// where MapID and MapPos are written as 16-bit words and EntranceID as a byte.
|
||||
RETURN_IF_ERROR(rom->WriteShort(map_addr, entrances[index].map_id_));
|
||||
RETURN_IF_ERROR(rom->WriteShort(pos_addr, entrances[index].map_pos_));
|
||||
RETURN_IF_ERROR(rom->WriteByte(id_addr, entrances[index].entrance_id_));
|
||||
return absl::OkStatus();
|
||||
};
|
||||
|
||||
// Always keep the legacy tables in sync for pure vanilla ROMs so e.g. Hyrule
|
||||
// Magic expects them. ZScream does the same in SaveOWEntrances.
|
||||
for (int i = 0; i < kNumOverworldEntrances; ++i) {
|
||||
RETURN_IF_ERROR(write_entrance(i, kOverworldEntranceMap + (i * 2),
|
||||
kOverworldEntrancePos + (i * 2),
|
||||
kOverworldEntranceEntranceId + i));
|
||||
}
|
||||
|
||||
if (expanded_entrances) {
|
||||
// For ZS v3+ ROMs, mirror writes into the expanded tables the way
|
||||
// ZeldaFullEditor does when the ASM patch is active.
|
||||
for (int i = 0; i < kNumOverworldEntrances; ++i) {
|
||||
RETURN_IF_ERROR(write_entrance(i, kOverworldEntranceMapExpanded + (i * 2),
|
||||
kOverworldEntrancePosExpanded + (i * 2),
|
||||
kOverworldEntranceEntranceIdExpanded + i));
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status SaveHoles(Rom* rom, const std::vector<OverworldEntrance>& holes) {
|
||||
for (int i = 0; i < kNumOverworldHoles; ++i) {
|
||||
RETURN_IF_ERROR(
|
||||
rom->WriteShort(kOverworldHoleArea + (i * 2), holes[i].map_id_));
|
||||
|
||||
// ZeldaFullEditor/Data/Overworld.cs::LoadHoles() adds 0x400 when loading
|
||||
// (see lines ~1006-1014). SaveOWEntrances subtracts it before writing
|
||||
// (Save.cs lines ~1088-1092). We replicate that here so vanilla ROMs
|
||||
// receive the expected values.
|
||||
uint16_t rom_map_pos = static_cast<uint16_t>(holes[i].map_pos_ >= 0x400
|
||||
? holes[i].map_pos_ - 0x400
|
||||
: holes[i].map_pos_);
|
||||
RETURN_IF_ERROR(rom->WriteShort(kOverworldHolePos + (i * 2), rom_map_pos));
|
||||
RETURN_IF_ERROR(
|
||||
rom->WriteByte(kOverworldHoleEntrance + i, holes[i].entrance_id_));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace yaze::zelda3
|
||||
@@ -1,14 +1,18 @@
|
||||
#ifndef YAZE_APP_ZELDA3_OVERWORLD_ENTRANCE_H
|
||||
#define YAZE_APP_ZELDA3_OVERWORLD_ENTRANCE_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "app/rom.h"
|
||||
#include "zelda3/common.h"
|
||||
#include "util/macro.h"
|
||||
#include "zelda3/common.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
namespace yaze::zelda3 {
|
||||
|
||||
// EXPANDED to 0x78000 to 0x7A000
|
||||
constexpr int kEntranceRoomEXP = 0x078000;
|
||||
@@ -49,6 +53,8 @@ constexpr int kOverworldEntranceMapExpanded = 0x0DB55F;
|
||||
constexpr int kOverworldEntrancePosExpanded = 0x0DB35F;
|
||||
constexpr int kOverworldEntranceEntranceIdExpanded = 0x0DB75F;
|
||||
|
||||
constexpr int kOverworldEntranceExpandedFlagPos = 0x0DB895; // 0xB8
|
||||
|
||||
// (0x13 entries, 2 bytes each) modified(less 0x400)
|
||||
// map16 coordinates for each hole
|
||||
constexpr int kOverworldHolePos = 0xDB800;
|
||||
@@ -124,8 +130,17 @@ struct OverworldEntranceTileTypes {
|
||||
std::array<uint16_t, kNumEntranceTileTypes> high;
|
||||
};
|
||||
|
||||
absl::StatusOr<std::vector<OverworldEntrance>> LoadEntrances(Rom* rom);
|
||||
absl::StatusOr<std::vector<OverworldEntrance>> LoadHoles(Rom* rom);
|
||||
|
||||
absl::Status SaveEntrances(Rom* rom,
|
||||
const std::vector<OverworldEntrance>& entrances,
|
||||
bool expanded_entrances);
|
||||
absl::Status SaveHoles(Rom* rom,
|
||||
const std::vector<OverworldEntrance>& holes);
|
||||
|
||||
inline absl::StatusOr<OverworldEntranceTileTypes> LoadEntranceTileTypes(
|
||||
Rom *rom) {
|
||||
Rom* rom) {
|
||||
OverworldEntranceTileTypes tiletypes;
|
||||
for (int i = 0; i < kNumEntranceTileTypes; i++) {
|
||||
ASSIGN_OR_RETURN(auto value_low,
|
||||
@@ -138,7 +153,6 @@ inline absl::StatusOr<OverworldEntranceTileTypes> LoadEntranceTileTypes(
|
||||
return tiletypes;
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
} // namespace yaze::zelda3
|
||||
|
||||
#endif
|
||||
|
||||
@@ -124,6 +124,15 @@ class OverworldExit : public GameEntity {
|
||||
|
||||
// Overworld overworld
|
||||
void UpdateMapProperties(uint16_t map_id) override {
|
||||
// CRITICAL FIX: Sync player position from base entity coordinates
|
||||
// The drag system in overworld_editor.cc updates x_/y_ (base GameEntity fields),
|
||||
// but exit auto-calculation uses x_player_/y_player_ for scroll/camera computation.
|
||||
// Without this sync, dragged exits retain old scroll values, causing save corruption.
|
||||
// Matches ZScream ExitMode.cs:229-244 where PlayerX/PlayerY are updated during drag,
|
||||
// then UpdateMapStuff recalculates scroll/camera from those values.
|
||||
x_player_ = static_cast<uint16_t>(x_);
|
||||
y_player_ = static_cast<uint16_t>(y_);
|
||||
|
||||
map_id_ = map_id;
|
||||
|
||||
int large = 256;
|
||||
|
||||
@@ -10,6 +10,7 @@ set(
|
||||
zelda3/music/tracker.cc
|
||||
zelda3/overworld/overworld.cc
|
||||
zelda3/overworld/overworld_map.cc
|
||||
zelda3/overworld/overworld_entrance.cc
|
||||
zelda3/palette_constants.cc
|
||||
zelda3/screen/dungeon_map.cc
|
||||
zelda3/screen/inventory.cc
|
||||
|
||||
Reference in New Issue
Block a user