yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
rightwards_routines.cc
Go to the documentation of this file.
2
5
6namespace yaze {
7namespace zelda3 {
8namespace draw_routines {
9
11 // Pattern: Draws 2x2 tiles rightward (object 0x00)
12 // Size byte determines how many times to repeat (1-15 or 32)
13 // ROM tile order is COLUMN-MAJOR: [col0_row0, col0_row1, col1_row0, col1_row1]
14 int size = ctx.object.size_;
15 if (size == 0)
16 size = 32; // Special case for object 0x00
17
18 for (int s = 0; s < size; s++) {
19 if (ctx.tiles.size() >= 4) {
20 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
21 // tiles[0] → $BF → (col 0, row 0) = top-left
22 // tiles[1] → $CB → (col 0, row 1) = bottom-left
23 // tiles[2] → $C2 → (col 1, row 0) = top-right
24 // tiles[3] → $CE → (col 1, row 1) = bottom-right
26 ctx.object.y_,
27 ctx.tiles[0]); // col 0, row 0
29 ctx.object.y_ + 1,
30 ctx.tiles[1]); // col 0, row 1
31 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
32 ctx.object.y_,
33 ctx.tiles[2]); // col 1, row 0
34 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
35 ctx.object.y_ + 1,
36 ctx.tiles[3]); // col 1, row 1
37 }
38 }
39}
40
42 // Pattern: Draws 2x4 tiles rightward (objects 0x01-0x02)
43 // Uses RoomDraw_Nx4 with N=2, tiles are COLUMN-MAJOR:
44 // [col0_row0, col0_row1, col0_row2, col0_row3, col1_row0, col1_row1, col1_row2, col1_row3]
45 int size = ctx.object.size_;
46 if (size == 0)
47 size = 26; // Special case
48
49 for (int s = 0; s < size; s++) {
50 if (ctx.tiles.size() >= 8) {
51 // Draw 2x4 pattern in COLUMN-MAJOR order (matching RoomDraw_Nx4)
52 // Column 0 (tiles 0-3)
54 ctx.object.y_,
55 ctx.tiles[0]); // col 0, row 0
57 ctx.object.y_ + 1,
58 ctx.tiles[1]); // col 0, row 1
60 ctx.object.y_ + 2,
61 ctx.tiles[2]); // col 0, row 2
63 ctx.object.y_ + 3,
64 ctx.tiles[3]); // col 0, row 3
65 // Column 1 (tiles 4-7)
66 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
67 ctx.object.y_,
68 ctx.tiles[4]); // col 1, row 0
69 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
70 ctx.object.y_ + 1,
71 ctx.tiles[5]); // col 1, row 1
72 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
73 ctx.object.y_ + 2,
74 ctx.tiles[6]); // col 1, row 2
75 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
76 ctx.object.y_ + 3,
77 ctx.tiles[7]); // col 1, row 3
78 } else if (ctx.tiles.size() >= 4) {
79 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
81 ctx.object.y_, ctx.tiles[0]);
83 ctx.object.y_ + 1, ctx.tiles[1]);
85 ctx.object.y_ + 2, ctx.tiles[2]);
87 ctx.object.y_ + 3, ctx.tiles[3]);
88 }
89 }
90}
91
93 // Pattern: Draws 2x4 tiles rightward with adjacent spacing (objects 0x03-0x04)
94 // Uses RoomDraw_Nx4 with N=2, tiles are COLUMN-MAJOR
95 // ASM: GetSize_1to16 means count = size + 1
96 int size = ctx.object.size_ & 0x0F;
97 int count = size + 1;
98
99 for (int s = 0; s < count; s++) {
100 if (ctx.tiles.size() >= 8) {
101 // Draw 2x4 pattern in COLUMN-MAJOR order with adjacent spacing (s * 2)
102 // Column 0 (tiles 0-3)
104 ctx.object.y_,
105 ctx.tiles[0]); // col 0, row 0
107 ctx.object.y_ + 1,
108 ctx.tiles[1]); // col 0, row 1
110 ctx.object.y_ + 2,
111 ctx.tiles[2]); // col 0, row 2
113 ctx.object.y_ + 3,
114 ctx.tiles[3]); // col 0, row 3
115 // Column 1 (tiles 4-7)
116 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
117 ctx.object.y_,
118 ctx.tiles[4]); // col 1, row 0
119 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
120 ctx.object.y_ + 1,
121 ctx.tiles[5]); // col 1, row 1
122 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
123 ctx.object.y_ + 2,
124 ctx.tiles[6]); // col 1, row 2
125 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
126 ctx.object.y_ + 3,
127 ctx.tiles[7]); // col 1, row 3
128 } else if (ctx.tiles.size() >= 4) {
129 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
131 ctx.object.y_, ctx.tiles[0]);
133 ctx.object.y_ + 1, ctx.tiles[1]);
135 ctx.object.y_ + 2, ctx.tiles[2]);
137 ctx.object.y_ + 3, ctx.tiles[3]);
138 }
139 }
140}
141
143 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16_BothBG ($01:8C37)
144 //
145 // Despite the "_BothBG" suffix in usdasm, this routine does NOT explicitly
146 // write to both tilemaps; it uses the current tilemap pointers and is thus
147 // single-layer.
148 //
149 // Behavior: draw a 2x4 block, then advance by an additional 4 tiles before
150 // drawing the next block (net step = 2 tile width + 4 tile gap = 6 tiles).
151 int size = ctx.object.size_ & 0x0F;
152 int count = size + 1;
153
154 constexpr int kStepTiles = 6;
155
156 for (int s = 0; s < count; s++) {
157 const int base_x = ctx.object.x_ + (s * kStepTiles);
158
159 if (ctx.tiles.size() >= 8) {
160 // Column 0 (tiles 0-3)
161 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
162 ctx.tiles[0]); // col 0, row 0
163 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
164 ctx.tiles[1]); // col 0, row 1
165 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 2,
166 ctx.tiles[2]); // col 0, row 2
167 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 3,
168 ctx.tiles[3]); // col 0, row 3
169
170 // Column 1 (tiles 4-7)
171 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
172 ctx.tiles[4]); // col 1, row 0
173 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
174 ctx.tiles[5]); // col 1, row 1
175 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 2,
176 ctx.tiles[6]); // col 1, row 2
177 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 3,
178 ctx.tiles[7]); // col 1, row 3
179 } else if (ctx.tiles.size() >= 4) {
180 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
181 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
182 ctx.tiles[0]);
183 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
184 ctx.tiles[1]);
185 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 2,
186 ctx.tiles[2]);
187 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 3,
188 ctx.tiles[3]);
189 }
190 }
191}
192
194 // Pattern: Draws 2x2 tiles rightward (objects 0x07-0x08)
195 // ROM tile order is COLUMN-MAJOR: [col0_row0, col0_row1, col1_row0, col1_row1]
196 int size = ctx.object.size_ & 0x0F;
197
198 // Assembly: JSR RoomDraw_GetSize_1to16
199 // GetSize_1to16: count = size + 1
200 int count = size + 1;
201
202 for (int s = 0; s < count; s++) {
203 if (ctx.tiles.size() >= 4) {
204 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
206 ctx.object.y_,
207 ctx.tiles[0]); // col 0, row 0
209 ctx.object.y_ + 1,
210 ctx.tiles[1]); // col 0, row 1
211 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
212 ctx.object.y_,
213 ctx.tiles[2]); // col 1, row 0
214 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
215 ctx.object.y_ + 1,
216 ctx.tiles[3]); // col 1, row 1
217 }
218 }
219}
220
222 // Pattern: 1x3 tiles rightward with caps (object 0x21)
223 int size = ctx.object.size_ & 0x0F;
224
225 if (ctx.tiles.size() >= 9) {
226 auto draw_column = [&](int x, int base) {
228 ctx.tiles[base + 0]);
230 ctx.tiles[base + 1]);
232 ctx.tiles[base + 2]);
233 };
234
235 draw_column(ctx.object.x_, 0);
236
237 int mid_cols = (size + 1) * 2;
238 for (int s = 0; s < mid_cols; s++) {
239 draw_column(ctx.object.x_ + 1 + s, 3);
240 }
241
242 draw_column(ctx.object.x_ + 1 + mid_cols, 6);
243 return;
244 }
245
246 int count = (size * 2) + 1;
247 for (int s = 0; s < count; s++) {
248 if (ctx.tiles.size() >= 2) {
250 ctx.object.y_, ctx.tiles[0]);
252 ctx.object.y_ + 1, ctx.tiles[1]);
253 }
254 }
255}
256
258 // Pattern: Rail with corner/middle/end (object 0x22)
259 int size = ctx.object.size_ & 0x0F;
260
261 int count = size + 2;
262 if (ctx.tiles.size() < 3)
263 return;
264
265 int x = ctx.object.x_;
266 // USDASM $01:8EF6-$01:8F01 suppresses the corner when the slot already
267 // contains the small-rail corner tile.
269 {0x00E2})) {
271 }
272 x++;
273 for (int s = 0; s < count; s++) {
275 x++;
276 }
278}
279
281 // Pattern: Rail with corner/middle/end (objects 0x23-0x2E, 0x3F-0x46)
282 int size = ctx.object.size_ & 0x0F;
283
284 int count = size + 1;
285 if (ctx.tiles.size() < 3)
286 return;
287
288 int x = ctx.object.x_;
289 // USDASM $01:8F65-$01:8F7F skips the leading cap when the existing tile is
290 // already the same corner, or one of the compatible trim corner tiles.
292 ctx.target_bg, x, ctx.object.y_, {0x01DB, 0x01A6, 0x01DD, 0x01FC})) {
294 }
295 x++;
296 for (int s = 0; s < count; s++) {
298 x++;
299 }
301}
302
304 // Pattern: Long rail with corner/middle/end (object 0x5F)
305 int size = ctx.object.size_ & 0x0F;
306 int count = size + 21;
307 if (ctx.tiles.size() < 3)
308 return;
309
310 int x = ctx.object.x_;
311 // USDASM $01:8EF6-$01:8F01 uses the same small-rail-corner suppression path
312 // for the long horizontal rail.
314 {0x00E2})) {
316 }
317 x++;
318 for (int s = 0; s < count; s++) {
320 x++;
321 }
323}
324
326 // Pattern: Top corner 1x2 tiles with +13 offset (object 0x2F)
327 const int size = ctx.object.size_ & 0x0F;
328 // USDASM $01:8FBD - Object_Size_N_to_N_plus_15 with N=0x0A.
329 const int count = size + 10;
330 if (count <= 0 || ctx.tiles.empty()) {
331 return;
332 }
333
334 // Middle columns are (top=tile3, bottom=tile0). Ends use cap tiles.
335 const gfx::TileInfo& bottom_fill = ctx.tiles[0];
336 const gfx::TileInfo& top_fill =
337 ctx.tiles.size() > 3 ? ctx.tiles[3] : ctx.tiles[0];
338 const gfx::TileInfo& start_cap_top =
339 ctx.tiles.size() > 1 ? ctx.tiles[1] : top_fill;
340 const gfx::TileInfo& end_cap_top =
341 ctx.tiles.size() > 4 ? ctx.tiles[4] : top_fill;
342
343 for (int s = 0; s < count; ++s) {
344 const gfx::TileInfo& top_tile = (s == 0) ? start_cap_top
345 : (s == count - 1) ? end_cap_top
346 : top_fill;
347
349 ctx.object.y_, top_tile);
351 ctx.object.y_ + 1, bottom_fill);
352 }
353}
354
356 // Pattern: Bottom corner 1x2 tiles with +13 offset (object 0x30)
357 const int size = ctx.object.size_ & 0x0F;
358 // USDASM $01:9001 - mirrored variant of the top-corner routine.
359 const int count = size + 10;
360 if (count <= 0 || ctx.tiles.empty()) {
361 return;
362 }
363
364 const gfx::TileInfo& top_fill = ctx.tiles[0];
365 const gfx::TileInfo& bottom_fill =
366 ctx.tiles.size() > 3 ? ctx.tiles[3] : ctx.tiles[0];
367 const gfx::TileInfo& start_cap_bottom =
368 ctx.tiles.size() > 1 ? ctx.tiles[1] : bottom_fill;
369 const gfx::TileInfo& end_cap_bottom =
370 ctx.tiles.size() > 4 ? ctx.tiles[4] : bottom_fill;
371
372 for (int s = 0; s < count; ++s) {
373 const gfx::TileInfo& bottom_tile = (s == 0) ? start_cap_bottom
374 : (s == count - 1) ? end_cap_bottom
375 : bottom_fill;
376
378 ctx.object.y_ + 1, top_fill);
380 ctx.object.y_ + 2, bottom_tile);
381 }
382}
383
385 // Pattern: 4x4 block rightward (object 0x33)
386 int size = ctx.object.size_ & 0x0F;
387
388 // Assembly: GetSize_1to16, so count = size + 1
389 int count = size + 1;
390
391 for (int s = 0; s < count; s++) {
392 if (ctx.tiles.size() >= 16) {
393 // Draw 4x4 pattern in COLUMN-MAJOR order (matching assembly)
394 // Iterate columns (x) first, then rows (y) within each column
395 for (int x = 0; x < 4; ++x) {
396 for (int y = 0; y < 4; ++y) {
398 ctx.object.x_ + (s * 4) + x,
399 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
400 }
401 }
402 }
403 }
404}
405
407 // Pattern: 1x1 solid tiles +3 offset (object 0x34)
408 int size = ctx.object.size_ & 0x0F;
409
410 // Assembly: GetSize_1to16_timesA(4), so count = size + 4
411 int count = size + 4;
412
413 for (int s = 0; s < count; s++) {
414 if (ctx.tiles.size() >= 1) {
415 // Use first 8x8 tile from span
417 ctx.object.y_, ctx.tiles[0]);
418 }
419 }
420}
421
423 // Pattern: 4x4 decoration with spacing (objects 0x36-0x37)
424 int size = ctx.object.size_ & 0x0F;
425
426 // Assembly: GetSize_1to16, so count = size + 1
427 int count = size + 1;
428
429 for (int s = 0; s < count; s++) {
430 if (ctx.tiles.size() >= 16) {
431 // Draw 4x4 pattern with spacing in COLUMN-MAJOR order (matching assembly)
432 for (int x = 0; x < 4; ++x) {
433 for (int y = 0; y < 4; ++y) {
435 ctx.object.x_ + (s * 6) + x,
436 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
437 }
438 }
439 }
440 }
441}
442
444 // Pattern: 2x3 statue with spacing (object 0x38)
445 // 2 columns × 3 rows = 6 tiles in COLUMN-MAJOR order
446 int size = ctx.object.size_ & 0x0F;
447
448 // Assembly: GetSize_1to16, so count = size + 1
449 int count = size + 1;
450
451 for (int s = 0; s < count; s++) {
452 if (ctx.tiles.size() >= 6) {
453 // Draw 2x3 pattern in COLUMN-MAJOR order (matching assembly)
454 for (int x = 0; x < 2; ++x) {
455 for (int y = 0; y < 3; ++y) {
457 ctx.object.x_ + (s * 4) + x,
458 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
459 }
460 }
461 }
462 }
463}
464
466 // Pattern: 2x4 pillar with spacing (objects 0x39, 0x3D)
467 // 2 columns × 4 rows = 8 tiles in COLUMN-MAJOR order
468 int size = ctx.object.size_ & 0x0F;
469
470 // Assembly: GetSize_1to16, so count = size + 1
471 int count = size + 1;
472
473 for (int s = 0; s < count; s++) {
474 if (ctx.tiles.size() >= 8) {
475 // Draw 2x4 pattern in COLUMN-MAJOR order (matching assembly)
476 for (int x = 0; x < 2; ++x) {
477 for (int y = 0; y < 4; ++y) {
479 ctx.object.x_ + (s * 6) + x,
480 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
481 }
482 }
483 }
484 }
485}
486
488 // Pattern: 4x3 decoration with spacing (objects 0x3A-0x3B)
489 // 4 columns × 3 rows = 12 tiles in COLUMN-MAJOR order
490 int size = ctx.object.size_ & 0x0F;
491
492 // Assembly: GetSize_1to16, so count = size + 1
493 int count = size + 1;
494
495 for (int s = 0; s < count; s++) {
496 if (ctx.tiles.size() >= 12) {
497 // Draw 4x3 pattern in COLUMN-MAJOR order (matching assembly)
498 for (int x = 0; x < 4; ++x) {
499 for (int y = 0; y < 3; ++y) {
501 ctx.object.x_ + (s * 6) + x,
502 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
503 }
504 }
505 }
506 }
507}
508
510 // Pattern: Doubled 2x2 with spacing (object 0x3C)
511 // 4 columns × 2 rows = 8 tiles in COLUMN-MAJOR order
512 int size = ctx.object.size_ & 0x0F;
513
514 // Assembly: GetSize_1to16, so count = size + 1
515 int count = size + 1;
516
517 for (int s = 0; s < count; s++) {
518 if (ctx.tiles.size() >= 8) {
519 // Draw doubled 2x2 pattern in COLUMN-MAJOR order (matching assembly)
520 for (int x = 0; x < 4; ++x) {
521 for (int y = 0; y < 2; ++y) {
523 ctx.object.x_ + (s * 6) + x,
524 ctx.object.y_ + y, ctx.tiles[x * 2 + y]);
525 }
526 }
527 }
528 }
529}
530
532 // Pattern: 2x2 decoration with large spacing (object 0x3E)
533 int size = ctx.object.size_ & 0x0F;
534
535 // Assembly: GetSize_1to16, so count = size + 1
536 int count = size + 1;
537
538 for (int s = 0; s < count; s++) {
539 if (ctx.tiles.size() >= 4) {
540 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
541 // tiles[0] → col 0, row 0 = top-left
542 // tiles[1] → col 0, row 1 = bottom-left
543 // tiles[2] → col 1, row 0 = top-right
544 // tiles[3] → col 1, row 1 = bottom-right
546 ctx.object.y_,
547 ctx.tiles[0]); // col 0, row 0
549 ctx.object.y_ + 1,
550 ctx.tiles[1]); // col 0, row 1
551 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
552 ctx.object.y_,
553 ctx.tiles[2]); // col 1, row 0
554 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
555 ctx.object.y_ + 1,
556 ctx.tiles[3]); // col 1, row 1
557 }
558 }
559}
560
562 // Pattern: Draws 4x2 tiles rightward (objects 0x49-0x4A: Floor Tile 4x2)
563 // Uses ROW-MAJOR tile order:
564 // row 0: tiles[0..3], row 1: tiles[4..7]
565 const int size = ctx.object.size_ & 0x0F;
566 const int count = size + 1; // GetSize_1to16
567
568 if (ctx.tiles.size() < 8) {
569 return;
570 }
571
572 for (int s = 0; s < count; ++s) {
573 const int base_x = ctx.object.x_ + (s * 4);
574
575 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
576 ctx.tiles[0]);
577 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
578 ctx.tiles[1]);
579 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_,
580 ctx.tiles[2]);
581 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_,
582 ctx.tiles[3]);
583
584 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
585 ctx.tiles[4]);
586 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
587 ctx.tiles[5]);
588 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_ + 1,
589 ctx.tiles[6]);
590 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_ + 1,
591 ctx.tiles[7]);
592 }
593}
594
596 // Pattern: Draws 1x8 column tiles with 12-tile horizontal spacing
597 // (objects 0x55-0x56 wall torches).
598 const int size = ctx.object.size_ & 0x0F;
599 const int count = size + 1; // GetSize_1to16
600
601 if (ctx.tiles.size() < 8) {
602 return;
603 }
604
605 for (int s = 0; s < count; ++s) {
606 const int base_x = ctx.object.x_ + (s * 12);
607 for (int row = 0; row < 8; ++row) {
608 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x, ctx.object.y_ + row,
609 ctx.tiles[row]);
610 }
611 }
612}
613
615 // USDASM behavior:
616 // - Repeat left 2-column 3-row segment count times
617 // - Then append right 2-column edge once
618 // Tile layout is COLUMN-MAJOR:
619 // col0: [0..2], col1: [3..5], col2: [6..8], col3: [9..11]
620 const int size = ctx.object.size_ & 0x0F;
621 const int count = size + 1; // GetSize_1to16
622
623 if (ctx.tiles.size() < 12) {
624 return;
625 }
626
627 auto draw_column = [&](int x, int y, const gfx::TileInfo& t0,
628 const gfx::TileInfo& t1, const gfx::TileInfo& t2) {
630 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 1, t1);
631 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 2, t2);
632 };
633
634 for (int s = 0; s < count; ++s) {
635 const int base_x = ctx.object.x_ + (s * 2);
636 draw_column(base_x + 0, ctx.object.y_, ctx.tiles[0], ctx.tiles[1],
637 ctx.tiles[2]);
638 draw_column(base_x + 1, ctx.object.y_, ctx.tiles[3], ctx.tiles[4],
639 ctx.tiles[5]);
640 }
641
642 const int right_base_x = ctx.object.x_ + (count * 2);
643 draw_column(right_base_x + 0, ctx.object.y_, ctx.tiles[6], ctx.tiles[7],
644 ctx.tiles[8]);
645 draw_column(right_base_x + 1, ctx.object.y_, ctx.tiles[9], ctx.tiles[10],
646 ctx.tiles[11]);
647}
648
650 const int size = ctx.object.size_ & 0x0F;
651 const int count = size + 2;
652 if (ctx.tiles.empty()) {
653 return;
654 }
655
656 for (int s = 0; s < count; ++s) {
658 ctx.object.y_, ctx.tiles[0]);
659 }
660}
661
663 const int size = ctx.object.size_ & 0x0F;
664 const int count = size + 1;
665 if (ctx.tiles.size() < 12) {
666 return;
667 }
668
669 for (int s = 0; s < count; ++s) {
670 const int base_x = ctx.object.x_ + (s * 4);
671 for (int x = 0; x < 4; ++x) {
672 for (int y = 0; y < 3; ++y) {
674 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
675 }
676 }
677 }
678}
679
681 const int size = ctx.object.size_ & 0x0F;
682 const int count = size + 1;
683 if (ctx.tiles.size() < 16) {
684 return;
685 }
686
687 for (int s = 0; s < count; ++s) {
688 const int base_x = ctx.object.x_ + (s * 4);
689 for (int x = 0; x < 4; ++x) {
690 for (int y = 0; y < 4; ++y) {
692 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
693 }
694 }
695 }
696}
697
699 const int size = ctx.object.size_ & 0x0F;
700 const int middle_columns = size + 2;
701 const int total_columns = size + 6;
702
703 if (ctx.tiles.size() >= 15) {
704 auto draw_column = [&](int x, int base_idx) {
706 ctx.tiles[base_idx + 0]);
708 ctx.tiles[base_idx + 1]);
710 ctx.tiles[base_idx + 2]);
711 };
712
713 // USDASM $01:8F36:
714 // - 2-column start cap (tiles 0..5)
715 // - repeated middle columns (tiles 6..8), repeated size+2 times
716 // - 2-column end cap (tiles 9..14)
717 draw_column(ctx.object.x_ + 0, 0);
718 draw_column(ctx.object.x_ + 1, 3);
719 for (int s = 0; s < middle_columns; ++s) {
720 draw_column(ctx.object.x_ + 2 + s, 6);
721 }
722 draw_column(ctx.object.x_ + 2 + middle_columns, 9);
723 draw_column(ctx.object.x_ + 3 + middle_columns, 12);
724 return;
725 }
726
727 if (ctx.tiles.empty()) {
728 return;
729 }
730 for (int x = 0; x < total_columns; ++x) {
731 for (int y = 0; y < 3; ++y) {
733 ctx.object.y_ + y, ctx.tiles[0]);
734 }
735 }
736}
737
739 const int size = ctx.object.size_ & 0x0F;
740 const int count = size + 1;
741 if (ctx.tiles.size() < 4) {
742 return;
743 }
744
745 for (int s = 0; s < count; ++s) {
746 const int base_x = ctx.object.x_ + (s * 4);
747 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
748 ctx.tiles[0]);
749 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
750 ctx.tiles[1]);
751 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
752 ctx.tiles[2]);
753 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
754 ctx.tiles[3]);
755 }
756}
757
759 const int size = ctx.object.size_ & 0x0F;
760 const int count = size + 8;
761 if (ctx.tiles.empty()) {
762 return;
763 }
764
765 for (int s = 0; s < count; ++s) {
767 ctx.object.y_, ctx.tiles[0]);
768 }
769}
770
772 const int size = ctx.object.size_ & 0x0F;
773 const int count = size + 1;
774 if (ctx.tiles.size() < 4) {
775 return;
776 }
777
778 for (int s = 0; s < count; ++s) {
779 const int base_x = ctx.object.x_ + (s * 2);
780 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
781 ctx.tiles[0]);
782 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
783 ctx.tiles[1]);
784 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
785 ctx.tiles[2]);
786 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
787 ctx.tiles[3]);
788 }
789}
790
794
795void RegisterRightwardsRoutines(std::vector<DrawRoutineInfo>& registry) {
796 // Note: Routine IDs are assigned based on the assembly routine table
797 // These rightwards routines are part of the core 40 draw routines
798 // Uses canonical IDs from DrawRoutineIds namespace
799
800 registry.push_back(DrawRoutineInfo{
802 .name = "Rightwards2x2_1to15or32",
803 .function = DrawRightwards2x2_1to15or32,
804 // USDASM: RoomDraw_Rightwards2x2_1to15or32 ($01:8B89) calls
805 // RoomDraw_Rightwards2x2 ($01:9895), which writes through the current
806 // tilemap pointer set (single-layer).
807 .draws_to_both_bgs = false,
808 .base_width = 2,
809 .base_height = 2,
810 .min_tiles = 4, // 2x2 block
812 });
813
814 registry.push_back(DrawRoutineInfo{
816 .name = "Rightwards2x4_1to15or26",
817 .function = DrawRightwards2x4_1to15or26,
818 // USDASM: RoomDraw_Rightwards2x4_1to15or26 ($01:8A92) uses RoomDraw_Nx4
819 // ($01:97F0) via pointers; single-layer.
820 .draws_to_both_bgs = false,
821 .base_width = 2,
822 .base_height = 4,
823 .min_tiles = 8, // 2x4 block
825 });
826
827 registry.push_back(DrawRoutineInfo{
829 .name = "Rightwards2x4_1to16",
830 .function = DrawRightwards2x4_1to16,
831 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16 ($01:8B0D) explicitly
832 // writes to both tilemaps ($7E2000 and $7E4000).
833 .draws_to_both_bgs = true,
834 .base_width = 2, // Adjacent spacing (s * 2)
835 .base_height = 4,
836 .min_tiles = 8, // 2x4 block
838 });
839
840 registry.push_back(DrawRoutineInfo{
842 .name = "Rightwards2x4_1to16_BothBG",
844 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16_BothBG ($01:8C37) uses
845 // RoomDraw_Nx4 through the current tilemap pointers (single-layer).
846 .draws_to_both_bgs = false,
847 .base_width = 2, // 2x4 blocks; repeat step is 6 tiles (2 wide + 4 gap)
848 .base_height = 4,
849 .min_tiles = 8, // 2x4 block
851 });
852
853 registry.push_back(DrawRoutineInfo{
855 .name = "Rightwards2x2_1to16",
856 .function = DrawRightwards2x2_1to16,
857 .draws_to_both_bgs = false,
858 .base_width = 2,
859 .base_height = 2,
860 .min_tiles = 4, // 2x2 block
862 });
863
864 registry.push_back(DrawRoutineInfo{
866 .name = "Rightwards1x2_1to16_plus2",
868 .draws_to_both_bgs = false,
869 .base_width = 4,
870 .base_height = 3,
871 .min_tiles = 2, // cap + middle tiles
873 });
874
875 registry.push_back(DrawRoutineInfo{
877 .name = "RightwardsHasEdge1x1_1to16_plus3",
879 .draws_to_both_bgs = false,
880 .base_width = 4,
881 .base_height = 1,
882 .min_tiles = 3, // left edge + middle + right edge
884 });
885
886 registry.push_back(DrawRoutineInfo{
888 .name = "RightwardsHasEdge1x1_1to16_plus2",
890 .draws_to_both_bgs = false,
891 .base_width = 3,
892 .base_height = 1,
893 .min_tiles = 3, // left edge + middle + right edge
895 });
896
897 registry.push_back(DrawRoutineInfo{
899 .name = "RightwardsHasEdge1x1_1to16_plus23",
901 .draws_to_both_bgs = false,
902 .base_width = 23,
903 .base_height = 1,
904 .min_tiles = 3, // left edge + middle + right edge
906 });
907
908 registry.push_back(DrawRoutineInfo{
910 .name = "RightwardsTopCorners1x2_1to16_plus13",
912 .draws_to_both_bgs = false,
913 .base_width = 1,
914 .base_height = 2,
915 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
917 });
918
919 registry.push_back(DrawRoutineInfo{
921 .name = "RightwardsBottomCorners1x2_1to16_plus13",
923 .draws_to_both_bgs = false,
924 .base_width = 1,
925 .base_height = 2, // spans y+1 to y+2
926 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
928 });
929
930 registry.push_back(DrawRoutineInfo{
932 .name = "Rightwards4x4_1to16",
933 .function = DrawRightwards4x4_1to16,
934 .draws_to_both_bgs = false,
935 .base_width = 4,
936 .base_height = 4,
937 .min_tiles = 16, // 4x4 block
939 });
940
941 registry.push_back(DrawRoutineInfo{
943 .name = "Rightwards1x1Solid_1to16_plus3",
945 .draws_to_both_bgs = false,
946 .base_width = 1,
947 .base_height = 1,
948 .min_tiles =
949 1, // object 0x34 repeats a single tile from the subtype-1 table
951 });
952
953 registry.push_back(DrawRoutineInfo{
955 .name = "RightwardsDecor4x4spaced2_1to16",
957 .draws_to_both_bgs = false,
958 .base_width = 6, // 4 tiles + 2 spacing
959 .base_height = 4,
960 .min_tiles = 16, // 4x4 block
962 });
963
964 registry.push_back(DrawRoutineInfo{
966 .name = "RightwardsStatue2x3spaced2_1to16",
968 .draws_to_both_bgs = false,
969 .base_width = 4, // 2 tiles + 2 spacing
970 .base_height = 3,
971 .min_tiles = 6, // 2x3 block
973 });
974
975 registry.push_back(DrawRoutineInfo{
977 .name = "RightwardsPillar2x4spaced4_1to16",
979 .draws_to_both_bgs = false,
980 .base_width = 6, // 2 tiles + 4 spacing
981 .base_height = 4,
982 .min_tiles = 8, // 2x4 block
984 });
985
986 registry.push_back(DrawRoutineInfo{
988 .name = "RightwardsDecor4x3spaced4_1to16",
990 .draws_to_both_bgs = false,
991 .base_width =
992 6, // 4 tiles + 2 spacing (actually the calculation seems off, kept as is)
993 .base_height = 3,
994 .min_tiles = 12, // 4x3 block
996 });
997
998 registry.push_back(DrawRoutineInfo{
1000 .name = "RightwardsDoubled2x2spaced2_1to16",
1002 .draws_to_both_bgs = false,
1003 .base_width = 6, // 4 tiles + 2 spacing
1004 .base_height = 2,
1005 .min_tiles = 8, // 4x2 block
1007 });
1008
1009 registry.push_back(DrawRoutineInfo{
1011 .name = "RightwardsDecor2x2spaced12_1to16",
1013 .draws_to_both_bgs = false,
1014 .base_width = 14, // 2 tiles + 12 spacing
1015 .base_height = 2,
1016 .min_tiles = 4, // 2x2 block
1018 });
1019
1020 registry.push_back(DrawRoutineInfo{
1022 .name = "Rightwards4x2_1to16",
1023 .function = DrawRightwards4x2_1to16,
1024 .draws_to_both_bgs = false,
1025 .base_width = 4,
1026 .base_height = 2,
1027 .min_tiles = 8, // 4x2 block
1029 });
1030
1031 registry.push_back(DrawRoutineInfo{
1033 .name = "RightwardsDecor4x2spaced8_1to16",
1035 .draws_to_both_bgs = false,
1036 .base_width = 12, // 1 tile + 11-tile gap (12-tile step)
1037 .base_height = 8,
1038 .min_tiles = 8, // 1x8 column
1040 });
1041
1042 registry.push_back(DrawRoutineInfo{
1044 .name = "RightwardsCannonHole4x3_1to16",
1046 .draws_to_both_bgs = false,
1047 .base_width = 4,
1048 .base_height = 3,
1049 .min_tiles = 12, // 4x3 block
1051 });
1052
1053 registry.push_back(DrawRoutineInfo{
1055 .name = "RightwardsLine1x1_1to16plus1",
1057 .draws_to_both_bgs = false,
1058 .base_width = 1,
1059 .base_height = 1,
1060 .min_tiles = 1,
1062 });
1063
1064 registry.push_back(DrawRoutineInfo{
1066 .name = "RightwardsBar4x3_1to16",
1067 .function = DrawRightwardsBar4x3_1to16,
1068 .draws_to_both_bgs = false,
1069 .base_width = 4,
1070 .base_height = 3,
1071 .min_tiles = 12,
1073 });
1074
1075 registry.push_back(DrawRoutineInfo{
1077 .name = "RightwardsShelf4x4_1to16",
1078 .function = DrawRightwardsShelf4x4_1to16,
1079 .draws_to_both_bgs = false,
1080 .base_width = 4,
1081 .base_height = 4,
1082 .min_tiles = 16,
1084 });
1085
1086 registry.push_back(DrawRoutineInfo{
1088 .name = "RightwardsBigRail1x3_1to16plus5",
1090 .draws_to_both_bgs = false,
1091 .base_width = 6,
1092 .base_height = 3,
1093 .min_tiles = 15,
1095 });
1096
1097 registry.push_back(DrawRoutineInfo{
1099 .name = "RightwardsBlock2x2spaced2_1to16",
1101 .draws_to_both_bgs = false,
1102 .base_width = 4,
1103 .base_height = 2,
1104 .min_tiles = 4,
1106 });
1107
1108 registry.push_back(DrawRoutineInfo{
1110 .name = "RightwardsEdge1x1_1to16plus7",
1112 .draws_to_both_bgs = false,
1113 .base_width = 8,
1114 .base_height = 1,
1115 .min_tiles = 1,
1117 });
1118
1119 registry.push_back(DrawRoutineInfo{
1121 .name = "RightwardsPots2x2_1to16",
1122 .function = DrawRightwardsPots2x2_1to16,
1123 .draws_to_both_bgs = false,
1124 .base_width = 2,
1125 .base_height = 2,
1126 .min_tiles = 4,
1128 });
1129
1130 registry.push_back(DrawRoutineInfo{
1132 .name = "RightwardsHammerPegs2x2_1to16",
1134 .draws_to_both_bgs = false,
1135 .base_width = 2,
1136 .base_height = 2,
1137 .min_tiles = 4,
1139 });
1140}
1141
1142} // namespace draw_routines
1143} // namespace zelda3
1144} // namespace yaze
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
constexpr int kRightwardsBottomCorners1x2_1to16_plus13
constexpr int kRightwardsTopCorners1x2_1to16_plus13
bool ExistingTileMatchesAny(const gfx::BackgroundBuffer &bg, int tile_x, int tile_y, std::initializer_list< uint16_t > tile_ids)
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 DrawRightwardsStatue2x3spaced2_1to16(const DrawContext &ctx)
Draw 2x3 statue with spacing.
void DrawRightwardsBar4x3_1to16(const DrawContext &ctx)
Draw rightwards 4x3 bar pattern.
void DrawRightwards2x4_1to15or26(const DrawContext &ctx)
Draw 2x4 tiles rightward (1-15 or 26 repetitions)
void DrawRightwards2x2_1to15or32(const DrawContext &ctx)
Draw 2x2 tiles rightward (1-15 or 32 repetitions)
void DrawRightwardsHasEdge1x1_1to16_plus23(const DrawContext &ctx)
Draw 1x1 tiles with edge detection +23.
void DrawRightwardsBottomCorners1x2_1to16_plus13(const DrawContext &ctx)
Draw bottom corner 1x2 tiles with +13 offset.
void DrawRightwardsTopCorners1x2_1to16_plus13(const DrawContext &ctx)
Draw top corner 1x2 tiles with +13 offset.
void DrawRightwardsHasEdge1x1_1to16_plus2(const DrawContext &ctx)
Draw 1x1 tiles with edge detection +2.
void DrawRightwardsDecor2x2spaced12_1to16(const DrawContext &ctx)
Draw 2x2 decoration with large spacing.
void DrawRightwards1x1Solid_1to16_plus3(const DrawContext &ctx)
Draw 1x1 solid tiles +3 offset.
void DrawRightwards1x2_1to16_plus2(const DrawContext &ctx)
Draw 1x3 tiles rightward with caps.
void DrawRightwardsPots2x2_1to16(const DrawContext &ctx)
Draw rightwards 2x2 pot groups.
void DrawRightwards4x2_1to16(const DrawContext &ctx)
Draw 4x2 floor tiles rightward.
void DrawRightwardsBigRail1x3_1to16plus5(const DrawContext &ctx)
Draw rightwards big rail pattern (1x3 columns with +5 extension)
void DrawRightwardsBlock2x2spaced2_1to16(const DrawContext &ctx)
Draw rightwards 2x2 blocks with 2-tile spacing.
void DrawRightwards4x4_1to16(const DrawContext &ctx)
Draw 4x4 block rightward.
void DrawRightwardsHasEdge1x1_1to16_plus3(const DrawContext &ctx)
Draw 1x1 tiles with edge detection +3.
void DrawRightwards2x2_1to16(const DrawContext &ctx)
Draw 2x2 tiles rightward (1-16 repetitions)
void DrawRightwardsHammerPegs2x2_1to16(const DrawContext &ctx)
Draw rightwards 2x2 hammer-peg groups.
void DrawRightwardsCannonHole4x3_1to16(const DrawContext &ctx)
Draw cannon-hole spans with repeated left segment and right cap.
void DrawRightwardsDoubled2x2spaced2_1to16(const DrawContext &ctx)
Draw doubled 2x2 with spacing.
void DrawRightwards2x4_1to16(const DrawContext &ctx)
Draw 2x4 tiles rightward with adjacent spacing (1-16 repetitions)
void RegisterRightwardsRoutines(std::vector< DrawRoutineInfo > &registry)
Register all rightwards draw routines to the registry.
void DrawRightwardsDecor4x3spaced4_1to16(const DrawContext &ctx)
Draw 4x3 decoration with spacing.
void DrawRightwardsShelf4x4_1to16(const DrawContext &ctx)
Draw rightwards 4x4 shelf pattern.
void DrawRightwards2x4_1to16_BothBG(const DrawContext &ctx)
Draw 2x4 tiles rightward with adjacent spacing to both BG layers.
void DrawRightwardsDecor4x2spaced8_1to16(const DrawContext &ctx)
Draw 1x8 decorative columns with wide horizontal spacing.
void DrawRightwardsPillar2x4spaced4_1to16(const DrawContext &ctx)
Draw 2x4 pillar with spacing.
void DrawRightwardsEdge1x1_1to16plus7(const DrawContext &ctx)
Draw rightwards 1x1 edge line with +7 extension.
void DrawRightwardsDecor4x4spaced2_1to16(const DrawContext &ctx)
Draw 4x4 decoration with spacing.
void DrawRightwardsLine1x1_1to16plus1(const DrawContext &ctx)
Draw rightwards 1x1 line with +1 extension.
Context passed to draw routines containing all necessary state.
std::span< const gfx::TileInfo > tiles
gfx::BackgroundBuffer & target_bg
Metadata about a draw routine.