yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_validator.cc
Go to the documentation of this file.
1#include "dungeon_validator.h"
2
3#include "absl/strings/str_format.h"
5
6namespace yaze {
7namespace zelda3 {
8namespace {
9
10constexpr int16_t kBigKeyLockObjectId = 0xF98;
11constexpr size_t kMaxStatefulRoomEventSlots = 6;
12
13} // namespace
14
16 ValidationResult result;
17
18 // Check object count
19 size_t object_count = room.GetTileObjects().size();
20 if (object_count > kMaxTileObjects) {
21 result.warnings.push_back(absl::StrFormat(
22 "High object count (%zu > %d). May cause lag or memory issues.",
23 object_count, kMaxTileObjects));
24 }
25
26 // Check sprite count
27 size_t sprite_count = room.GetSprites().size();
28 if (sprite_count > kMaxTotalSprites) {
29 result.warnings.push_back(
30 absl::StrFormat("Too many sprites (%zu > %d). Game limit is strict.",
31 sprite_count, kMaxTotalSprites));
32 }
33
34 // Stateful chests and big-key locks share a six-entry room-event table in
35 // the game engine. Chests use a chest-only index and then synchronize the
36 // shared index, so a chest after a lock reuses an earlier event slot.
37 // Validate the encoded stream order (primary, BG2 overlay, BG1 overlay), not
38 // the editor vector order, which may interleave objects from those lists.
39 size_t chest_slot_index = 0;
40 size_t shared_event_slot_index = 0;
41 bool saw_big_key_lock = false;
42 int first_chest_after_lock = -1;
43 int first_out_of_range_object = -1;
44 size_t first_out_of_range_slot = 0;
45 for (uint8_t list_index = 0; list_index < 3; ++list_index) {
46 for (const auto& obj : room.GetTileObjects()) {
47 if (!UsesRoomObjectStream(obj) || obj.GetLayerValue() != list_index) {
48 continue;
49 }
50
51 if (obj.id_ == kBigKeyLockObjectId) {
52 if (shared_event_slot_index >= kMaxStatefulRoomEventSlots &&
53 first_out_of_range_object < 0) {
54 first_out_of_range_object = obj.id_;
55 first_out_of_range_slot = shared_event_slot_index;
56 }
57 saw_big_key_lock = true;
58 ++shared_event_slot_index;
59 } else if (IsStatefulChestObjectId(obj.id_)) {
60 if (chest_slot_index >= kMaxStatefulRoomEventSlots &&
61 first_out_of_range_object < 0) {
62 first_out_of_range_object = obj.id_;
63 first_out_of_range_slot = chest_slot_index;
64 }
65 if (saw_big_key_lock && first_chest_after_lock < 0) {
66 first_chest_after_lock = obj.id_;
67 }
68 ++chest_slot_index;
69 // RoomDraw_Chest and RoomDraw_BigChest advance the chest-only $0496
70 // index, then copy its next value into shared index $0498.
71 shared_event_slot_index = chest_slot_index;
72 }
73 }
74 }
75
76 if (first_chest_after_lock >= 0) {
77 result.warnings.push_back(absl::StrFormat(
78 "Stateful chest 0x%03X appears after big-key lock 0xF98 in "
79 "room-object stream order; move stateful chests before locks to avoid "
80 "room-state slot conflicts.",
81 first_chest_after_lock));
82 }
83 if (first_out_of_range_object >= 0) {
84 result.warnings.push_back(absl::StrFormat(
85 "Stateful object 0x%03X accesses room-event slot %zu; RoomFlagMask "
86 "only defines slots 0-%zu.",
87 first_out_of_range_object, first_out_of_range_slot,
88 kMaxStatefulRoomEventSlots - 1));
89 }
90
91 // Check bounds
92 for (const auto& obj : room.GetTileObjects()) {
93 if (UsesRoomObjectStream(obj)) {
94 const absl::Status status = ValidateRoomObjectStreamEntryForSave(obj);
95 if (!status.ok()) {
96 result.errors.emplace_back(std::string(status.message()));
97 result.is_valid = false;
98 }
99 continue;
100 }
101
102 const int layer = static_cast<int>(obj.GetLayerValue());
103 if (layer > 1) {
104 result.errors.push_back(absl::StrFormat(
105 "Special-table object 0x%02X has invalid layer selector %d; "
106 "expected upper/BG1 (0) or lower/BG2 (1)",
107 obj.id_, layer));
108 result.is_valid = false;
109 }
110
111 if (obj.x_ < 0 || obj.x_ >= 64 || obj.y_ < 0 || obj.y_ >= 64) {
112 result.errors.push_back(absl::StrFormat(
113 "Object 0x%02X out of bounds at (%d, %d)", obj.id_, obj.x_, obj.y_));
114 result.is_valid = false;
115 }
116 }
117
118 return result;
119}
120
121} // namespace zelda3
122} // namespace yaze
ValidationResult ValidateRoom(const Room &room)
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:254
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:383
constexpr size_t kMaxTileObjects
bool UsesRoomObjectStream(const RoomObject &object)
constexpr bool IsStatefulChestObjectId(int object_id)
absl::Status ValidateRoomObjectStreamEntryForSave(const RoomObject &object)
constexpr size_t kMaxTotalSprites
std::vector< std::string > errors
std::vector< std::string > warnings