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 // usdasm RoomDraw_RightwardsDecor4x3spaced4_1to16 ($01:9387) draws the
491 // 4-column stamp, then ADC #$0008 after RoomDraw_1x3N_rightwards. The
492 // helper already advanced over the 4-column stamp, so start-to-start stride
493 // is 8 tile columns.
494 int size = ctx.object.size_ & 0x0F;
495
496 // Assembly: GetSize_1to16, so count = size + 1
497 int count = size + 1;
498
499 for (int s = 0; s < count; s++) {
500 if (ctx.tiles.size() >= 12) {
501 // Draw 4x3 pattern in COLUMN-MAJOR order (matching assembly)
502 for (int x = 0; x < 4; ++x) {
503 for (int y = 0; y < 3; ++y) {
505 ctx.object.x_ + (s * 8) + x,
506 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
507 }
508 }
509 }
510 }
511}
512
514 // Pattern: Doubled 2x2 with spacing (object 0x3C)
515 // 4 columns × 2 rows = 8 tiles in COLUMN-MAJOR order
516 int size = ctx.object.size_ & 0x0F;
517
518 // Assembly: GetSize_1to16, so count = size + 1
519 int count = size + 1;
520
521 for (int s = 0; s < count; s++) {
522 if (ctx.tiles.size() >= 8) {
523 // Draw doubled 2x2 pattern in COLUMN-MAJOR order (matching assembly)
524 for (int x = 0; x < 4; ++x) {
525 for (int y = 0; y < 2; ++y) {
527 ctx.object.x_ + (s * 6) + x,
528 ctx.object.y_ + y, ctx.tiles[x * 2 + y]);
529 }
530 }
531 }
532 }
533}
534
536 // Pattern: 2x2 decoration with large spacing (object 0x3E)
537 int size = ctx.object.size_ & 0x0F;
538
539 // Assembly: GetSize_1to16, so count = size + 1
540 int count = size + 1;
541
542 for (int s = 0; s < count; s++) {
543 if (ctx.tiles.size() >= 4) {
544 // Draw 2x2 pattern in COLUMN-MAJOR order (matching assembly)
545 // tiles[0] → col 0, row 0 = top-left
546 // tiles[1] → col 0, row 1 = bottom-left
547 // tiles[2] → col 1, row 0 = top-right
548 // tiles[3] → col 1, row 1 = bottom-right
550 ctx.object.y_,
551 ctx.tiles[0]); // col 0, row 0
553 ctx.object.y_ + 1,
554 ctx.tiles[1]); // col 0, row 1
555 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
556 ctx.object.y_,
557 ctx.tiles[2]); // col 1, row 0
558 DrawRoutineUtils::WriteTile8(ctx.target_bg, ctx.object.x_ + (s * 14) + 1,
559 ctx.object.y_ + 1,
560 ctx.tiles[3]); // col 1, row 1
561 }
562 }
563}
564
566 // Pattern: Draws 4x2 tiles rightward (objects 0x49-0x4A: Floor Tile 4x2)
567 // Uses ROW-MAJOR tile order:
568 // row 0: tiles[0..3], row 1: tiles[4..7]
569 const int size = ctx.object.size_ & 0x0F;
570 const int count = size + 1; // GetSize_1to16
571
572 if (ctx.tiles.size() < 8) {
573 return;
574 }
575
576 for (int s = 0; s < count; ++s) {
577 const int base_x = ctx.object.x_ + (s * 4);
578
579 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
580 ctx.tiles[0]);
581 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
582 ctx.tiles[1]);
583 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_,
584 ctx.tiles[2]);
585 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_,
586 ctx.tiles[3]);
587
588 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
589 ctx.tiles[4]);
590 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
591 ctx.tiles[5]);
592 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_ + 1,
593 ctx.tiles[6]);
594 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_ + 1,
595 ctx.tiles[7]);
596 }
597}
598
600 // USDASM RoomDraw_RightwardsDecor4x2spaced8_1to16 ($01:96F9)
601 // jumps into RoomDraw_Downwards4x2VariableSpacing with A=0x0018:
602 // each step writes one 4x2 stamp, then advances 12 tiles right.
603 const int size = ctx.object.size_ & 0x0F;
604 const int count = size + 1; // GetSize_1to16
605
606 if (ctx.tiles.size() < 8) {
607 return;
608 }
609
610 for (int s = 0; s < count; ++s) {
611 const int base_x = ctx.object.x_ + (s * 12);
612 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_,
613 ctx.tiles[0]);
614 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_,
615 ctx.tiles[1]);
616 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_,
617 ctx.tiles[2]);
618 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_,
619 ctx.tiles[3]);
620
621 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
622 ctx.tiles[4]);
623 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
624 ctx.tiles[5]);
625 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 2, ctx.object.y_ + 1,
626 ctx.tiles[6]);
627 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 3, ctx.object.y_ + 1,
628 ctx.tiles[7]);
629 }
630}
631
633 // USDASM behavior:
634 // - Repeat left 2-column 3-row segment count times
635 // - Then append right 2-column edge once
636 // Tile layout is COLUMN-MAJOR:
637 // col0: [0..2], col1: [3..5], col2: [6..8], col3: [9..11]
638 const int size = ctx.object.size_ & 0x0F;
639 const int count = size + 1; // GetSize_1to16
640
641 if (ctx.tiles.size() < 12) {
642 return;
643 }
644
645 auto draw_column = [&](int x, int y, const gfx::TileInfo& t0,
646 const gfx::TileInfo& t1, const gfx::TileInfo& t2) {
648 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 1, t1);
649 DrawRoutineUtils::WriteTile8(ctx.target_bg, x, y + 2, t2);
650 };
651
652 for (int s = 0; s < count; ++s) {
653 const int base_x = ctx.object.x_ + (s * 2);
654 draw_column(base_x + 0, ctx.object.y_, ctx.tiles[0], ctx.tiles[1],
655 ctx.tiles[2]);
656 draw_column(base_x + 1, ctx.object.y_, ctx.tiles[3], ctx.tiles[4],
657 ctx.tiles[5]);
658 }
659
660 const int right_base_x = ctx.object.x_ + (count * 2);
661 draw_column(right_base_x + 0, ctx.object.y_, ctx.tiles[6], ctx.tiles[7],
662 ctx.tiles[8]);
663 draw_column(right_base_x + 1, ctx.object.y_, ctx.tiles[9], ctx.tiles[10],
664 ctx.tiles[11]);
665}
666
668 const int size = ctx.object.size_ & 0x0F;
669 const int count = size + 2;
670 if (ctx.tiles.empty()) {
671 return;
672 }
673
674 for (int s = 0; s < count; ++s) {
676 ctx.object.y_, ctx.tiles[0]);
677 }
678}
679
681 const int size = ctx.object.size_ & 0x0F;
682 const int count = size + 1;
683 if (ctx.tiles.size() < 12) {
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 < 3; ++y) {
692 ctx.object.y_ + y, ctx.tiles[x * 3 + y]);
693 }
694 }
695 }
696}
697
699 const int size = ctx.object.size_ & 0x0F;
700 const int count = size + 1;
701 if (ctx.tiles.size() < 16) {
702 return;
703 }
704
705 for (int s = 0; s < count; ++s) {
706 const int base_x = ctx.object.x_ + (s * 4);
707 for (int x = 0; x < 4; ++x) {
708 for (int y = 0; y < 4; ++y) {
710 ctx.object.y_ + y, ctx.tiles[x * 4 + y]);
711 }
712 }
713 }
714}
715
717 const int size = ctx.object.size_ & 0x0F;
718 const int middle_columns = size + 2;
719 const int total_columns = size + 6;
720
721 if (ctx.tiles.size() >= 15) {
722 auto draw_column = [&](int x, int base_idx) {
724 ctx.tiles[base_idx + 0]);
726 ctx.tiles[base_idx + 1]);
728 ctx.tiles[base_idx + 2]);
729 };
730
731 // USDASM $01:8F36:
732 // - 2-column start cap (tiles 0..5)
733 // - repeated middle columns (tiles 6..8), repeated size+2 times
734 // - 2-column end cap (tiles 9..14)
735 draw_column(ctx.object.x_ + 0, 0);
736 draw_column(ctx.object.x_ + 1, 3);
737 for (int s = 0; s < middle_columns; ++s) {
738 draw_column(ctx.object.x_ + 2 + s, 6);
739 }
740 draw_column(ctx.object.x_ + 2 + middle_columns, 9);
741 draw_column(ctx.object.x_ + 3 + middle_columns, 12);
742 return;
743 }
744
745 if (ctx.tiles.empty()) {
746 return;
747 }
748 for (int x = 0; x < total_columns; ++x) {
749 for (int y = 0; y < 3; ++y) {
751 ctx.object.y_ + y, ctx.tiles[0]);
752 }
753 }
754}
755
757 const int size = ctx.object.size_ & 0x0F;
758 const int count = size + 1;
759 if (ctx.tiles.size() < 4) {
760 return;
761 }
762
763 for (int s = 0; s < count; ++s) {
764 const int base_x = ctx.object.x_ + (s * 4);
765 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
766 ctx.tiles[0]);
767 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
768 ctx.tiles[1]);
769 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
770 ctx.tiles[2]);
771 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
772 ctx.tiles[3]);
773 }
774}
775
777 const int size = ctx.object.size_ & 0x0F;
778 const int count = size + 8;
779 if (ctx.tiles.empty()) {
780 return;
781 }
782
783 for (int s = 0; s < count; ++s) {
785 ctx.object.y_, ctx.tiles[0]);
786 }
787}
788
790 const int size = ctx.object.size_ & 0x0F;
791 const int count = size + 1;
792 if (ctx.tiles.size() < 4) {
793 return;
794 }
795
796 for (int s = 0; s < count; ++s) {
797 const int base_x = ctx.object.x_ + (s * 2);
798 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 0,
799 ctx.tiles[0]);
800 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 0, ctx.object.y_ + 1,
801 ctx.tiles[1]);
802 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 0,
803 ctx.tiles[2]);
804 DrawRoutineUtils::WriteTile8(ctx.target_bg, base_x + 1, ctx.object.y_ + 1,
805 ctx.tiles[3]);
806 }
807}
808
812
813void RegisterRightwardsRoutines(std::vector<DrawRoutineInfo>& registry) {
814 // Note: Routine IDs are assigned based on the assembly routine table
815 // These rightwards routines are part of the core 40 draw routines
816 // Uses canonical IDs from DrawRoutineIds namespace
817
818 registry.push_back(DrawRoutineInfo{
820 .name = "Rightwards2x2_1to15or32",
821 .function = DrawRightwards2x2_1to15or32,
822 // USDASM: RoomDraw_Rightwards2x2_1to15or32 ($01:8B89) calls
823 // RoomDraw_Rightwards2x2 ($01:9895), which writes through the current
824 // tilemap pointer set (single-layer).
825 .draws_to_both_bgs = false,
826 .base_width = 2,
827 .base_height = 2,
828 .min_tiles = 4, // 2x2 block
830 });
831
832 registry.push_back(DrawRoutineInfo{
834 .name = "Rightwards2x4_1to15or26",
835 .function = DrawRightwards2x4_1to15or26,
836 // USDASM: RoomDraw_Rightwards2x4_1to15or26 ($01:8A92) uses RoomDraw_Nx4
837 // ($01:97F0) via pointers; single-layer.
838 .draws_to_both_bgs = false,
839 .base_width = 2,
840 .base_height = 4,
841 .min_tiles = 8, // 2x4 block
843 });
844
845 registry.push_back(DrawRoutineInfo{
847 .name = "Rightwards2x4_1to16",
848 .function = DrawRightwards2x4_1to16,
849 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16 ($01:8B0D) explicitly
850 // writes to both tilemaps ($7E2000 and $7E4000).
851 .draws_to_both_bgs = true,
852 .base_width = 2, // Adjacent spacing (s * 2)
853 .base_height = 4,
854 .min_tiles = 8, // 2x4 block
856 });
857
858 registry.push_back(DrawRoutineInfo{
860 .name = "Weird2x4_1to16",
861 .function = DrawRightwards2x4_1to16,
862 // USDASM: RoomDraw_Weird2x4_1_to_16 ($01:97DC) uses RoomDraw_Nx4
863 // through the current tilemap pointers, so object 0xB5 is single-layer.
864 .draws_to_both_bgs = false,
865 .base_width = 2,
866 .base_height = 4,
867 .min_tiles = 8,
869 });
870
871 registry.push_back(DrawRoutineInfo{
873 .name = "Rightwards2x4_1to16_BothBG",
875 // USDASM: RoomDraw_Rightwards2x4spaced4_1to16_BothBG ($01:8C37) uses
876 // RoomDraw_Nx4 through the current tilemap pointers (single-layer).
877 .draws_to_both_bgs = false,
878 .base_width = 2, // 2x4 blocks; repeat step is 6 tiles (2 wide + 4 gap)
879 .base_height = 4,
880 .min_tiles = 8, // 2x4 block
882 });
883
884 registry.push_back(DrawRoutineInfo{
886 .name = "Rightwards2x2_1to16",
887 .function = DrawRightwards2x2_1to16,
888 .draws_to_both_bgs = false,
889 .base_width = 2,
890 .base_height = 2,
891 .min_tiles = 4, // 2x2 block
893 });
894
895 registry.push_back(DrawRoutineInfo{
897 .name = "Rightwards1x2_1to16_plus2",
899 .draws_to_both_bgs = false,
900 .base_width = 4,
901 .base_height = 3,
902 .min_tiles = 2, // cap + middle tiles
904 });
905
906 registry.push_back(DrawRoutineInfo{
908 .name = "RightwardsHasEdge1x1_1to16_plus3",
910 .draws_to_both_bgs = false,
911 .base_width = 4,
912 .base_height = 1,
913 .min_tiles = 3, // left edge + middle + right edge
915 });
916
917 registry.push_back(DrawRoutineInfo{
919 .name = "RightwardsHasEdge1x1_1to16_plus2",
921 .draws_to_both_bgs = false,
922 .base_width = 3,
923 .base_height = 1,
924 .min_tiles = 3, // left edge + middle + right edge
926 });
927
928 registry.push_back(DrawRoutineInfo{
930 .name = "RightwardsHasEdge1x1_1to16_plus23",
932 .draws_to_both_bgs = false,
933 .base_width = 23,
934 .base_height = 1,
935 .min_tiles = 3, // left edge + middle + right edge
937 });
938
939 registry.push_back(DrawRoutineInfo{
941 .name = "RightwardsTopCorners1x2_1to16_plus13",
943 .draws_to_both_bgs = false,
944 .base_width = 1,
945 .base_height = 2,
946 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
948 });
949
950 registry.push_back(DrawRoutineInfo{
952 .name = "RightwardsBottomCorners1x2_1to16_plus13",
954 .draws_to_both_bgs = false,
955 .base_width = 1,
956 .base_height = 2, // spans y+1 to y+2
957 .min_tiles = 6, // cap + body + endpoint tile spans from subtype-1 table
959 });
960
961 registry.push_back(DrawRoutineInfo{
963 .name = "Rightwards4x4_1to16",
964 .function = DrawRightwards4x4_1to16,
965 .draws_to_both_bgs = false,
966 .base_width = 4,
967 .base_height = 4,
968 .min_tiles = 16, // 4x4 block
970 });
971
972 registry.push_back(DrawRoutineInfo{
974 .name = "Rightwards1x1Solid_1to16_plus3",
976 .draws_to_both_bgs = false,
977 .base_width = 1,
978 .base_height = 1,
979 .min_tiles =
980 1, // object 0x34 repeats a single tile from the subtype-1 table
982 });
983
984 registry.push_back(DrawRoutineInfo{
986 .name = "RightwardsDecor4x4spaced2_1to16",
988 .draws_to_both_bgs = false,
989 .base_width = 6, // 4 tiles + 2 spacing
990 .base_height = 4,
991 .min_tiles = 16, // 4x4 block
993 });
994
995 registry.push_back(DrawRoutineInfo{
997 .name = "RightwardsStatue2x3spaced2_1to16",
999 .draws_to_both_bgs = false,
1000 .base_width = 4, // 2 tiles + 2 spacing
1001 .base_height = 3,
1002 .min_tiles = 6, // 2x3 block
1004 });
1005
1006 registry.push_back(DrawRoutineInfo{
1008 .name = "RightwardsPillar2x4spaced4_1to16",
1010 .draws_to_both_bgs = false,
1011 .base_width = 6, // 2 tiles + 4 spacing
1012 .base_height = 4,
1013 .min_tiles = 8, // 2x4 block
1015 });
1016
1017 registry.push_back(DrawRoutineInfo{
1019 .name = "RightwardsDecor4x3spaced4_1to16",
1021 .draws_to_both_bgs = false,
1022 .base_width =
1023 6, // 4 tiles + 2 spacing (actually the calculation seems off, kept as is)
1024 .base_height = 3,
1025 .min_tiles = 12, // 4x3 block
1027 });
1028
1029 registry.push_back(DrawRoutineInfo{
1031 .name = "RightwardsDoubled2x2spaced2_1to16",
1033 .draws_to_both_bgs = false,
1034 .base_width = 6, // 4 tiles + 2 spacing
1035 .base_height = 2,
1036 .min_tiles = 8, // 4x2 block
1038 });
1039
1040 registry.push_back(DrawRoutineInfo{
1042 .name = "RightwardsDecor2x2spaced12_1to16",
1044 .draws_to_both_bgs = false,
1045 .base_width = 14, // 2 tiles + 12 spacing
1046 .base_height = 2,
1047 .min_tiles = 4, // 2x2 block
1049 });
1050
1051 registry.push_back(DrawRoutineInfo{
1053 .name = "Rightwards4x2_1to16",
1054 .function = DrawRightwards4x2_1to16,
1055 .draws_to_both_bgs = false,
1056 .base_width = 4,
1057 .base_height = 2,
1058 .min_tiles = 8, // 4x2 block
1060 });
1061
1062 registry.push_back(DrawRoutineInfo{
1064 .name = "RightwardsDecor4x2spaced8_1to16",
1066 .draws_to_both_bgs = false,
1067 .base_width = 12, // 1 tile + 11-tile gap (12-tile step)
1068 .base_height = 8,
1069 .min_tiles = 8, // 1x8 column
1071 });
1072
1073 registry.push_back(DrawRoutineInfo{
1075 .name = "RightwardsCannonHole4x3_1to16",
1077 .draws_to_both_bgs = false,
1078 .base_width = 4,
1079 .base_height = 3,
1080 .min_tiles = 12, // 4x3 block
1082 });
1083
1084 registry.push_back(DrawRoutineInfo{
1086 .name = "RightwardsLine1x1_1to16plus1",
1088 .draws_to_both_bgs = false,
1089 .base_width = 1,
1090 .base_height = 1,
1091 .min_tiles = 1,
1093 });
1094
1095 registry.push_back(DrawRoutineInfo{
1097 .name = "RightwardsBar4x3_1to16",
1098 .function = DrawRightwardsBar4x3_1to16,
1099 .draws_to_both_bgs = false,
1100 .base_width = 4,
1101 .base_height = 3,
1102 .min_tiles = 12,
1104 });
1105
1106 registry.push_back(DrawRoutineInfo{
1108 .name = "RightwardsShelf4x4_1to16",
1109 .function = DrawRightwardsShelf4x4_1to16,
1110 .draws_to_both_bgs = false,
1111 .base_width = 4,
1112 .base_height = 4,
1113 .min_tiles = 16,
1115 });
1116
1117 registry.push_back(DrawRoutineInfo{
1119 .name = "RightwardsBigRail1x3_1to16plus5",
1121 .draws_to_both_bgs = false,
1122 .base_width = 6,
1123 .base_height = 3,
1124 .min_tiles = 15,
1126 });
1127
1128 registry.push_back(DrawRoutineInfo{
1130 .name = "RightwardsBlock2x2spaced2_1to16",
1132 .draws_to_both_bgs = false,
1133 .base_width = 4,
1134 .base_height = 2,
1135 .min_tiles = 4,
1137 });
1138
1139 registry.push_back(DrawRoutineInfo{
1141 .name = "RightwardsEdge1x1_1to16plus7",
1143 .draws_to_both_bgs = false,
1144 .base_width = 8,
1145 .base_height = 1,
1146 .min_tiles = 1,
1148 });
1149
1150 registry.push_back(DrawRoutineInfo{
1152 .name = "RightwardsPots2x2_1to16",
1153 .function = DrawRightwardsPots2x2_1to16,
1154 .draws_to_both_bgs = false,
1155 .base_width = 2,
1156 .base_height = 2,
1157 .min_tiles = 4,
1159 });
1160
1161 registry.push_back(DrawRoutineInfo{
1163 .name = "RightwardsHammerPegs2x2_1to16",
1165 .draws_to_both_bgs = false,
1166 .base_width = 2,
1167 .base_height = 2,
1168 .min_tiles = 4,
1170 });
1171}
1172
1173} // namespace draw_routines
1174} // namespace zelda3
1175} // 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 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.