yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_render_context.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5
6#include "absl/strings/str_format.h"
9
10namespace yaze::editor {
11
12namespace {
13
14int OwnerIndexForRoom(const zelda3::RoomCensus* census, int room_id) {
15 if (census == nullptr || room_id < 0 ||
16 room_id >= static_cast<int>(census->rooms.size())) {
17 return -1;
18 }
19 const int owner_index = census->rooms[room_id].owner_index;
20 return owner_index >= 0 &&
21 owner_index < static_cast<int>(census->owners.size())
22 ? owner_index
23 : -1;
24}
25
27 std::span<const DungeonRenderEntranceCandidate> entrances, int slot) {
28 const auto it =
29 std::find_if(entrances.begin(), entrances.end(),
30 [slot](const DungeonRenderEntranceCandidate& candidate) {
31 return candidate.slot == slot;
32 });
33 return it == entrances.end() ? nullptr : &*it;
34}
35
37 const zelda3::RoomCensus* census) {
38 if (context == nullptr || census == nullptr || context->owner_index < 0 ||
39 context->owner_index >= static_cast<int>(census->owners.size())) {
40 return;
41 }
42 const auto& owner = census->owners[context->owner_index];
43 context->owner_id = owner.id;
44 context->owner_name = owner.name;
45}
46
49 const DungeonRenderEntranceCandidate& entrance) {
50 context->source = source;
51 context->entrance_slot = entrance.slot;
52 context->entrance_blockset = entrance.main_gfx;
53 context->candidate_slots = {entrance.slot};
54}
55
56} // namespace
57
58std::string DungeonEntrancePreviewLabel(int slot) {
59 if (slot < 0)
60 return "None";
62 ? absl::StrFormat("Spawn %02X", slot)
63 : absl::StrFormat("Entrance %02X",
65}
66
68 switch (source) {
70 return "Selected entrance";
72 return "Room entrance";
74 return "Inferred owner entrance";
76 return "Room header fallback";
78 return "Ambiguous owner entrances";
79 }
80 return "Unknown";
81}
82
84 const DungeonRenderContext& context) {
85 if (!context.uses_entrance() &&
86 context.default_entrance.main_blockset != 0xFF) {
87 switch (context.default_entrance.source) {
89 return "Default direct entrance";
91 return "Default entrance inferred from dungeon map";
93 return "Default entrance inferred from neighboring room";
95 break;
96 }
97 }
99}
100
102 int room_id, int selected_entrance_slot, uint8_t room_header_blockset,
103 const zelda3::RoomCensus* census,
104 std::span<const DungeonRenderEntranceCandidate> entrances,
105 zelda3::RoomDefaultEntrance default_entrance,
106 const core::DungeonEntry* project_dungeon) {
107 DungeonRenderContext context;
108 context.room_id = room_id;
109 context.room_header_blockset = room_header_blockset;
110 context.default_entrance = default_entrance;
111 context.owner_index = OwnerIndexForRoom(census, room_id);
112 SetOwnerMetadata(&context, census);
113 if (project_dungeon != nullptr) {
114 context.owner_id = project_dungeon->id;
115 context.owner_name = project_dungeon->name;
116 }
117
118 const DungeonRenderEntranceCandidate* selected =
119 FindCandidate(entrances, selected_entrance_slot);
120 if (selected != nullptr) {
121 // An explicit preview entrance remains selected while browsing rooms.
122 // The game keeps $0AA1 until another entrance is loaded; census ownership
123 // must not silently replace the user's choice with different wall sheets.
125 *selected);
126 return context;
127 }
128
129 // Matrix adjacency is not dungeon membership. Keep that legacy loader
130 // heuristic out of automatic previews, including ambiguous entrances.
131 context.default_entrance = {};
132
133 const bool has_direct = std::any_of(
134 entrances.begin(), entrances.end(), [room_id](const auto& entrance) {
135 return entrance.slot >= zelda3::kNumDungeonSpawnPoints &&
136 entrance.room_id == room_id;
137 });
138
139 std::map<uint8_t, std::vector<const DungeonRenderEntranceCandidate*>>
140 candidates_by_gfx;
141 for (const auto& candidate : entrances) {
142 if (candidate.slot < zelda3::kNumDungeonSpawnPoints)
143 continue;
144 const bool same_owner =
145 project_dungeon != nullptr
146 ? std::any_of(project_dungeon->rooms.begin(),
147 project_dungeon->rooms.end(),
148 [&candidate](const auto& room) {
149 return room.id == candidate.room_id;
150 })
151 : context.owner_index >= 0 &&
152 OwnerIndexForRoom(census, candidate.room_id) ==
153 context.owner_index;
154 if (has_direct ? candidate.room_id == room_id : same_owner) {
155 candidates_by_gfx[candidate.main_gfx].push_back(&candidate);
156 context.candidate_slots.push_back(candidate.slot);
157 }
158 }
159
160 std::sort(context.candidate_slots.begin(), context.candidate_slots.end());
161 context.candidate_slots.erase(std::unique(context.candidate_slots.begin(),
162 context.candidate_slots.end()),
163 context.candidate_slots.end());
164
165 if (candidates_by_gfx.empty()) {
166 // A pause-map association is a fallback for ROMs without project metadata.
167 // Use the live entrance record, not a cached graphics byte.
168 if (project_dungeon == nullptr &&
170 if (const auto* entrance =
171 FindCandidate(entrances, default_entrance.entrance_id +
173 UseEntrance(&context,
175 *entrance);
176 }
177 }
178 return context;
179 }
180 if (candidates_by_gfx.size() > 1) {
182 return context;
183 }
184
185 auto& agreeing = candidates_by_gfx.begin()->second;
186 const auto inferred =
187 *std::min_element(agreeing.begin(), agreeing.end(),
188 [](const DungeonRenderEntranceCandidate* lhs,
190 return lhs->slot < rhs->slot;
191 });
192 context.source = has_direct
195 context.entrance_slot = inferred->slot;
196 context.entrance_blockset = inferred->main_gfx;
197 return context;
198}
199
200} // namespace yaze::editor
const DungeonRenderEntranceCandidate * FindCandidate(std::span< const DungeonRenderEntranceCandidate > entrances, int slot)
void UseEntrance(DungeonRenderContext *context, DungeonRenderContextSource source, const DungeonRenderEntranceCandidate &entrance)
void SetOwnerMetadata(DungeonRenderContext *context, const zelda3::RoomCensus *census)
int OwnerIndexForRoom(const zelda3::RoomCensus *census, int room_id)
Editors are the view controllers for the application.
std::string DungeonEntrancePreviewLabel(int slot)
const char * DungeonRenderContextSourceName(DungeonRenderContextSource source)
DungeonRenderContext ResolveDungeonRenderContext(int room_id, int selected_entrance_slot, uint8_t room_header_blockset, const zelda3::RoomCensus *census, std::span< const DungeonRenderEntranceCandidate > entrances, zelda3::RoomDefaultEntrance default_entrance, const core::DungeonEntry *project_dungeon)
constexpr int kNumDungeonSpawnPoints
A complete dungeon entry with rooms and connections.
std::vector< DungeonRoom > rooms
zelda3::RoomDefaultEntrance default_entrance
std::vector< RoomCensusOwner > owners
std::vector< RoomCensusEntry > rooms