yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
corner_routines.cc
Go to the documentation of this file.
1#include "corner_routines.h"
2
6
7namespace yaze {
8namespace zelda3 {
9namespace draw_routines {
10
11namespace {
12
14 // RoomDraw_SanctuaryWall ($019B56): twenty facade columns write directly
15 // to $7E2000. The final 4x3 center uses the active tilemap pointers instead.
16 if (ctx.tiles.size() < 24) {
17 return;
18 }
19 auto& facade_bg = ctx.object.layer_ == RoomObject::LayerType::BG2 &&
20 ctx.secondary_bg != nullptr
21 ? *ctx.secondary_bg
22 : ctx.target_bg;
23
24 for (int row = 0; row < 6; ++row) {
25 const auto& first = ctx.tiles[row];
26 auto first_mirrored = first;
27 first_mirrored.horizontal_mirror_ = true; // OR $4000, not XOR.
28 for (int column : {0, 4, 8, 14, 18, 22}) {
29 DrawRoutineUtils::WriteTile8(facade_bg, ctx.object.x_ + column,
30 ctx.object.y_ + row, first);
31 }
32 for (int column : {1, 5, 9, 15, 19, 23}) {
33 DrawRoutineUtils::WriteTile8(facade_bg, ctx.object.x_ + column,
34 ctx.object.y_ + row, first_mirrored);
35 }
36 const auto& second = ctx.tiles[6 + row];
37 auto second_mirrored = second;
38 second_mirrored.horizontal_mirror_ = true;
39 for (int column : {2, 6, 16, 20}) {
40 DrawRoutineUtils::WriteTile8(facade_bg, ctx.object.x_ + column,
41 ctx.object.y_ + row, second);
42 }
43 for (int column : {3, 7, 17, 21}) {
44 DrawRoutineUtils::WriteTile8(facade_bg, ctx.object.x_ + column,
45 ctx.object.y_ + row, second_mirrored);
46 }
47 }
48
49 // $019BC6 advances past both facade columns, then $019BD6 tail-calls
50 // RoomDraw_1x3N_rightwards with A=4 at the original anchor plus ten tiles.
51 for (int column = 0; column < 4; ++column) {
52 for (int row = 0; row < 3; ++row) {
53 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + 10 + column,
54 ctx.object.y_ + row,
55 ctx.tiles[12 + column * 3 + row]);
56 }
57 }
58}
59
66
68 // USDASM parity:
69 // RoomDraw_DiagonalCeiling* uses GetSize_1to16_timesA with A=4.
70 // Effective side length is (size_nibble & 0x0F) + 4.
71 if (ctx.tiles.empty()) {
72 return;
73 }
74
75 const int side = (ctx.object.size_ & 0x0F) + 4;
76 const gfx::TileInfo& fill_tile = ctx.tiles[0];
77
78 // Each USDASM variant changes the repeated span and/or row direction. The
79 // names describe the triangle, not a common corner-origin mirror transform.
80 for (int row = 0; row < side; ++row) {
81 int first_column = 0;
82 int last_column = side - row - 1;
83 int y = ctx.object.y_ + row;
84
85 switch (anchor) {
86 case DiagonalCeilingAnchor::kTopLeft:
87 break;
88 case DiagonalCeilingAnchor::kBottomLeft:
89 last_column = row;
90 break;
91 case DiagonalCeilingAnchor::kTopRight:
92 first_column = row;
93 last_column = side - 1;
94 break;
95 case DiagonalCeilingAnchor::kBottomRight:
96 first_column = row;
97 last_column = side - 1;
98 y = ctx.object.y_ - row;
99 break;
100 }
101
102 for (int column = first_column; column <= last_column; ++column) {
103 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + column, y,
104 fill_tile);
105 }
106 }
107}
108
109} // namespace
110
111// Note: DrawWaterFace is defined in special_routines.cc along with other
112// water face variants (Empty, Spitting, Drenching)
113
114void DrawCorner4x4(const DrawContext& ctx) {
115 // Canonical 4x4 corners consume 16 words. Shorter payloads below are editor
116 // compatibility fallbacks, not alternate vanilla subtype-2 formats.
117
118 if (ctx.tiles.size() >= 16) {
119 // Full 4x4 pattern - Column-major ordering per ZScream
120 int tid = 0;
121 for (int xx = 0; xx < 4; xx++) {
122 for (int yy = 0; yy < 4; yy++) {
124 ctx.object.y_ + yy, ctx.tiles[tid++]);
125 }
126 }
127 } else if (ctx.tiles.size() >= 8) {
128 // Short-payload fallback: 2x4 column-major.
129 int tid = 0;
130 for (int xx = 0; xx < 2; xx++) {
131 for (int yy = 0; yy < 4; yy++) {
133 ctx.object.y_ + yy, ctx.tiles[tid++]);
134 }
135 }
136 } else if (ctx.tiles.size() >= 4) {
137 // Fallback: 2x2 pattern
138 int tid = 0;
139 for (int xx = 0; xx < 2; xx++) {
140 for (int yy = 0; yy < 2; yy++) {
142 ctx.object.y_ + yy, ctx.tiles[tid++]);
143 }
144 }
145 }
146}
147
149 // USDASM: RoomDraw_4x4Corner_BothBG ($01:9813)
150 // Canonical shape is 4 columns x 4 rows (column-major). We retain smaller
151 // fallback shapes for abbreviated editor payloads.
152 if (ctx.tiles.size() >= 16) {
153 DrawCorner4x4(ctx);
154 } else if (ctx.tiles.size() >= 8) {
155 // Fallback: 2x4 corner pattern (column-major)
156 int tid = 0;
157 for (int xx = 0; xx < 2; xx++) {
158 for (int yy = 0; yy < 4; yy++) {
160 ctx.object.y_ + yy, ctx.tiles[tid++]);
161 }
162 }
163 } else if (ctx.tiles.size() >= 4) {
164 // Fallback: 2x2 pattern
165 DrawWaterFace(ctx);
166 }
167}
168
170 // USDASM: RoomDraw_WeirdCornerBottom_BothBG ($01:9854)
171 // ASM sets count=3 and reuses RoomDraw_4x4Corner_BothBG_set_count:
172 // 3 columns x 4 rows, column-major (12 tiles).
173 if (ctx.tiles.size() >= 12) {
174 int tid = 0;
175 for (int xx = 0; xx < 3; xx++) {
176 for (int yy = 0; yy < 4; yy++) {
178 ctx.object.y_ + yy, ctx.tiles[tid++]);
179 }
180 }
181 } else if (ctx.tiles.size() >= 8) {
182 // Fallback for truncated payloads: 2x4 column-major.
183 int tid = 0;
184 for (int xx = 0; xx < 2; xx++) {
185 for (int yy = 0; yy < 4; yy++) {
187 ctx.object.y_ + yy, ctx.tiles[tid++]);
188 }
189 }
190 } else if (ctx.tiles.size() >= 4) {
191 DrawWaterFace(ctx);
192 }
193}
194
196 // USDASM: RoomDraw_WeirdCornerTop_BothBG ($01:985C)
197 // ASM writes 3 rows per column and advances source by 6 bytes each loop:
198 // 4 columns x 3 rows, column-major (12 tiles).
199 if (ctx.tiles.size() >= 12) {
200 int tid = 0;
201 for (int xx = 0; xx < 4; xx++) {
202 for (int yy = 0; yy < 3; yy++) {
204 ctx.object.y_ + yy, ctx.tiles[tid++]);
205 }
206 }
207 } else if (ctx.tiles.size() >= 8) {
208 // Fallback for truncated payloads: 2x4 column-major.
209 int tid = 0;
210 for (int xx = 0; xx < 2; xx++) {
211 for (int yy = 0; yy < 4; yy++) {
213 ctx.object.y_ + yy, ctx.tiles[tid++]);
214 }
215 }
216 } else if (ctx.tiles.size() >= 4) {
217 DrawWaterFace(ctx);
218 }
219}
220
222 DrawDiagonalCeiling(ctx, DiagonalCeilingAnchor::kTopLeft);
223}
224
226 DrawDiagonalCeiling(ctx, DiagonalCeilingAnchor::kBottomLeft);
227}
228
230 DrawDiagonalCeiling(ctx, DiagonalCeilingAnchor::kTopRight);
231}
232
234 DrawDiagonalCeiling(ctx, DiagonalCeilingAnchor::kBottomRight);
235}
236
237void RegisterCornerRoutines(std::vector<DrawRoutineInfo>& registry) {
238 // Note: Routine IDs are assigned based on the assembly routine table
239 // These corner routines are part of the core 40 draw routines
240
241 registry.push_back(DrawRoutineInfo{
243 .name = "SanctuaryWall",
244 .function = DrawSanctuaryWall,
245 .draws_to_both_bgs = false, // Mixed routing, not duplicated geometry.
246 .base_width = 24,
247 .base_height = 6,
248 .min_tiles = 24,
250 });
251
252 registry.push_back(DrawRoutineInfo{
253 .id = 19, // DrawCorner4x4
254 .name = "Corner4x4",
255 .function = DrawCorner4x4,
256 // Structural layout routine: writes to both BG1 and BG2 in the engine.
257 .draws_to_both_bgs = true,
258 .base_width = 4,
259 .base_height = 4,
260 .min_tiles = 4, // handles 4, 8, or 16 tile variants
262 });
263
264 registry.push_back(DrawRoutineInfo{
265 .id = 35, // Draw4x4Corner_BothBG
266 .name = "4x4Corner_BothBG",
267 .function = Draw4x4Corner_BothBG,
268 .draws_to_both_bgs = true,
269 .base_width = 4,
270 .base_height = 4,
271 .min_tiles = 4, // handles 4, 8, or 16 tile variants
273 });
274
275 registry.push_back(DrawRoutineInfo{
276 .id = 36, // DrawWeirdCornerBottom_BothBG
277 .name = "WeirdCornerBottom_BothBG",
279 .draws_to_both_bgs = true,
280 .base_width = 3,
281 .base_height = 4,
282 .min_tiles = 12, // Enforce canonical USDASM payload size.
284 });
285
286 registry.push_back(DrawRoutineInfo{
287 .id = 37, // DrawWeirdCornerTop_BothBG
288 .name = "WeirdCornerTop_BothBG",
289 .function = DrawWeirdCornerTop_BothBG,
290 .draws_to_both_bgs = true,
291 .base_width = 4,
292 .base_height = 3,
293 .min_tiles = 12, // Enforce canonical USDASM payload size.
295 });
296
297 registry.push_back(DrawRoutineInfo{
299 .name = "DiagonalCeilingTopLeft",
300 .function = DrawDiagonalCeilingTopLeft,
301 .draws_to_both_bgs = false,
302 .base_width = 4, // size_nibble + 4
303 .base_height = 4, // size_nibble + 4
304 .min_tiles = 1,
306 });
307
308 registry.push_back(DrawRoutineInfo{
310 .name = "DiagonalCeilingBottomLeft",
312 .draws_to_both_bgs = false,
313 .base_width = 4, // size_nibble + 4
314 .base_height = 4, // size_nibble + 4
315 .min_tiles = 1,
317 });
318
319 registry.push_back(DrawRoutineInfo{
321 .name = "DiagonalCeilingTopRight",
322 .function = DrawDiagonalCeilingTopRight,
323 .draws_to_both_bgs = false,
324 .base_width = 4, // size_nibble + 4
325 .base_height = 4, // size_nibble + 4
326 .min_tiles = 1,
328 });
329
330 registry.push_back(DrawRoutineInfo{
332 .name = "DiagonalCeilingBottomRight",
334 .draws_to_both_bgs = false,
335 .base_width = 4, // size_nibble + 4
336 .base_height = 4, // size_nibble + 4
337 .min_tiles = 1,
339 });
340}
341
342} // namespace draw_routines
343} // namespace zelda3
344} // namespace yaze
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
void WriteTile8(gfx::BackgroundBuffer &bg, int tile_x, int tile_y, const gfx::TileInfo &tile_info)
Write an 8x8 tile to the background buffer.
void DrawDiagonalCeiling(const DrawContext &ctx, DiagonalCeilingAnchor anchor)
void DrawWeirdCornerTop_BothBG(const DrawContext &ctx)
Draw a weird corner top pattern for both BG layers.
void DrawDiagonalCeilingTopLeft(const DrawContext &ctx)
Draw diagonal ceiling triangle anchored at top-left.
void RegisterCornerRoutines(std::vector< DrawRoutineInfo > &registry)
Register all corner draw routines to the registry.
void DrawCorner4x4(const DrawContext &ctx)
Draw a 4x4 grid corner pattern.
void DrawWaterFace(const DrawContext &ctx)
Draw a generic 2x2 water-face helper pattern.
void DrawWeirdCornerBottom_BothBG(const DrawContext &ctx)
Draw a weird corner bottom pattern for both BG layers.
void DrawDiagonalCeilingBottomLeft(const DrawContext &ctx)
Draw diagonal ceiling triangle anchored at bottom-left.
void DrawDiagonalCeilingTopRight(const DrawContext &ctx)
Draw diagonal ceiling triangle anchored at top-right.
void Draw4x4Corner_BothBG(const DrawContext &ctx)
Draw a 4x4 corner for both BG layers.
void DrawDiagonalCeilingBottomRight(const DrawContext &ctx)
Draw diagonal ceiling triangle anchored at bottom-right.
Context passed to draw routines containing all necessary state.
std::span< const gfx::TileInfo > tiles
gfx::BackgroundBuffer & target_bg
gfx::BackgroundBuffer * secondary_bg
Metadata about a draw routine.