yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_door_edges.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdlib>
5#include <set>
6#include <utility>
7
8namespace yaze::zelda3::atlas {
9
10namespace {
11
12constexpr int kRooms = 0x128;
13
14char WallLetter(int direction) {
15 switch (direction) {
16 case 0:
17 return 'N';
18 case 1:
19 return 'S';
20 case 2:
21 return 'W';
22 case 3:
23 return 'E';
24 }
25 return '?';
26}
27
28char Opposite(char wall) {
29 switch (wall) {
30 case 'N':
31 return 'S';
32 case 'S':
33 return 'N';
34 case 'E':
35 return 'W';
36 case 'W':
37 return 'E';
38 }
39 return '?';
40}
41
42// room_data.py CAT_RANK; categories it does not list rank as "open".
44 switch (category) {
46 return 5;
48 return 4;
50 return 3;
52 return 2;
54 return 0;
55 default:
56 return 1;
57 }
58}
59
64
65} // namespace
66
68 switch (category) {
70 return "open";
72 return "onesided";
74 return "shutter";
76 return "key";
78 return "bigkey";
80 return "bomb";
82 return "exit";
84 return "keystairs";
86 return "other";
88 return "ignore";
89 }
90 return "other";
91}
92
94 switch (door_type) {
95 case 0x00: // Normal door
96 case 0x02: // Normal door (lower layer)
97 case 0x32: // Curtain door
98 case 0x46: // Explicit room door
100 case 0x40:
102 case 0x18: // Double-sided shutter
103 case 0x36: // Bottom-sided shutter
104 case 0x38: // Top-sided shutter
105 case 0x44: // Double-sided shutter (lower layer)
106 case 0x48: // Bottom-sided shutter (lower layer)
108 case 0x1C:
109 return DoorCategory::kKey;
110 case 0x1E:
112 case 0x28: // Dash wall
113 case 0x2E: // Bombable door
114 case 0x30: // Exploding wall
115 return DoorCategory::kBomb;
116 case 0x04: // Exit (lower layer)
117 case 0x0A: // Fancy dungeon exit
118 case 0x0E: // Cave exit
119 case 0x10: // Lit cave exit (lower layer)
120 case 0x12: // Exit marker
121 return DoorCategory::kExit;
122 case 0x20: // Small key stairs (up)
123 case 0x22: // Small key stairs (down)
125 case 0x16: // Layer swap marker
127 }
129}
130
131std::vector<AtlasDoor> OuterWallDoors(const std::vector<AtlasDoorFact>& doors) {
132 std::vector<AtlasDoor> out;
133 for (const auto& fact : doors) {
134 const DoorCategory category = DoorCategoryForType(fact.type);
135 const char wall = WallLetter(fact.direction);
136 const bool outer = (wall == 'N' && fact.tile_y <= 8) ||
137 (wall == 'S' && fact.tile_y >= 50) ||
138 (wall == 'W' && fact.tile_x <= 8) ||
139 (wall == 'E' && fact.tile_x >= 50);
140 if (category == DoorCategory::kIgnore || !outer) {
141 continue;
142 }
143 AtlasDoor door;
144 door.wall = wall;
145 door.pos = (wall == 'N' || wall == 'S') ? fact.tile_x + 2 : fact.tile_y + 2;
146 door.type = fact.type;
147 door.type_name = fact.type_name;
148 door.category = category;
149 out.push_back(std::move(door));
150 }
151 std::vector<std::pair<char, int>> exits;
152 for (const auto& door : out) {
153 if (door.category == DoorCategory::kExit) {
154 exits.emplace_back(door.wall, door.pos);
155 }
156 }
157 for (auto& door : out) {
158 if (door.category == DoorCategory::kExit) {
159 continue;
160 }
161 for (const auto& [wall, pos] : exits) {
162 if (wall == door.wall && std::abs(pos - door.pos) <= 2) {
163 door.category = DoorCategory::kExit;
164 break;
165 }
166 }
167 }
168 return out;
169}
170
171std::vector<DoorEdge> PairDoorEdges(
172 const std::vector<std::vector<AtlasDoor>>& doors_by_room) {
173 std::vector<DoorEdge> out;
174 std::set<std::pair<int, size_t>> used;
175 const int rooms = std::min<int>(kRooms, doors_by_room.size());
176 for (int a = 0; a < rooms; ++a) {
177 // Same order as room_data.py: E, S, W, N. Rows are 16 rooms wide across
178 // the whole $000-$127 range (0xF5 south is 0x105).
179 const std::pair<char, int> neighbours[] = {
180 {'E', (a & 0xF) != 0xF ? a + 1 : -1},
181 {'S', a + 0x10 < kRooms ? a + 0x10 : -1},
182 {'W', (a & 0xF) != 0 ? a - 1 : -1},
183 {'N', a >= 0x10 ? a - 0x10 : -1}};
184 for (const auto& [dir, b] : neighbours) {
185 if (b < 0 || b >= rooms) {
186 continue;
187 }
188 const auto& doors_a = doors_by_room[a];
189 const auto& doors_b = doors_by_room[b];
190 for (size_t i = 0; i < doors_a.size(); ++i) {
191 const AtlasDoor& da = doors_a[i];
192 if (da.wall != dir || !Pairable(da.category) || used.contains({a, i})) {
193 continue;
194 }
195 int match = -1;
196 for (size_t j = 0; j < doors_b.size(); ++j) {
197 const AtlasDoor& db = doors_b[j];
198 if (db.wall == Opposite(dir) && Pairable(db.category) &&
199 std::abs(db.pos - da.pos) <= 3 && !used.contains({b, j})) {
200 match = static_cast<int>(j);
201 break;
202 }
203 }
204 used.insert({a, i});
205 DoorEdge edge;
206 edge.a = a;
207 edge.b = b;
208 edge.dir = dir;
209 edge.pos = da.pos;
210 if (match >= 0) {
211 const AtlasDoor& db = doors_b[match];
212 used.insert({b, static_cast<size_t>(match)});
213 // Python max(): the first of equal ranks wins.
215 Rank(db.category) > Rank(da.category) ? db.category : da.category;
218 }
219 edge.category = category;
220 edge.both_sides = true;
223 edge.types = {da.type_name, db.type_name};
224 } else {
227 : da.category;
228 edge.both_sides = false;
230 edge.types = {da.type_name};
231 }
232 out.push_back(std::move(edge));
233 }
234 }
235 }
236 return out;
237}
238
239} // namespace yaze::zelda3::atlas
std::vector< AtlasDoor > OuterWallDoors(const std::vector< AtlasDoorFact > &doors)
DoorCategory DoorCategoryForType(int door_type)
std::vector< DoorEdge > PairDoorEdges(const std::vector< std::vector< AtlasDoor > > &doors_by_room)
const char * DoorCategoryName(DoorCategory category)
std::vector< std::string > types