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
10namespace {
11
12void DrawRightwardsCorners1x2(const DrawContext& ctx, bool edge_is_top) {
13 if (ctx.tiles.size() < 6) {
14 return;
15 }
16
17 // USDASM caches tile 0 as the solid row. Tiles 1-2 form the opening cap,
18 // tile 3 repeats for size+10 columns, and tiles 4-5 form the closing cap.
19 // The routine name's "+13" is the minimum extent, not an X offset.
20 const int body_count = (ctx.object.size_ & 0x0F) + 10;
21 const int edge_y = ctx.object.y_ + (edge_is_top ? 0 : 1);
22 const int fill_y = ctx.object.y_ + (edge_is_top ? 1 : 0);
23 int x = ctx.object.x_;
24
25 if (!DrawRoutineUtils::ExistingTileMatchesAny(ctx, x, edge_y, {0x00E2})) {
26 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, edge_y, ctx.tiles[1]);
27 DrawRoutineUtils::WriteTile8(ctx.target_bg, x + 1, edge_y, ctx.tiles[2]);
28 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, fill_y, ctx.tiles[0]);
29 DrawRoutineUtils::WriteTile8(ctx.target_bg, x + 1, fill_y, ctx.tiles[0]);
30 x += 2;
31 }
32
33 for (int i = 0; i < body_count; ++i, ++x) {
34 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, edge_y, ctx.tiles[3]);
35 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, fill_y, ctx.tiles[0]);
36 }
37
38 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, edge_y, ctx.tiles[4]);
39 DrawRoutineUtils::WriteTile8(ctx.target_bg, x + 1, edge_y, ctx.tiles[5]);
40 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, fill_y, ctx.tiles[0]);
41 DrawRoutineUtils::WriteTile8(ctx.target_bg, x + 1, fill_y, ctx.tiles[0]);
42}
43
44} // namespace
45
47 // Pattern: Draws 2x2 tiles rightward (object 0x00)
48 // Size byte determines how many times to repeat (1-15 or 32)
49 // ROM tile order is COLUMN-MAJOR: [col0_row0, col0_row1, col1_row0, col1_row1]
50 int size = ctx.object.size_;
51 if (size == 0)
52 size = 32; // Special case for object 0x00
53
54 for (int s = 0; s < size; s++) {
55 if (ctx.tiles.size() >= 4) {
56 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
57 // tiles[0] → $BF → (col 0, row 0) = top-left
58 // tiles[1] → $CB → (col 0, row 1) = bottom-left
59 // tiles[2] → $C2 → (col 1, row 0) = top-right
60 // tiles[3] → $CE → (col 1, row 1) = bottom-right
62 ctx.object.y_,
63 ctx.tiles[0]); // col 0, row 0
65 ctx.object.y_ + 1,
66 ctx.tiles[1]); // col 0, row 1
67 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
68 ctx.object.y_,
69 ctx.tiles[2]); // col 1, row 0
70 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
71 ctx.object.y_ + 1,
72 ctx.tiles[3]); // col 1, row 1
73 }
74 }
75}
76
78 // Pattern: Draws 2x4 tiles rightward (objects 0x01-0x02)
79 // Uses RoomDraw_Nx4 with N=2, tiles are COLUMN-MAJOR:
80 // [col0_row0, col0_row1, col0_row2, col0_row3, col1_row0, col1_row1, col1_row2, col1_row3]
81 int size = ctx.object.size_;
82 if (size == 0)
83 size = 26; // Special case
84
85 for (int s = 0; s < size; s++) {
86 if (ctx.tiles.size() >= 8) {
87 // Draw 2x4 pattern in COLUMN-MAJOR order (matching RoomDraw_Nx4)
88 // Column 0 (tiles 0-3)
90 ctx.object.y_,
91 ctx.tiles[0]); // col 0, row 0
93 ctx.object.y_ + 1,
94 ctx.tiles[1]); // col 0, row 1
96 ctx.object.y_ + 2,
97 ctx.tiles[2]); // col 0, row 2
99 ctx.object.y_ + 3,
100 ctx.tiles[3]); // col 0, row 3
101 // Column 1 (tiles 4-7)
102 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
103 ctx.object.y_,
104 ctx.tiles[4]); // col 1, row 0
105 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
106 ctx.object.y_ + 1,
107 ctx.tiles[5]); // col 1, row 1
108 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
109 ctx.object.y_ + 2,
110 ctx.tiles[6]); // col 1, row 2
111 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
112 ctx.object.y_ + 3,
113 ctx.tiles[7]); // col 1, row 3
114 } else if (ctx.tiles.size() >= 4) {
115 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
117 ctx.object.y_, ctx.tiles[0]);
119 ctx.object.y_ + 1, ctx.tiles[1]);
121 ctx.object.y_ + 2, ctx.tiles[2]);
123 ctx.object.y_ + 3, ctx.tiles[3]);
124 }
125 }
126}
127
129 // Pattern: Draws 2x4 tiles rightward with adjacent spacing (objects 0x03-0x04)
130 // Uses RoomDraw_Nx4 with N=2, tiles are COLUMN-MAJOR
131 // ASM: GetSize_1to16 means count = size + 1
132 int size = ctx.object.size_ & 0x0F;
133 int count = size + 1;
134
135 for (int s = 0; s < count; s++) {
136 if (ctx.tiles.size() >= 8) {
137 // Draw 2x4 pattern in COLUMN-MAJOR order with adjacent spacing (s * 2)
138 // Column 0 (tiles 0-3)
140 ctx.object.y_,
141 ctx.tiles[0]); // col 0, row 0
143 ctx.object.y_ + 1,
144 ctx.tiles[1]); // col 0, row 1
146 ctx.object.y_ + 2,
147 ctx.tiles[2]); // col 0, row 2
149 ctx.object.y_ + 3,
150 ctx.tiles[3]); // col 0, row 3
151 // Column 1 (tiles 4-7)
152 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
153 ctx.object.y_,
154 ctx.tiles[4]); // col 1, row 0
155 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
156 ctx.object.y_ + 1,
157 ctx.tiles[5]); // col 1, row 1
158 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
159 ctx.object.y_ + 2,
160 ctx.tiles[6]); // col 1, row 2
161 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
162 ctx.object.y_ + 3,
163 ctx.tiles[7]); // col 1, row 3
164 } else if (ctx.tiles.size() >= 4) {
165 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
167 ctx.object.y_, ctx.tiles[0]);
169 ctx.object.y_ + 1, ctx.tiles[1]);
171 ctx.object.y_ + 2, ctx.tiles[2]);
173 ctx.object.y_ + 3, ctx.tiles[3]);
174 }
175 }
176}
177
179 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16_BothBG ($01:8C37)
180 //
181 // Despite the "_BothBG" suffix in usdasm, this routine does NOT explicitly
182 // write to both tilemaps; it uses the current tilemap pointers and is thus
183 // single-layer.
184 //
185 // Behavior: draw a 2x4 block, then advance by an additional 4 tiles before
186 // drawing the next block (net step = 2 tile width + 4 tile gap = 6 tiles).
187 int size = ctx.object.size_ & 0x0F;
188 int count = size + 1;
189
190 constexpr int kStepTiles = 6;
191
192 for (int s = 0; s < count; s++) {
193 const int base_x = ctx.object.x_ + (s * kStepTiles);
194
195 if (ctx.tiles.size() >= 8) {
196 // Column 0 (tiles 0-3)
197 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
198 ctx.tiles[0]); // col 0, row 0
199 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
200 ctx.tiles[1]); // col 0, row 1
201 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 2,
202 ctx.tiles[2]); // col 0, row 2
203 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 3,
204 ctx.tiles[3]); // col 0, row 3
205
206 // Column 1 (tiles 4-7)
207 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
208 ctx.tiles[4]); // col 1, row 0
209 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
210 ctx.tiles[5]); // col 1, row 1
211 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 2,
212 ctx.tiles[6]); // col 1, row 2
213 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 3,
214 ctx.tiles[7]); // col 1, row 3
215 } else if (ctx.tiles.size() >= 4) {
216 // Fallback: with 4 tiles we can only draw 1 column (1x4 pattern)
217 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
218 ctx.tiles[0]);
219 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
220 ctx.tiles[1]);
221 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 2,
222 ctx.tiles[2]);
223 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 3,
224 ctx.tiles[3]);
225 }
226 }
227}
228
230 // Pattern: Draws 2x2 tiles rightward (objects 0x07-0x08)
231 // ROM tile order is COLUMN-MAJOR: [col0_row0, col0_row1, col1_row0, col1_row1]
232 int size = ctx.object.size_ & 0x0F;
233
234 // Assembly: JSR RoomDraw_GetSize_1to16
235 // GetSize_1to16: count = size + 1
236 int count = size + 1;
237
238 for (int s = 0; s < count; s++) {
239 if (ctx.tiles.size() >= 4) {
240 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
242 ctx.object.y_,
243 ctx.tiles[0]); // col 0, row 0
245 ctx.object.y_ + 1,
246 ctx.tiles[1]); // col 0, row 1
247 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
248 ctx.object.y_,
249 ctx.tiles[2]); // col 1, row 0
250 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 2) + 1,
251 ctx.object.y_ + 1,
252 ctx.tiles[3]); // col 1, row 1
253 }
254 }
255}
256
258 // Pattern: 1x3 tiles rightward with caps (object 0x21)
259 int size = ctx.object.size_ & 0x0F;
260
261 if (ctx.tiles.size() >= 9) {
262 auto draw_column = [&](int x, int base) {
264 ctx.tiles[base + 0]);
266 ctx.tiles[base + 1]);
268 ctx.tiles[base + 2]);
269 };
270
271 draw_column(ctx.object.x_, 0);
272
273 int mid_cols = (size + 1) * 2;
274 for (int s = 0; s < mid_cols; s++) {
275 draw_column(ctx.object.x_ + 1 + s, 3);
276 }
277
278 draw_column(ctx.object.x_ + 1 + mid_cols, 6);
279 return;
280 }
281
282 int count = (size * 2) + 1;
283 for (int s = 0; s < count; s++) {
284 if (ctx.tiles.size() >= 2) {
286 ctx.object.y_, ctx.tiles[0]);
288 ctx.object.y_ + 1, ctx.tiles[1]);
289 }
290 }
291}
292
294 // Pattern: Rail with corner/middle/end (object 0x22)
295 int size = ctx.object.size_ & 0x0F;
296
297 int count = size + 2;
298 if (ctx.tiles.size() < 3)
299 return;
300
301 int x = ctx.object.x_;
302 // USDASM $01:8EF6-$01:8F01 suppresses the corner when the slot already
303 // contains the small-rail corner tile.
305 {0x00E2})) {
307 }
308 x++;
309 for (int s = 0; s < count; s++) {
311 x++;
312 }
314}
315
317 // Pattern: Rail with corner/middle/end (objects 0x23-0x2E, 0x3F-0x46)
318 int size = ctx.object.size_ & 0x0F;
319
320 int count = size + 1;
321 if (ctx.tiles.size() < 3)
322 return;
323
324 int x = ctx.object.x_;
325 // USDASM $01:8F65-$01:8F7F skips the leading cap when the existing tile is
326 // already the same corner, or one of the compatible trim corner tiles.
328 ctx, x, ctx.object.y_, {0x01DB, 0x01A6, 0x01DD, 0x01FC})) {
330 }
331 x++;
332 for (int s = 0; s < count; s++) {
334 x++;
335 }
337}
338
340 // Pattern: Long rail with corner/middle/end (object 0x5F)
341 int size = ctx.object.size_ & 0x0F;
342 int count = size + 21;
343 if (ctx.tiles.size() < 3)
344 return;
345
346 int x = ctx.object.x_;
347 // USDASM $01:8EF6-$01:8F01 uses the same small-rail-corner suppression path
348 // for the long horizontal rail.
350 {0x00E2})) {
352 }
353 x++;
354 for (int s = 0; s < count; s++) {
356 x++;
357 }
359}
360
362 DrawRightwardsCorners1x2(ctx, /*edge_is_top=*/true);
363}
364
366 DrawRightwardsCorners1x2(ctx, /*edge_is_top=*/false);
367}
368
370 // Pattern: 4x4 block rightward (object 0x33)
371 int size = ctx.object.size_ & 0x0F;
372
373 // Assembly: GetSize_1to16, so count = size + 1
374 int count = size + 1;
375
376 for (int s = 0; s < count; s++) {
377 if (ctx.tiles.size() >= 16) {
378 // Draw 4x4 pattern in COLUMN-MAJOR order (matching assembly)
379 // Iterate columns (x) first, then rows (y) within each column
380 for (int x = 0; x < 4; ++x) {
381 for (int y = 0; y < 4; ++y) {
383 ctx.object.x_ + (s * 4) + x,
384 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
385 }
386 }
387 }
388 }
389}
390
392 if (ctx.tiles.empty()) {
393 return;
394 }
395
396 // USDASM loads A=4 into GetSize_1to16_timesA. That adds four draws; it
397 // does not move the object's origin by three tiles.
398 const int count = (ctx.object.size_ & 0x0F) + 4;
399 for (int s = 0; s < count; ++s) {
401 ctx.object.y_, ctx.tiles[0]);
402 }
403}
404
406 // Pattern: 4x4 decoration with spacing (objects 0x36-0x37)
407 int size = ctx.object.size_ & 0x0F;
408
409 // Assembly: GetSize_1to16, so count = size + 1
410 int count = size + 1;
411
412 for (int s = 0; s < count; s++) {
413 if (ctx.tiles.size() >= 16) {
414 // Draw 4x4 pattern with spacing in COLUMN-MAJOR order (matching assembly)
415 for (int x = 0; x < 4; ++x) {
416 for (int y = 0; y < 4; ++y) {
418 ctx.object.x_ + (s * 6) + x,
419 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
420 }
421 }
422 }
423 }
424}
425
427 // Pattern: 2x3 statue with spacing (object 0x38)
428 // 2 columns × 3 rows = 6 tiles in COLUMN-MAJOR order
429 int size = ctx.object.size_ & 0x0F;
430
431 // Assembly: GetSize_1to16, so count = size + 1
432 int count = size + 1;
433
434 for (int s = 0; s < count; s++) {
435 if (ctx.tiles.size() >= 6) {
436 // Draw 2x3 pattern in COLUMN-MAJOR order (matching assembly)
437 for (int x = 0; x < 2; ++x) {
438 for (int y = 0; y < 3; ++y) {
440 ctx.object.x_ + (s * 4) + x,
441 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
442 }
443 }
444 }
445 }
446}
447
449 // Pattern: 2x4 pillar with spacing (objects 0x39, 0x3D)
450 // 2 columns × 4 rows = 8 tiles in COLUMN-MAJOR order
451 int size = ctx.object.size_ & 0x0F;
452
453 // Assembly: GetSize_1to16, so count = size + 1
454 int count = size + 1;
455
456 for (int s = 0; s < count; s++) {
457 if (ctx.tiles.size() >= 8) {
458 // Draw 2x4 pattern in COLUMN-MAJOR order (matching assembly)
459 for (int x = 0; x < 2; ++x) {
460 for (int y = 0; y < 4; ++y) {
462 ctx.object.x_ + (s * 6) + x,
463 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
464 }
465 }
466 }
467 }
468}
469
471 // Pattern: 4x3 decoration with spacing (objects 0x3A-0x3B)
472 // 4 columns × 3 rows = 12 tiles in COLUMN-MAJOR order
473 // usdasm RoomDraw_RightwardsDecor4x3spaced4_1to16 ($01:9387) draws the
474 // 4-column stamp, then ADC #$0008 after RoomDraw_1x3N_rightwards. The
475 // helper already advanced over the 4-column stamp, so start-to-start stride
476 // is 8 tile columns.
477 int size = ctx.object.size_ & 0x0F;
478
479 // Assembly: GetSize_1to16, so count = size + 1
480 int count = size + 1;
481
482 for (int s = 0; s < count; s++) {
483 if (ctx.tiles.size() >= 12) {
484 // Draw 4x3 pattern in COLUMN-MAJOR order (matching assembly)
485 for (int x = 0; x < 4; ++x) {
486 for (int y = 0; y < 3; ++y) {
488 ctx.object.x_ + (s * 8) + x,
489 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
490 }
491 }
492 }
493 }
494}
495
497 // Pattern: Doubled 2x2 with spacing (object 0x3C)
498 // 4 columns × 2 rows = 8 tiles in COLUMN-MAJOR order
499 int size = ctx.object.size_ & 0x0F;
500
501 // Assembly: GetSize_1to16, so count = size + 1
502 int count = size + 1;
503
504 for (int s = 0; s < count; s++) {
505 if (ctx.tiles.size() >= 8) {
506 // Draw doubled 2x2 pattern in COLUMN-MAJOR order (matching assembly)
507 for (int x = 0; x < 4; ++x) {
508 for (int y = 0; y < 2; ++y) {
510 ctx.object.x_ + (s * 6) + x,
511 ctx.object.y_ + y, ctx.tiles[x * 2 + y]);
512 }
513 }
514 }
515 }
516}
517
519 // Pattern: 2x2 decoration with large spacing (object 0x3E)
520 int size = ctx.object.size_ & 0x0F;
521
522 // Assembly: GetSize_1to16, so count = size + 1
523 int count = size + 1;
524
525 for (int s = 0; s < count; s++) {
526 if (ctx.tiles.size() >= 4) {
527 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
528 // tiles[0] → col 0, row 0 = top-left
529 // tiles[1] → col 0, row 1 = bottom-left
530 // tiles[2] → col 1, row 0 = top-right
531 // tiles[3] → col 1, row 1 = bottom-right
533 ctx.object.y_,
534 ctx.tiles[0]); // col 0, row 0
536 ctx.object.y_ + 1,
537 ctx.tiles[1]); // col 0, row 1
538 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
539 ctx.object.y_,
540 ctx.tiles[2]); // col 1, row 0
541 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
542 ctx.object.y_ + 1,
543 ctx.tiles[3]); // col 1, row 1
544 }
545 }
546}
547
549 // Pattern: Draws 4x2 tiles rightward (objects 0x49-0x4A: Floor Tile 4x2)
550 // Uses ROW-MAJOR tile order:
551 // row 0: tiles[0..3], row 1: tiles[4..7]
552 const int size = ctx.object.size_ & 0x0F;
553 const int count = size + 1; // GetSize_1to16
554
555 if (ctx.tiles.size() < 8) {
556 return;
557 }
558
559 for (int s = 0; s < count; ++s) {
560 const int base_x = ctx.object.x_ + (s * 4);
561
562 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
563 ctx.tiles[0]);
564 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
565 ctx.tiles[1]);
566 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_,
567 ctx.tiles[2]);
568 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_,
569 ctx.tiles[3]);
570
571 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
572 ctx.tiles[4]);
573 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
574 ctx.tiles[5]);
575 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_ + 1,
576 ctx.tiles[6]);
577 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_ + 1,
578 ctx.tiles[7]);
579 }
580}
581
583 // USDASM RoomDraw_RightwardsDecor4x2spaced8_1to16 ($01:96F9)
584 // jumps into RoomDraw_Downwards4x2VariableSpacing with A=0x0018:
585 // each step writes one 4x2 stamp, then advances 12 tiles right.
586 const int size = ctx.object.size_ & 0x0F;
587 const int count = size + 1; // GetSize_1to16
588
589 if (ctx.tiles.size() < 8) {
590 return;
591 }
592
593 for (int s = 0; s < count; ++s) {
594 const int base_x = ctx.object.x_ + (s * 12);
595 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
596 ctx.tiles[0]);
597 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
598 ctx.tiles[1]);
599 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_,
600 ctx.tiles[2]);
601 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_,
602 ctx.tiles[3]);
603
604 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
605 ctx.tiles[4]);
606 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
607 ctx.tiles[5]);
608 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_ + 1,
609 ctx.tiles[6]);
610 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_ + 1,
611 ctx.tiles[7]);
612 }
613}
614
616 // USDASM behavior:
617 // - Repeat left 2-column 3-row segment count times
618 // - Then append right 2-column edge once
619 // Tile layout is COLUMN-MAJOR:
620 // col0: [0..2], col1: [3..5], col2: [6..8], col3: [9..11]
621 const int size = ctx.object.size_ & 0x0F;
622 const int count = size + 1; // GetSize_1to16
623
624 if (ctx.tiles.size() < 12) {
625 return;
626 }
627
628 auto draw_column = [&](int x, int y, const gfx::TileInfo& t0,
629 const gfx::TileInfo& t1, const gfx::TileInfo& t2) {
631 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 1, t1);
632 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 2, t2);
633 };
634
635 for (int s = 0; s < count; ++s) {
636 const int base_x = ctx.object.x_ + (s * 2);
637 draw_column(base_x + 0, ctx.object.y_, ctx.tiles[0], ctx.tiles[1],
638 ctx.tiles[2]);
639 draw_column(base_x + 1, ctx.object.y_, ctx.tiles[3], ctx.tiles[4],
640 ctx.tiles[5]);
641 }
642
643 const int right_base_x = ctx.object.x_ + (count * 2);
644 draw_column(right_base_x + 0, ctx.object.y_, ctx.tiles[6], ctx.tiles[7],
645 ctx.tiles[8]);
646 draw_column(right_base_x + 1, ctx.object.y_, ctx.tiles[9], ctx.tiles[10],
647 ctx.tiles[11]);
648}
649
651 const int size = ctx.object.size_ & 0x0F;
652 const int count = size + 2;
653 if (ctx.tiles.empty()) {
654 return;
655 }
656
657 for (int s = 0; s < count; ++s) {
659 ctx.object.y_, ctx.tiles[0]);
660 }
661}
662
664 if (ctx.tiles.size() < 9) {
665 return;
666 }
667
668 // USDASM $0194BD-$0194DC draws one opening column, 2*(size+1) middle
669 // columns, then one closing column. $01B2F6 reads three words per column;
670 // "4x3" names the minimum footprint, not a twelve-word repeating stamp.
671 const int middle_columns = 2 * ((ctx.object.size_ & 0x0F) + 1);
672 auto draw_column = [&](int x, int tile_base) {
673 for (int row = 0; row < 3; ++row) {
675 ctx.tiles[tile_base + row]);
676 }
677 };
678 draw_column(ctx.object.x_, 0);
679 for (int column = 0; column < middle_columns; ++column) {
680 draw_column(ctx.object.x_ + 1 + column, 3);
681 }
682 draw_column(ctx.object.x_ + 1 + middle_columns, 6);
683}
684
686 const int size = ctx.object.size_ & 0x0F;
687 const int count = size + 1;
688 if (ctx.tiles.size() < 16) {
689 return;
690 }
691
692 for (int s = 0; s < count; ++s) {
693 const int base_x = ctx.object.x_ + (s * 4);
694 for (int x = 0; x < 4; ++x) {
695 for (int y = 0; y < 4; ++y) {
697 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
698 }
699 }
700 }
701}
702
704 const int size = ctx.object.size_ & 0x0F;
705 const int middle_columns = size + 2;
706 const int total_columns = size + 6;
707
708 if (ctx.tiles.size() >= 15) {
709 auto draw_column = [&](int x, int base_idx) {
711 ctx.tiles[base_idx + 0]);
713 ctx.tiles[base_idx + 1]);
715 ctx.tiles[base_idx + 2]);
716 };
717
718 // USDASM $01:8F36:
719 // - 2-column start cap (tiles 0..5)
720 // - repeated middle columns (tiles 6..8), repeated size+2 times
721 // - 2-column end cap (tiles 9..14)
722 draw_column(ctx.object.x_ + 0, 0);
723 draw_column(ctx.object.x_ + 1, 3);
724 for (int s = 0; s < middle_columns; ++s) {
725 draw_column(ctx.object.x_ + 2 + s, 6);
726 }
727 draw_column(ctx.object.x_ + 2 + middle_columns, 9);
728 draw_column(ctx.object.x_ + 3 + middle_columns, 12);
729 return;
730 }
731
732 if (ctx.tiles.empty()) {
733 return;
734 }
735 for (int x = 0; x < total_columns; ++x) {
736 for (int y = 0; y < 3; ++y) {
738 ctx.object.y_ + y, ctx.tiles[0]);
739 }
740 }
741}
742
744 const int size = ctx.object.size_ & 0x0F;
745 const int count = size + 1;
746 if (ctx.tiles.size() < 4) {
747 return;
748 }
749
750 for (int s = 0; s < count; ++s) {
751 const int base_x = ctx.object.x_ + (s * 4);
752 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
753 ctx.tiles[0]);
754 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
755 ctx.tiles[1]);
756 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
757 ctx.tiles[2]);
758 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
759 ctx.tiles[3]);
760 }
761}
762
764 const int size = ctx.object.size_ & 0x0F;
765 const int count = size + 8;
766 if (ctx.tiles.empty()) {
767 return;
768 }
769
770 for (int s = 0; s < count; ++s) {
772 ctx.object.y_, ctx.tiles[0]);
773 }
774}
775
777 const int size = ctx.object.size_ & 0x0F;
778 const int count = size + 1;
779 if (ctx.tiles.size() < 4) {
780 return;
781 }
782
783 for (int s = 0; s < count; ++s) {
784 const int base_x = ctx.object.x_ + (s * 2);
785 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
786 ctx.tiles[0]);
787 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
788 ctx.tiles[1]);
789 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
790 ctx.tiles[2]);
791 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
792 ctx.tiles[3]);
793 }
794}
795
799
800void RegisterRightwardsRoutines(std::vector<DrawRoutineInfo>& registry) {
801 // Note: Routine IDs are assigned based on the assembly routine table
802 // These rightwards routines are part of the core 40 draw routines
803 // Uses canonical IDs from DrawRoutineIds namespace
804
805 registry.push_back(DrawRoutineInfo{
807 .name = "Rightwards2x2_1to15or32",
808 .function = DrawRightwards2x2_1to15or32,
809 // USDASM: RoomDraw_Rightwards2x2_1to15or32 ($01:8B89) calls
810 // RoomDraw_Rightwards2x2 ($01:9895), which writes through the current
811 // tilemap pointer set (single-layer).
812 .draws_to_both_bgs = false,
813 .base_width = 2,
814 .base_height = 2,
815 .min_tiles = 4, // 2x2 block
817 });
818
819 registry.push_back(DrawRoutineInfo{
821 .name = "Rightwards2x4_1to15or26",
822 .function = DrawRightwards2x4_1to15or26,
823 // USDASM: RoomDraw_Rightwards2x4_1to15or26 ($01:8A92) uses RoomDraw_Nx4
824 // ($01:97F0) via pointers; single-layer.
825 .draws_to_both_bgs = false,
826 .base_width = 2,
827 .base_height = 4,
828 .min_tiles = 8, // 2x4 block
830 });
831
832 registry.push_back(DrawRoutineInfo{
834 .name = "Rightwards2x4_1to16",
835 .function = DrawRightwards2x4_1to16,
836 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16 ($01:8B0D) explicitly
837 // writes to both tilemaps ($7E2000 and $7E4000).
838 .draws_to_both_bgs = true,
839 .base_width = 2, // Adjacent spacing (s * 2)
840 .base_height = 4,
841 .min_tiles = 8, // 2x4 block
843 });
844
845 registry.push_back(DrawRoutineInfo{
847 .name = "Weird2x4_1to16",
848 .function = DrawRightwards2x4_1to16,
849 // USDASM: RoomDraw_Weird2x4_1_to_16 ($01:97DC) uses RoomDraw_Nx4
850 // through the current tilemap pointers, so object 0xB5 is single-layer.
851 .draws_to_both_bgs = false,
852 .base_width = 2,
853 .base_height = 4,
854 .min_tiles = 8,
856 });
857
858 registry.push_back(DrawRoutineInfo{
860 .name = "Rightwards2x4_1to16_BothBG",
862 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16_BothBG ($01:8C37) uses
863 // RoomDraw_Nx4 through the current tilemap pointers (single-layer).
864 .draws_to_both_bgs = false,
865 .base_width = 2, // 2x4 blocks; repeat step is 6 tiles (2 wide + 4 gap)
866 .base_height = 4,
867 .min_tiles = 8, // 2x4 block
869 });
870
871 registry.push_back(DrawRoutineInfo{
873 .name = "Rightwards2x2_1to16",
874 .function = DrawRightwards2x2_1to16,
875 .draws_to_both_bgs = false,
876 .base_width = 2,
877 .base_height = 2,
878 .min_tiles = 4, // 2x2 block
880 });
881
882 registry.push_back(DrawRoutineInfo{
884 .name = "Rightwards1x2_1to16_plus2",
886 .draws_to_both_bgs = false,
887 .base_width = 4,
888 .base_height = 3,
889 .min_tiles = 2, // cap + middle tiles
891 });
892
893 registry.push_back(DrawRoutineInfo{
895 .name = "RightwardsHasEdge1x1_1to16_plus3",
897 .draws_to_both_bgs = false,
898 .base_width = 4,
899 .base_height = 1,
900 .min_tiles = 3, // left edge + middle + right edge
902 });
903
904 registry.push_back(DrawRoutineInfo{
906 .name = "RightwardsHasEdge1x1_1to16_plus2",
908 .draws_to_both_bgs = false,
909 .base_width = 3,
910 .base_height = 1,
911 .min_tiles = 3, // left edge + middle + right edge
913 });
914
915 registry.push_back(DrawRoutineInfo{
917 .name = "RightwardsHasEdge1x1_1to16_plus23",
919 .draws_to_both_bgs = false,
920 .base_width = 23,
921 .base_height = 1,
922 .min_tiles = 3, // left edge + middle + right edge
924 });
925
926 registry.push_back(DrawRoutineInfo{
928 .name = "RightwardsTopCorners1x2_1to16_plus13",
930 .draws_to_both_bgs = false,
931 .base_width = 14,
932 .base_height = 2,
933 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
935 });
936
937 registry.push_back(DrawRoutineInfo{
939 .name = "RightwardsBottomCorners1x2_1to16_plus13",
941 .draws_to_both_bgs = false,
942 .base_width = 14,
943 .base_height = 2,
944 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
946 });
947
948 registry.push_back(DrawRoutineInfo{
950 .name = "Rightwards4x4_1to16",
951 .function = DrawRightwards4x4_1to16,
952 .draws_to_both_bgs = false,
953 .base_width = 4,
954 .base_height = 4,
955 .min_tiles = 16, // 4x4 block
957 });
958
959 registry.push_back(DrawRoutineInfo{
961 .name = "Rightwards1x1Solid_1to16_plus3",
963 .draws_to_both_bgs = false,
964 .base_width = 1,
965 .base_height = 1,
966 .min_tiles =
967 1, // object 0x34 repeats a single tile from the subtype-1 table
969 });
970
971 registry.push_back(DrawRoutineInfo{
973 .name = "RightwardsDecor4x4spaced2_1to16",
975 .draws_to_both_bgs = false,
976 .base_width = 6, // 4 tiles + 2 spacing
977 .base_height = 4,
978 .min_tiles = 16, // 4x4 block
980 });
981
982 registry.push_back(DrawRoutineInfo{
984 .name = "RightwardsStatue2x3spaced2_1to16",
986 .draws_to_both_bgs = false,
987 .base_width = 4, // 2 tiles + 2 spacing
988 .base_height = 3,
989 .min_tiles = 6, // 2x3 block
991 });
992
993 registry.push_back(DrawRoutineInfo{
995 .name = "RightwardsPillar2x4spaced4_1to16",
997 .draws_to_both_bgs = false,
998 .base_width = 6, // 2 tiles + 4 spacing
999 .base_height = 4,
1000 .min_tiles = 8, // 2x4 block
1002 });
1003
1004 registry.push_back(DrawRoutineInfo{
1006 .name = "RightwardsDecor4x3spaced4_1to16",
1008 .draws_to_both_bgs = false,
1009 .base_width =
1010 6, // 4 tiles + 2 spacing (actually the calculation seems off, kept as is)
1011 .base_height = 3,
1012 .min_tiles = 12, // 4x3 block
1014 });
1015
1016 registry.push_back(DrawRoutineInfo{
1018 .name = "RightwardsDoubled2x2spaced2_1to16",
1020 .draws_to_both_bgs = false,
1021 .base_width = 6, // 4 tiles + 2 spacing
1022 .base_height = 2,
1023 .min_tiles = 8, // 4x2 block
1025 });
1026
1027 registry.push_back(DrawRoutineInfo{
1029 .name = "RightwardsDecor2x2spaced12_1to16",
1031 .draws_to_both_bgs = false,
1032 .base_width = 14, // 2 tiles + 12 spacing
1033 .base_height = 2,
1034 .min_tiles = 4, // 2x2 block
1036 });
1037
1038 registry.push_back(DrawRoutineInfo{
1040 .name = "Rightwards4x2_1to16",
1041 .function = DrawRightwards4x2_1to16,
1042 .draws_to_both_bgs = false,
1043 .base_width = 4,
1044 .base_height = 2,
1045 .min_tiles = 8, // 4x2 block
1047 });
1048
1049 registry.push_back(DrawRoutineInfo{
1051 .name = "RightwardsDecor4x2spaced8_1to16",
1053 .draws_to_both_bgs = false,
1054 .base_width = 12, // 1 tile + 11-tile gap (12-tile step)
1055 .base_height = 8,
1056 .min_tiles = 8, // 1x8 column
1058 });
1059
1060 registry.push_back(DrawRoutineInfo{
1062 .name = "RightwardsCannonHole4x3_1to16",
1064 .draws_to_both_bgs = false,
1065 .base_width = 4,
1066 .base_height = 3,
1067 .min_tiles = 12, // 4x3 block
1069 });
1070
1071 registry.push_back(DrawRoutineInfo{
1073 .name = "RightwardsLine1x1_1to16plus1",
1075 .draws_to_both_bgs = false,
1076 .base_width = 1,
1077 .base_height = 1,
1078 .min_tiles = 1,
1080 });
1081
1082 registry.push_back(DrawRoutineInfo{
1084 .name = "RightwardsBar4x3_1to16",
1085 .function = DrawRightwardsBar4x3_1to16,
1086 .draws_to_both_bgs = false,
1087 .base_width = 4,
1088 .base_height = 3,
1089 .min_tiles = 9,
1091 });
1092
1093 registry.push_back(DrawRoutineInfo{
1095 .name = "RightwardsShelf4x4_1to16",
1096 .function = DrawRightwardsShelf4x4_1to16,
1097 .draws_to_both_bgs = false,
1098 .base_width = 4,
1099 .base_height = 4,
1100 .min_tiles = 16,
1102 });
1103
1104 registry.push_back(DrawRoutineInfo{
1106 .name = "RightwardsBigRail1x3_1to16plus5",
1108 .draws_to_both_bgs = false,
1109 .base_width = 6,
1110 .base_height = 3,
1111 .min_tiles = 15,
1113 });
1114
1115 registry.push_back(DrawRoutineInfo{
1117 .name = "RightwardsBlock2x2spaced2_1to16",
1119 .draws_to_both_bgs = false,
1120 .base_width = 4,
1121 .base_height = 2,
1122 .min_tiles = 4,
1124 });
1125
1126 registry.push_back(DrawRoutineInfo{
1128 .name = "RightwardsEdge1x1_1to16plus7",
1130 .draws_to_both_bgs = false,
1131 .base_width = 8,
1132 .base_height = 1,
1133 .min_tiles = 1,
1135 });
1136
1137 registry.push_back(DrawRoutineInfo{
1139 .name = "RightwardsPots2x2_1to16",
1140 .function = DrawRightwardsPots2x2_1to16,
1141 .draws_to_both_bgs = false,
1142 .base_width = 2,
1143 .base_height = 2,
1144 .min_tiles = 4,
1146 });
1147
1148 registry.push_back(DrawRoutineInfo{
1150 .name = "RightwardsHammerPegs2x2_1to16",
1152 .draws_to_both_bgs = false,
1153 .base_width = 2,
1154 .base_height = 2,
1155 .min_tiles = 4,
1157 });
1158}
1159
1160} // namespace draw_routines
1161} // namespace zelda3
1162} // 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 DrawRightwardsCorners1x2(const DrawContext &ctx, bool edge_is_top)
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 opening and closing caps.
void DrawRightwardsTopCorners1x2_1to16_plus13(const DrawContext &ctx)
Draw top corner 1x2 tiles with opening and closing caps.
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 with a +3 extent.
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 4x2 decorative stamps 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.