yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_census.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_ROOM_CENSUS_H_
2#define YAZE_ZELDA3_DUNGEON_ROOM_CENSUS_H_
3
4// Room census: who owns each of the 296 underworld rooms, and which rooms are
5// free or reclaimable for new content.
6//
7// Definitions (from the Oracle room census, 2026-09-26):
8// - A room is REACHED when a breadth-first search from the placed overworld
9// entrances, overworld holes and spawn points gets to it over strong room
10// links (room_links.h, shared with the z3ed graph commands): mutual doors,
11// teleport doors, stair objects (resolved to their header slot), project
12// warp tags, warp tiles, and the holewarp of a room with pits, pit tiles,
13// tag-driven holes or falling-floor sprites that is not listed in
14// RoomsWithPitDamage. Doorless walk-off edges are not modeled; instead a
15// non-empty room drawn on the pause map of a dungeon that is itself reached
16// counts as reached. Reachability does not depend on project ownership:
17// the project only names owners and keeps its listed rooms in use.
18// - EMPTY: 0 room objects, or 1 object and at most 1 sprite.
19// - FREE: not reached, not listed by the project, and empty.
20// - RECLAIMABLE: not reached, not listed by the project, not empty, and its
21// objects are vanilla leftovers (Jaccard similarity to the vanilla room's
22// objects >= kReclaimableVanillaSimilarity; partly edited leftovers down to
23// kPartlyEditedVanillaSimilarity are reclaimable with a caveat).
24// - IN USE: everything else. Unreached authored rooms stay IN USE with an
25// "orphan" reason; they need an owner decision.
26//
27// The model is split in two: CollectRoomCensusInput() reads the ROM, and
28// BuildRoomCensus() classifies. The UI (Room Matrix) and z3ed
29// `dungeon-room-census` both call these, so they report the same answer.
30
31#include <array>
32#include <cstdint>
33#include <functional>
34#include <optional>
35#include <set>
36#include <string>
37#include <vector>
38
39#include "absl/status/statusor.h"
40#include "util/stop_token.h"
42
43namespace yaze {
44class Rom;
45namespace core {
46struct ProjectRegistry;
47struct RomAddressOverrides;
48class HackManifest;
49} // namespace core
50
51namespace zelda3 {
52
53class Room;
54
56inline constexpr float kReclaimableVanillaSimilarity = 0.90f;
57// At least a third of the objects still vanilla. Oracle 0x30 (Agahnim tower
58// chamber with an Oracle stair, 38%) qualifies; 0x11A (rebuilt interior, 25%)
59// does not.
60inline constexpr float kPartlyEditedVanillaSimilarity = 1.0f / 3.0f;
61inline constexpr int kRoomCensusInteriorDungeonId = 0xFF;
62
63enum class RoomCensusStatus : uint8_t { kInUse, kFree, kReclaimable };
64
67 bool strong = false; // Counts for reachability.
68 int from_room = -1; // Source room, or -1 for entrances/spawns.
69 int entrance_id = -1;
70 std::string detail; // Human-readable description.
71};
72
73// Per-room facts read from the ROM: the link facts (room_links.h) plus the
74// room's content. Plain data so tests can build them.
76 int object_count = 0; // Encoded room-stream objects.
77 int sprite_count = 0;
78 int chest_count = 0;
79 // Similarity of the room's objects to the vanilla room's objects, 0..1,
80 // or negative when no vanilla baseline is available.
81 float vanilla_similarity = -1.0f;
82};
83
85 int entrance_id = 0;
86 int room_id = 0;
89 std::string placement; // e.g. "OW 0x0B"
90 int ow_map = -1; // Overworld map of the placement, -1 = none.
91};
92
94 std::string id; // e.g. "D1"
95 std::string name; // e.g. "Mushroom Grotto"
96 std::vector<int> rooms;
97};
98
100 std::vector<RoomCensusRoomFacts> rooms; // kRoomCensusRoomCount entries.
101 std::vector<RoomCensusEntranceFact> entrances;
102 // Project dungeon ownership. Empty: derive owners from entrances.
103 std::vector<RoomCensusOwnerGroup> project_owners;
104 std::set<uint8_t> warp_tag_ids; // Tags that warp through stair slots.
105 // Rooms on each pause map; map i belongs to dungeon ID i * 2.
106 std::vector<std::vector<int>> pause_map_rooms;
110};
111
113 std::string id;
114 std::string name;
115 bool interior = false;
116 bool from_project = false;
117 int dungeon_id = -1; // Derived owners only.
118 int room_count = 0;
119};
120
122 int room_id = 0;
124 int owner_index = -1; // Into RoomCensus::owners, -1 = no owner.
125 bool interior = false;
126 bool reached = false;
127 bool empty = false;
128 bool orphan = false; // In use, but nothing reaches it.
131 float vanilla_similarity = -1.0f;
132 std::vector<std::string> reasons;
133 std::vector<RoomReference> references; // Inbound.
134};
135
137 std::vector<int> rooms; // Sorted.
138 std::vector<int> core_rooms; // The contiguous core.
139 std::string summary; // "Row 9: 0x93-0x96 + 0xA0/0xA6/0xB0, 7 rooms"
140};
141
143 std::vector<RoomCensusEntry> rooms; // kRoomCensusRoomCount entries.
144 std::vector<RoomCensusOwner> owners;
146 // Derived owners use vanilla dungeon names (>= 80% of rooms with objects
147 // are vanilla); otherwise "Dungeon ID 0xNN".
152 int free_count = 0;
157 // Free-room blocks, largest first: a contiguous (4-connected, same $A1
158 // page) core of 3+ free rooms plus smaller free cores within 3 grid cells.
159 std::vector<RoomCensusCluster> free_clusters;
160};
161
163 // Cancellation discards all partial facts. Background owners request stop
164 // before invalidating or joining the job; synchronous callers need no token.
166 // Background callers must capture on the owner thread and retain this
167 // snapshot for the entire call. Null keeps synchronous live-editor behavior.
171 const core::HackManifest* manifest = nullptr; // Warp tags.
172 // Optional vanilla ROM used instead of the built-in fingerprints.
173 Rom* vanilla_rom = nullptr;
174 // Optional: called once per room with the room as the census loaded it
175 // (objects, doors, chests, pot items and sprites), so callers such as the
176 // Atlas read extra facts without loading all 296 rooms a second time.
177 std::function<void(int room_id, const Room& room)> room_visitor;
178};
179
180// Reads everything the census needs from `rom`.
181absl::StatusOr<RoomCensusInput> CollectRoomCensusInput(
182 Rom* rom, const RoomCensusOptions& options = {});
183
184// Classifies every room. Pure function of `input`.
185RoomCensus BuildRoomCensus(const RoomCensusInput& input);
186
187// Convenience: CollectRoomCensusInput + BuildRoomCensus.
188absl::StatusOr<RoomCensus> ComputeRoomCensus(
189 Rom* rom, const RoomCensusOptions& options = {});
190
191std::vector<RoomCensusOwnerGroup> RoomCensusOwnersFromProject(
192 const core::ProjectRegistry& registry);
193
194// Room tags that warp through header stair slots (a manifest tag named
195// "WarpTag", e.g. Oracle's 0x3A).
196std::set<uint8_t> RoomCensusWarpTagsFromManifest(
197 const core::HackManifest& manifest);
198
199const char* RoomCensusStatusName(RoomCensusStatus status);
200
201// "Row 9: 0x93-0x96 + 0xA0/0xA6/0xB0, 7 rooms"
202std::string FormatRoomBlockSummary(const std::vector<int>& rooms);
203
204// Object fingerprint used for vanilla comparison: sorted 16-bit hashes of
205// (object id, x, y, size). Hashes are one-way; they identify a vanilla room's
206// objects without storing the room data.
207uint16_t HashRoomCensusObject(int id, int x, int y, int size);
208float RoomFingerprintSimilarity(const std::vector<uint16_t>& a,
209 const std::vector<uint16_t>& b);
210
211// Fingerprints of every room in `rom`, for generating the built-in table.
212absl::StatusOr<std::vector<std::vector<uint16_t>>> ComputeRoomFingerprints(
213 Rom* rom, util::StopToken stop_token = {});
214
215} // namespace zelda3
216} // namespace yaze
217
218#endif // YAZE_ZELDA3_DUNGEON_ROOM_CENSUS_H_
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:49
Loads and queries the hack manifest JSON for yaze-ASM integration.
absl::StatusOr< RoomCensusInput > CollectRoomCensusInput(Rom *rom, const RoomCensusOptions &options)
std::vector< RoomCensusOwnerGroup > RoomCensusOwnersFromProject(const core::ProjectRegistry &registry)
std::set< uint8_t > RoomCensusWarpTagsFromManifest(const core::HackManifest &manifest)
absl::StatusOr< RoomCensus > ComputeRoomCensus(Rom *rom, const RoomCensusOptions &options)
std::string FormatRoomBlockSummary(const std::vector< int > &rooms_in)
constexpr int kRoomCensusInteriorDungeonId
Definition room_census.h:61
const char * RoomCensusStatusName(RoomCensusStatus status)
absl::StatusOr< std::vector< std::vector< uint16_t > > > ComputeRoomFingerprints(Rom *rom, util::StopToken stop_token)
constexpr int kDungeonRoomCount
Definition room_links.h:51
constexpr float kReclaimableVanillaSimilarity
Definition room_census.h:56
RoomCensus BuildRoomCensus(const RoomCensusInput &input)
float RoomFingerprintSimilarity(const std::vector< uint16_t > &a, const std::vector< uint16_t > &b)
constexpr float kPartlyEditedVanillaSimilarity
Definition room_census.h:60
constexpr int kRoomCensusRoomCount
Definition room_census.h:55
uint16_t HashRoomCensusObject(int id, int x, int y, int size)
Project-level registry data loaded from the Oracle planning outputs.
std::vector< int > core_rooms
std::vector< RoomReference > references
std::vector< std::string > reasons
std::vector< RoomCensusEntranceFact > entrances
std::vector< std::vector< int > > pause_map_rooms
std::vector< RoomCensusOwnerGroup > project_owners
std::set< uint8_t > warp_tag_ids
std::vector< RoomCensusRoomFacts > rooms
std::function< void(int room_id, const Room &room) room_visitor)
const core::RomAddressOverrides * address_overrides
const core::ProjectRegistry * project
const core::HackManifest * manifest
const CustomObjectRenderSnapshot * custom_objects
std::vector< RoomCensusOwner > owners
std::vector< RoomCensusCluster > free_clusters
std::string vanilla_baseline_source
std::vector< RoomCensusEntry > rooms
RoomReferenceKind kind
Definition room_census.h:66