16namespace draw_routines {
20constexpr int kThievesTownEastAtticRoomId = 0x65;
26 constexpr int kCmpPc = 0xB3E3;
28 const auto opcode = rom->
ReadByte(kCmpPc);
29 const auto operand = rom->
ReadWord(kCmpPc + 1);
30 if (opcode.ok() && *opcode == 0xC9 && operand.ok()) {
34 return kThievesTownEastAtticRoomId;
40 constexpr int kLdaPc = 0xA7D3;
42 const auto opcode = rom->
ReadByte(kLdaPc);
43 const auto address = rom->
ReadWord(kLdaPc + 1);
44 const auto bank = rom->
ReadByte(kLdaPc + 3);
45 if (opcode.ok() && *opcode == 0xAF && address.ok() && bank.ok() &&
46 *bank == 0x7E && *address >= 0xF000 && *address < 0xF250 &&
47 (*address & 1) == 0) {
48 return (*address - 0xF000) / 2;
51 return kThievesTownEastAtticRoomId;
56 return tiles[index % tiles.size()];
60 int h, std::span<const gfx::TileInfo> tiles,
61 size_t start_index = 0) {
65 size_t index = start_index;
66 for (
int x = 0; x < w; ++x) {
67 for (
int y = 0; y < h; ++y) {
75 int h, std::span<const gfx::TileInfo> tiles,
76 size_t start_index = 0) {
80 size_t index = start_index;
81 for (
int y = 0; y < h; ++y) {
82 for (
int x = 0; x < w; ++x) {
92 columns, 3, ctx.
tiles, start_index);
97 ctx.
tiles, start_index);
102 ctx.
tiles, start_index);
106 size_t start_index) {
112 if (ctx.
tiles.size() < 64)
122 std::span<const gfx::TileInfo> tiles) {
127 std::span<const gfx::TileInfo> tiles) {
139 if (ctx.
tiles.size() < 8)
154 int object_data_offset,
156 std::array<gfx::TileInfo, 16> tiles{};
157 for (
int i = 0; i < count && i < 16; ++i) {
159 object_data_offset + (i * 2));
169 constexpr int kWaterOverlayData = 0x271CD;
170 constexpr int kFloorTiles = 0x0110;
172 for (
int entry = kWaterOverlayData;; entry += 3) {
175 if (!b0.ok() || !b1.ok() || (*b0 == 0xFF && *b1 == 0xFF)) {
178 const int x = *b0 >> 2;
179 const int y = *b1 >> 2;
180 const int blocks_x = (*b0 & 0x03) + 1;
181 const int blocks_y = (*b1 & 0x03) + 1;
182 for (
int by = 0; by < blocks_y; ++by) {
183 for (
int bx = 0; bx < blocks_x; ++bx) {
184 for (
int row = 0; row < 4; ++row) {
185 for (
int col = 0; col < 4; ++col) {
188 tiles[((row & 1) * 4) + col]);
200 constexpr size_t kSpanTiles = 40;
201 const bool use_open_tiles = ctx.
state !=
nullptr &&
203 ctx.
tiles.size() >= kSpanTiles * 2;
204 const size_t start_index = use_open_tiles ? kSpanTiles : 0;
205 if (ctx.
tiles.size() < start_index + kSpanTiles)
210 if (use_open_tiles && ctx.
rom !=
nullptr && ctx.
secondary_bg !=
nullptr) {
216 if (index >= ctx.
tiles.size())
224 size_t start_index,
int columns) {
225 for (
int x = 0; x < columns; ++x) {
226 for (
int y = 0; y < 3; ++y) {
233 size_t start_index) {
241 size_t start_index) {
252 0x3C15, 0x3C15, 0x3C15};
255 std::array<gfx::TileInfo, 3> fill_tiles;
256 for (
size_t i = 0; i < fill_tiles.size(); ++i) {
258 if (ctx.
rom !=
nullptr) {
260 static_cast<int>(i * 2));
273 for (
int x = 0; x < column_count; ++x) {
276 for (
int y = 1; y < height - 1; ++y) {
281 ctx.
object.
y_ + height - 1, fill_tiles[2]);
288 for (
int i = 0; i < direction; ++i) {
299 size_t start_index,
int fill_width) {
302 for (
int i = 0; i < fill_width; ++i) {
306 const int left_cap_x = 1 + fill_width;
309 for (
int i = 0; i < 4; ++i) {
313 const int right_cap_x = left_cap_x + 5;
316 for (
int i = 0; i < fill_width; ++i) {
329 if (!is_open && ctx.
state) {
336 size_t start_index = is_open && ctx.
tiles.size() >= 32 ? 16 : 0;
337 for (
int x = 0; x < 4; ++x) {
338 for (
int y = 0; y < 4; ++y) {
341 ctx.
tiles[start_index +
static_cast<size_t>(x * 4 + y)]);
347 size_t start_index = 0;
348 if (is_open && ctx.
object.
id_ == 0xF99 && ctx.
tiles.size() >= 8) {
351 if (ctx.
tiles.size() < start_index + 4) {
356 ctx.
tiles[start_index]);
358 ctx.
tiles[start_index + 1]);
360 ctx.
tiles[start_index + 2]);
382 if (ctx.
tiles.size() >= 1) {
392 absl::StatusOr<std::shared_ptr<const CustomObject>> result =
395 : absl::StatusOr<std::shared_ptr<const CustomObject>>(
399 if (!result.ok() || !result.value()) {
401 if (ctx.
tiles.size() >= 1) {
408 const auto& custom_obj = *result.value();
409 if (custom_obj.IsEmpty()) {
414 for (
const auto& entry : custom_obj.tiles) {
417 if (entry.tile_data == 0) {
421 const uint16_t runtime_word =
427 int draw_x = ctx.
object.
x_ + entry.rel_x;
428 int draw_y = ctx.
object.
y_ + entry.rel_y;
441 if (ctx.
tiles.size() >= 2) {
446 if (ctx.
tiles.size() >
static_cast<size_t>(tile_index)) {
448 ctx.
tiles[tile_index]);
456 if (ctx.
tiles.empty()) {
468 if (ctx.
tiles.size() >= 4) {
485 if (ctx.
tiles.size() >=
static_cast<size_t>(width * height)) {
486 for (
int y = 0; y < height; ++y) {
487 for (
int x = 0; x < width; ++x) {
490 ctx.
tiles[y * width + x]);
504 Draw1x3NRightwards(ctx, 6, 0);
509 Draw1x3NRightwards(ctx, 6, 0);
515 Draw1x3NRightwards(ctx, 8, 0);
521 if (ctx.
tiles.size() < 8)
530 if (ctx.
tiles.size() < 48)
545 if (ctx.
tiles.size() < 16)
559 if (ctx.
tiles.size() < 84)
562 constexpr std::array<int, 14> kSourceColumns = {
563 0, 1, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1, 0,
565 for (
int y = 0; y < 14; ++y) {
566 for (
int x = 0; x < 14; ++x) {
568 if (x >= 7 && x <= 12) {
570 }
else if (x == 13) {
582 if (ctx.
tiles.size() < 26)
585 auto write = [&](
int dx,
int dy,
size_t tile_index,
586 bool force_horizontal_mirror =
false,
587 bool toggle_horizontal_mirror =
false) {
589 if (force_horizontal_mirror) {
591 }
else if (toggle_horizontal_mirror) {
599 for (
int section = 0; section < 6; ++section) {
600 const int x = section * 2;
606 write(x + 2, 2, 1,
true);
611 for (
int row = 0; row < 3; ++row) {
612 const size_t wall_tile =
static_cast<size_t>(2 + row);
613 const size_t center_tile =
static_cast<size_t>(5 + row);
614 const int y = 3 + row;
616 for (
int x : {0, 2, 10, 12}) {
617 write(x, y, wall_tile);
619 for (
int x : {1, 3, 11, 13}) {
620 write(x, y, wall_tile,
true);
622 for (
int x : {4, 6, 8}) {
623 write(x, y, center_tile);
625 for (
int x : {5, 7, 9}) {
626 write(x, y, center_tile,
true);
633 write(13, 0, 8,
true);
634 write(13, 1, 8,
true);
636 write(13, 2, 9,
true);
640 for (
int row = 0; row < 4; ++row) {
641 const int y = 10 + row;
642 write(3, y,
static_cast<size_t>(10 + row));
643 write(10, y,
static_cast<size_t>(10 + row),
646 write(4, y,
static_cast<size_t>(14 + row));
647 write(9, y,
static_cast<size_t>(14 + row),
650 write(5, y,
static_cast<size_t>(18 + row));
651 write(8, y,
static_cast<size_t>(18 + row),
654 write(6, y,
static_cast<size_t>(22 + row));
655 write(7, y,
static_cast<size_t>(22 + row),
665 if (ctx.
tiles.size() < 56)
676 if (ctx.
tiles.empty())
680 for (
int x = 0; x < 3; ++x) {
683 TileAtWrapped(ctx.
tiles,
static_cast<size_t>(x)));
687 for (
int y = 1; y <= 3; ++y) {
688 for (
int x = 0; x < 3; ++x) {
691 TileAtWrapped(ctx.
tiles,
static_cast<size_t>(3 + x)));
696 for (
int x = 0; x < 3; ++x) {
699 TileAtWrapped(ctx.
tiles,
static_cast<size_t>(6 + x)));
706 if (ctx.
tiles.empty())
708 Draw1x3NRightwards(ctx, 4, 0, 0);
709 Draw1x3NRightwards(ctx, 4, 12, 3);
722 if (ctx.
tiles.size() < 32)
724 Draw4x4ColumnMajor(ctx, 0, 0, 0);
725 Draw4x4ColumnMajor(ctx, 0, 2, 0);
726 Draw4x4ColumnMajor(ctx, 0, 6, 16);
735 if (ctx.
state !=
nullptr &&
739 DrawFloorLightGrid(ctx);
744 DrawFloorLightGrid(ctx);
749 Draw4x4ColumnMajor(ctx, 0, 0, 0);
757 if (ctx.
tiles.size() < 8)
760 for (
int block_y = 0; block_y < 2; ++block_y) {
761 for (
int block_x = 0; block_x < 5; ++block_x) {
776 if (ctx.
tiles.empty())
778 Draw1x3NRightwards(ctx, 3, 0, 0);
779 Draw1x3NRightwards(ctx, 3, 9, 3);
787 if (ctx.
tiles.empty())
790 Draw4x4ColumnMajor(ctx, 0, 0, 0);
791 Draw4x4ColumnMajor(ctx, -2, 4, 16);
792 Draw4x4ColumnMajor(ctx, 2, 4, 16);
798 if (ctx.
tiles.size() < 4)
813 if (ctx.
tiles.size() < 16)
817 for (
int x = 0; x < 4; ++x) {
818 for (
int y = 0; y < 4; ++y) {
827 if (ctx.
tiles.size() < 12)
831 for (
int x = 0; x < 4; ++x) {
832 for (
int y = 0; y < 3; ++y) {
844 ctx.
tiles.size() < 2) {
848 constexpr std::array<int, 3> kTopRows = {0, 3, 6};
849 constexpr std::array<int, 3> kBottomRows = {1, 4, 7};
850 for (
int col = 0; col < 3; ++col) {
851 const int x = ctx.
object.
x_ + col * 2;
852 for (
int row : kTopRows) {
856 for (
int row : kBottomRows) {
871 if (ctx.
tiles.empty())
875 const int middle_columns = (size + 1) * 2;
877 Draw1x5Column(ctx, 0, 0);
878 for (
int i = 0; i < middle_columns; ++i) {
879 Draw1x5Column(ctx, 1 + i, 5);
881 Draw1x5Column(ctx, 1 + middle_columns, 10);
887 if (ctx.
tiles.empty())
891 const int middle_columns = (size + 1) * 2;
895 for (
int i = 0; i < middle_columns; ++i) {
918 "Draw4x4BlocksIn4x4SuperSquare: obj=0x%03X pos=(%d,%d) size=0x%02X "
919 "tiles=%zu size_x=%d size_y=%d",
921 ctx.
tiles.size(), size_x, size_y);
923 if (ctx.
tiles.empty()) {
925 "Draw4x4BlocksIn4x4SuperSquare: SKIPPING - no tiles loaded!");
930 const auto& tile = ctx.
tiles[0];
932 "Draw4x4BlocksIn4x4SuperSquare: tile[0] id=%d palette=%d", tile.id_,
935 for (
int sy = 0; sy < size_y; ++sy) {
936 for (
int sx = 0; sx < size_x; ++sx) {
938 int base_x = ctx.
object.
x_ + (sx * 4);
939 int base_y = ctx.
object.
y_ + (sy * 4);
942 for (
int y = 0; y < 4; ++y) {
943 for (
int x = 0; x < 4; ++x) {
958 if (ctx.
tiles.empty())
961 const auto& tile = ctx.
tiles[0];
962 for (
int sy = 0; sy < size_y; ++sy) {
963 for (
int sx = 0; sx < size_x; ++sx) {
964 int base_x = ctx.
object.
x_ + (sx * 3);
965 int base_y = ctx.
object.
y_ + (sy * 3);
967 for (
int y = 0; y < 3; ++y) {
968 for (
int x = 0; x < 3; ++x) {
984 if (ctx.
tiles.empty())
986 if (ctx.
tiles.size() < 8) {
993 for (
int sy = 0; sy < size_y; ++sy) {
994 for (
int sx = 0; sx < size_x; ++sx) {
995 int base_x = ctx.
object.
x_ + (sx * 4);
996 int base_y = ctx.
object.
y_ + (sy * 4);
1020 if (ctx.
tiles.size() < 24)
1037 for (
int xx = 1; xx < max; ++xx) {
1038 for (
int yy = 1; yy < max; ++yy) {
1048 for (
int yy = 1; yy < max; ++yy) {
1059 int size_x = ((ctx.
object.
size_ >> 2) & 0x03) + 1;
1062 if (ctx.
tiles.size() < 4)
1065 for (
int sy = 0; sy < size_y; ++sy) {
1066 for (
int sx = 0; sx < size_x; ++sx) {
1067 int base_x = ctx.
object.
x_ + (sx * 2);
1068 int base_y = ctx.
object.
y_ + (sy * 2);
1087 const int size_x = (ctx.
object.
size_ >> 2) & 0x03;
1089 if (ctx.
tiles.size() < 16) {
1093 auto draw_row = [&](
int y,
int payload_row) {
1094 const int first = payload_row * 4;
1097 for (
int pair = 0; pair <= size_x; ++pair) {
1098 const int x = ctx.
object.
x_ + 1 + pair * 2;
1101 ctx.
tiles[first + 2]);
1104 y, ctx.
tiles[first + 3]);
1109 for (
int row = 0; row < 2 * size_y + 1; ++row) {
1126 int count_x = size_x + 2;
1127 int count_y = size_y + 2;
1129 if (ctx.
tiles.empty())
1131 if (ctx.
tiles.size() < 8) {
1133 for (
int yy = 0; yy < count_y; ++yy) {
1134 for (
int xx = 0; xx < count_x; ++xx) {
1135 int base_x = ctx.
object.
x_ + (xx * 4);
1136 int base_y = ctx.
object.
y_ + (yy * 4);
1138 ctx.
tiles[
static_cast<size_t>((xx + yy) % ctx.
tiles.size())];
1139 for (
int y = 0; y < 4; ++y) {
1140 for (
int x = 0; x < 4; ++x) {
1156 constexpr int kWaterOnTiles = 0x1438;
1157 const auto tiles = LoadRoomDrawObjectTiles(ctx, kWaterOnTiles, 16);
1158 const int base_x = ctx.
object.
x_ + (2 * (size_x + 1));
1159 const int base_y = ctx.
object.
y_ + (2 * (size_y + 1));
1160 for (
int row = 0; row < 4; ++row) {
1161 for (
int col = 0; col < 4; ++col) {
1163 base_y + row, tiles[(row * 4) + col]);
1175 const int chunks = 2 * size_y + 3;
1176 for (
int chunk = 0; chunk < chunks; ++chunk) {
1177 for (
int xx = 0; xx < count_x; ++xx) {
1178 const int base_x = ctx.
object.
x_ + (xx * 4);
1179 const int base_y = ctx.
object.
y_ + (chunk * 2);
1180 for (
int x = 0; x < 4; ++x) {
1191 for (
int yy = 0; yy < count_y; ++yy) {
1192 for (
int xx = 0; xx < count_x; ++xx) {
1193 int base_x = ctx.
object.
x_ + (xx * 4);
1194 int base_y = ctx.
object.
y_ + (yy * 4);
1196 for (
int x = 0; x < 4; ++x) {
1220 if (ctx.
tiles.size() < 16)
1248 if (ctx.
tiles.size() < 12)
1258 if (ctx.
tiles.size() < 16)
1273 if (ctx.
tiles.size() < 16)
1284 const bool south_lower =
1287 size_t tile_index = 0;
1288 for (
int col = 0; col < 4; ++col) {
1289 for (
int row = 0; row < 4; ++row) {
1290 const auto& tile = TileAtWrapped(ctx.
tiles, tile_index++);
1293 if ((!south_lower && row == 0) || (south_lower && row == 3)) {
1312 if (ctx.
tiles.size() < 6) {
1322 tile.horizontal_mirror_ =
true;
1326 for (
int col = 0; col < 5; ++col) {
1327 const int left_x = base_x + 2 + col;
1328 const int right_x = base_x + 9 + col;
1334 with_horizontal_mirror(ctx.
tiles[2]));
1338 with_horizontal_mirror(ctx.
tiles[4]));
1342 with_horizontal_mirror(ctx.
tiles[5]));
1347 with_horizontal_mirror(ctx.
tiles[0]));
1348 for (
int x_offset : {1, 7, 8, 14}) {
1355 with_horizontal_mirror(ctx.
tiles[3]));
1372 if (ctx.
tiles.size() < 16) {
1378 const bool is_open =
1379 ctx.
state !=
nullptr && ctx.
room_id == BombableFloorRoomId(ctx.
rom) &&
1381 const size_t state_offset = is_open ? 16 : 0;
1383 for (
int block_y = 0; block_y < 2; ++block_y) {
1384 for (
int block_x = 0; block_x < 2; ++block_x) {
1385 const size_t block_offset =
1386 state_offset +
static_cast<size_t>((block_y * 2 + block_x) * 4);
1387 for (
int x = 0; x < 2; ++x) {
1388 for (
int y = 0; y < 2; ++y) {
1392 ctx.
tiles[block_offset +
static_cast<size_t>(x * 2 + y)]);
1402 ctx.
tiles.size() < 24) {
1408 const int height = direction * 2 + 6;
1412 DrawMovingWallFill(ctx, ctx.
object.
x_ - column_count, column_count, height);
1413 DrawMovingWallPlatform(ctx, direction);
1419 ctx.
tiles.size() < 24) {
1425 const int height = direction * 2 + 6;
1429 DrawMovingWallPlatform(ctx, direction);
1430 DrawMovingWallFill(ctx, ctx.
object.
x_ + 3, column_count, height);
1441 if (row_count <= 0 || tile_offset < 0 || ctx.
tiles.empty()) {
1445 if (tile_offset >=
static_cast<int>(ctx.
tiles.size())) {
1449 const int available_tiles =
static_cast<int>(ctx.
tiles.size()) - tile_offset;
1451 const int rows_to_draw = std::min(row_count, available_rows);
1453 for (
int row = 0; row < rows_to_draw; ++row) {
1458 ctx.
tiles[row_base + col]);
1476 const bool tag_allows_water =
1478 const bool water_active = tag_allows_water && (ctx.
state !=
nullptr) &&
1481 const int row_count = water_active ? 5 : 3;
1482 const int tile_offset = water_active ? 12 : 0;
1483 DrawWaterFaceRows(ctx, row_count, tile_offset);
1489 DrawWaterFaceRows(ctx, 5, 0);
1495 DrawWaterFaceRows(ctx, 7, 0);
1509 if (ctx.
tiles.size() < 68)
1512 const int size_x = (ctx.
object.
size_ >> 2) & 0x03;
1514 const int width_blocks = size_x + 4;
1515 const int height_blocks = size_y + 1;
1518 DrawPlatform1x3Rightwards(ctx, 0, 0, 0,
1520 for (
int x = 0; x < width_blocks; ++x) {
1521 DrawPlatform1x3Rightwards(ctx, 3 + x * 2, 0,
1524 DrawPlatform1x3Rightwards(ctx, 3 + width_blocks * 2, 0,
1528 for (
int y = 0; y < height_blocks; ++y) {
1529 const int dy = 3 + y * 2;
1530 DrawPlatform3x2(ctx, 0, dy, 24);
1531 for (
int x = 0; x < width_blocks; ++x) {
1532 DrawPlatform2x2(ctx, 3 + x * 2, dy, 30);
1534 DrawPlatform3x2(ctx, 3 + width_blocks * 2, dy,
1539 const int bottom_y = 3 + height_blocks * 2;
1540 DrawPlatform1x3Rightwards(ctx, 0, bottom_y, 40,
1542 for (
int x = 0; x < width_blocks; ++x) {
1543 DrawPlatform1x3Rightwards(ctx, 3 + x * 2, bottom_y,
1546 DrawPlatform1x3Rightwards(ctx, 3 + width_blocks * 2, bottom_y,
1551 DrawPlatform2x2(ctx, width_blocks + 2,
1552 bottom_y - (height_blocks + 1),
1560 if (ctx.
tiles.size() < 21)
1563 const int size_x = (ctx.
object.
size_ >> 2) & 0x03;
1565 const int fill_width = size_x + 1;
1566 const int body_rows = size_y * 2 + 5;
1568 for (
int row = 0; row < body_rows; ++row) {
1569 DrawOpenChestPlatformSegment(ctx, row, 0, fill_width);
1571 DrawOpenChestPlatformSegment(ctx, body_rows, 1, fill_width);
1572 DrawOpenChestPlatformSegment(ctx, body_rows + 1, 2,
1580 if (ctx.
tiles.size() < 3)
1583 for (
int x = 0; x < width; ++x) {
1584 size_t tile_idx = (x == 0) ? 0 : ((x == width - 1) ? 2 : 1);
1594 if (ctx.
tiles.empty())
1597 for (
int y = 0; y < height; ++y) {
1616 .draws_to_both_bgs =
false,
1627 .draws_to_both_bgs =
false,
1635 .name =
"DoorSwitcherer",
1637 .draws_to_both_bgs =
false,
1646 .name =
"SomariaLine",
1648 .draws_to_both_bgs =
false,
1657 .name =
"WaterFace",
1659 .draws_to_both_bgs =
false,
1672 .name =
"4x4BlocksIn4x4SuperSquare",
1674 .draws_to_both_bgs =
false,
1682 .name =
"3x3FloorIn4x4SuperSquare",
1684 .draws_to_both_bgs =
false,
1692 .name =
"4x4FloorIn4x4SuperSquare",
1694 .draws_to_both_bgs =
false,
1702 .name =
"4x4FloorOneIn4x4SuperSquare",
1704 .draws_to_both_bgs =
false,
1712 .name =
"4x4FloorTwoIn4x4SuperSquare",
1714 .draws_to_both_bgs =
false,
1722 .name =
"BigHole4x4_1to16",
1724 .draws_to_both_bgs =
false,
1732 .name =
"Spike2x2In4x4SuperSquare",
1734 .draws_to_both_bgs =
false,
1742 .name =
"TableRock4x4_1to16",
1744 .draws_to_both_bgs =
false,
1752 .name =
"WaterOverlay8x8_1to16",
1754 .draws_to_both_bgs =
false,
1763 .name =
"InterRoomFatStairsUp",
1765 .draws_to_both_bgs =
false,
1774 .name =
"InterRoomFatStairsDownA",
1776 .draws_to_both_bgs =
false,
1785 .name =
"InterRoomFatStairsDownB",
1787 .draws_to_both_bgs =
false,
1796 .name =
"AutoStairs",
1798 .draws_to_both_bgs =
false,
1807 .name =
"StraightInterRoomStairs",
1809 .draws_to_both_bgs =
false,
1819 .name =
"SpiralStairsGoingUpUpper",
1822 .draws_to_both_bgs =
false,
1831 .name =
"SpiralStairsGoingDownUpper",
1834 .draws_to_both_bgs =
false,
1843 .name =
"SpiralStairsGoingUpLower",
1846 .draws_to_both_bgs =
false,
1855 .name =
"SpiralStairsGoingDownLower",
1858 .draws_to_both_bgs =
false,
1867 .name =
"WaterHopStairsA",
1868 .function = DrawWaterHopStairsA,
1869 .draws_to_both_bgs =
false,
1878 .name =
"WaterHopStairsB",
1879 .function = DrawWaterHopStairsB,
1880 .draws_to_both_bgs =
true,
1889 .name =
"DamFloodGate",
1890 .function = DrawDamFloodGate,
1891 .draws_to_both_bgs =
false,
1901 .name =
"BigKeyLock",
1903 .draws_to_both_bgs =
false,
1912 .name =
"BombableFloor",
1914 .draws_to_both_bgs =
false,
1924 .name =
"EmptyWaterFace",
1926 .draws_to_both_bgs =
false,
1935 .name =
"SpittingWaterFace",
1937 .draws_to_both_bgs =
false,
1946 .name =
"DrenchingWaterFace",
1948 .draws_to_both_bgs =
false,
1959 .name =
"PrisonCell",
1961 .draws_to_both_bgs =
false,
1972 .draws_to_both_bgs =
false,
1980 .name =
"Rightwards3x6",
1982 .draws_to_both_bgs =
false,
1990 .name =
"Utility6x3",
1992 .draws_to_both_bgs =
false,
2000 .name =
"Utility3x5",
2002 .draws_to_both_bgs =
false,
2010 .name =
"VerticalTurtleRockPipe",
2012 .draws_to_both_bgs =
false,
2020 .name =
"HorizontalTurtleRockPipe",
2022 .draws_to_both_bgs =
false,
2030 .name =
"LightBeam",
2032 .draws_to_both_bgs =
false,
2041 .name =
"BigLightBeam",
2043 .draws_to_both_bgs =
false,
2052 .name =
"FloorLight",
2054 .draws_to_both_bgs =
false,
2063 .name =
"BossShell4x4",
2065 .draws_to_both_bgs =
false,
2073 .name =
"VitreousGooDamage",
2075 .draws_to_both_bgs =
false,
2084 .name =
"SolidWallDecor3x4",
2086 .draws_to_both_bgs =
false,
2094 .name =
"ArcheryGameTargetDoor",
2096 .draws_to_both_bgs =
false,
2104 .name =
"GanonTriforceFloorDecor",
2106 .draws_to_both_bgs =
false,
2114 .name =
"Single2x2",
2116 .draws_to_both_bgs =
false,
2125 .name =
"Single4x4",
2127 .draws_to_both_bgs =
false,
2136 .name =
"Single4x3",
2138 .draws_to_both_bgs =
false,
2147 .name =
"RupeeFloor",
2149 .draws_to_both_bgs =
false,
2158 .name =
"Actual4x4",
2160 .draws_to_both_bgs =
false,
2169 .name =
"BigWallDecor",
2171 .draws_to_both_bgs =
false,
2180 .name =
"TableBowl",
2182 .draws_to_both_bgs =
false,
2191 .name =
"SmithyFurnace",
2193 .draws_to_both_bgs =
false,
2202 .name =
"BigGrayRock",
2204 .draws_to_both_bgs =
false,
2213 .name =
"AgahnimsAltar",
2215 .draws_to_both_bgs =
false,
2224 .name =
"FortuneTellerRoom",
2226 .draws_to_both_bgs =
false,
2235 .name =
"MagicBatAltar",
2237 .draws_to_both_bgs =
false,
2246 .name =
"Waterfall47",
2248 .draws_to_both_bgs =
false,
2256 .name =
"Waterfall48",
2258 .draws_to_both_bgs =
false,
2267 .name =
"ClosedChestPlatform",
2269 .draws_to_both_bgs =
false,
2278 .name =
"MovingWallWest",
2280 .draws_to_both_bgs =
false,
2289 .name =
"MovingWallEast",
2291 .draws_to_both_bgs =
false,
2300 .name =
"OpenChestPlatform",
2302 .draws_to_both_bgs =
false,
2312 .name =
"DownwardsHasEdge1x1_1to16_plus23",
2316 int size = ctx.object.size_ & 0x0F;
2317 int count = size + 21;
2318 if (ctx.tiles.size() < 3)
2321 int tile_y = ctx.object.y_;
2325 tile_y, {0x00E3})) {
2331 for (
int s = 0; s < count; s++) {
2340 .draws_to_both_bgs =
false,
2352 .name =
"CustomObject",
2354 .draws_to_both_bgs =
false,
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::StatusOr< uint8_t > ReadByte(int offset) const
absl::StatusOr< uint16_t > ReadWord(int offset) const
SNES 16-bit tile metadata container.
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< CustomObject > > GetObjectInternal(int object_id, int subtype)
absl::StatusOr< std::shared_ptr< const CustomObject > > GetObject(int object_id, int subtype) const
bool CustomObjectsEnabled() const
static DrawRoutineRegistry & Get()
virtual bool IsWaterFaceActive(int room_id) const
virtual bool IsFloorBombable(int room_id) const =0
virtual bool IsWallMoved(int room_id) const =0
virtual bool IsChestOpen(int room_id, int chest_index) const =0
virtual bool IsDoorSwitchActive(int room_id) const =0
virtual bool IsDamFloodgateOpen(int room_id) const
virtual bool IsRupeeFloorCleared(int room_id) const =0
#define LOG_DEBUG(category, format,...)
TileInfo WordToTileInfo(uint16_t word)
constexpr int kGanonTriforceFloorDecor
constexpr int kWaterHopStairsA
constexpr int kArcheryGameTargetDoor
constexpr int kAgahnimsAltar
constexpr int kDamFloodGate
constexpr int kRupeeFloor
constexpr int kWaterfall47
constexpr int kClosedChestPlatform
constexpr int kHorizontalTurtleRockPipe
constexpr int kVitreousGooDamage
constexpr int kBigLightBeam
constexpr int kSolidWallDecor3x4
constexpr int kVerticalTurtleRockPipe
constexpr int kMagicBatAltar
constexpr int kWaterHopStairsB
constexpr int kMovingWallWest
constexpr int kMovingWallEast
constexpr int kBigGrayRock
constexpr int kSmithyFurnace
constexpr int kFloorLight
constexpr int kUtility6x3
constexpr int kCustomObject
constexpr int kOpenChestPlatform
constexpr int kUtility3x5
constexpr int kFortuneTellerRoom
constexpr int kBigWallDecor
constexpr int kRightwards3x6
constexpr int kDownwardsHasEdge1x1_1to16_plus23
constexpr int kBossShell4x4
constexpr int kWaterfall48
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 DrawNx4(const DrawContext &ctx, int columns, size_t start_index)
std::array< gfx::TileInfo, 3 > LoadMovingWallFillTiles(const DrawContext &ctx)
void DrawMovingWallFill(const DrawContext &ctx, int start_x, int column_count, int height)
int LightBeamFloorRoomId(const Rom *rom)
void DrawRowMajor(gfx::BackgroundBuffer &bg, int base_x, int base_y, int w, int h, std::span< const gfx::TileInfo > tiles, size_t start_index=0)
void DrawOpenChestPlatformSegment(const DrawContext &ctx, int dy, size_t start_index, int fill_width)
void Draw4x4ColumnMajor(const DrawContext &ctx, int x_offset, int y_offset, size_t start_index)
constexpr int kWaterFaceWidthTiles
void DrawWaterHopStairsA(const DrawContext &ctx)
void DrawWaterFaceRows(const DrawContext &ctx, int row_count, int tile_offset)
void Draw1x3NRightwards(const DrawContext &ctx, int columns, size_t start_index, int y_offset=0)
int BombableFloorRoomId(const Rom *rom)
void DrawPlatform3x2(const DrawContext &ctx, int dx, int dy, size_t start_index)
const gfx::TileInfo & TileAtWrapped(std::span< const gfx::TileInfo > tiles, size_t index)
void Draw4x4ColumnMajorTo(gfx::BackgroundBuffer &bg, const RoomObject &object, std::span< const gfx::TileInfo > tiles)
void Draw1x5Column(const DrawContext &ctx, int x_offset, size_t start_index)
void DrawMany32x32Block(gfx::BackgroundBuffer &bg, int base_x, int base_y, std::span< const gfx::TileInfo > tiles)
void DrawFloorLightGrid(const DrawContext &ctx)
void DrawPlatform2x2(const DrawContext &ctx, int dx, int dy, size_t start_index)
constexpr int kMovingWallFillDataOffset
void DrawMovingWallPlatform(const DrawContext &ctx, int direction)
void WritePlatformTile(const DrawContext &ctx, int dx, int dy, size_t index)
void DrawDamFloodGate(const DrawContext &ctx)
std::array< gfx::TileInfo, 16 > LoadRoomDrawObjectTiles(const DrawContext &ctx, int object_data_offset, int count)
void DrawColumnMajor(gfx::BackgroundBuffer &bg, int base_x, int base_y, int w, int h, std::span< const gfx::TileInfo > tiles, size_t start_index=0)
void DrawPlatform1x3Rightwards(const DrawContext &ctx, int dx, int dy, size_t start_index, int columns)
void DrawDamWaterFlooring(const DrawContext &ctx, gfx::BackgroundBuffer &bg2)
void DrawWaterHopStairsB(const DrawContext &ctx)
constexpr std::array< uint16_t, 3 > kMovingWallFallbackFillWords
void DrawTableRock4x4_1to16(const DrawContext &ctx)
Draw 4x4 table rock pattern.
void DrawInterRoomFatStairsDownA(const DrawContext &ctx)
Draw inter-room fat stairs going down A (Type 2 object 0x12E)
void DrawSingle2x2(const DrawContext &ctx)
Draw a single 2x2 block (column-major order)
void DrawWaterfall47(const DrawContext &ctx)
Draw waterfall object 0x47 pattern.
void DrawHorizontalTurtleRockPipe(const DrawContext &ctx)
Draw horizontal Turtle Rock pipe (6x4)
void DrawAutoStairs(const DrawContext &ctx)
Draw auto stairs (Type 2/3 objects 0x130-0x133, 0x21B-0x21D, 0x233)
void DrawSpittingWaterFace(const DrawContext &ctx)
Draw spitting water face (Type 3 object 0x201)
void DrawAgahnimsAltar(const DrawContext &ctx)
Draw the fixed 14x14 Agahnim altar with mirrored right half.
void DrawLargeCanvasObject(const DrawContext &ctx, int width, int height)
Draw large canvas object with arbitrary dimensions.
void DrawMovingWallEast(const DrawContext &ctx)
Draw the east-moving wall (Type 1 object 0xCE)
void DrawBigGrayRock(const DrawContext &ctx)
Draw the fixed 4x4 big gray rock as two column-major 4x2 bands.
void DrawChest(const DrawContext &ctx, int chest_index)
Draw a small chest with open/closed state support.
void DrawMovingWallWest(const DrawContext &ctx)
Draw the west-moving wall (Type 1 object 0xCD)
void DrawBed4x5(const DrawContext &ctx)
Draw a 4x5 bed pattern (row-major)
void DrawSingle4x4(const DrawContext &ctx)
Draw a single 4x4 block (column-major order)
void DrawUtility3x5(const DrawContext &ctx)
Draw utility 3x5 pattern (special row pattern)
void DrawDoorSwitcherer(const DrawContext &ctx)
Draw door switcher object with state-based graphics.
void Draw4x4BlocksIn4x4SuperSquare(const DrawContext &ctx)
Draw 4x4 solid blocks in a super square grid.
void DrawBigHole4x4_1to16(const DrawContext &ctx)
Draw 4x4 big hole pattern.
void DrawFloorLight(const DrawContext &ctx)
Draw unconditional floor light (8x8 footprint)
void Draw4x4FloorTwoIn4x4SuperSquare(const DrawContext &ctx)
Draw two 4x4 floor pattern variant.
void DrawSolidWallDecor3x4(const DrawContext &ctx)
Draw solid wall decor 3x4.
void DrawLightBeamOnFloor(const DrawContext &ctx)
Draw floor light beam composed of three 4x4 blocks.
void DrawWaterFace(const DrawContext &ctx)
Draw a generic 2x2 water-face helper pattern.
void DrawTableBowl(const DrawContext &ctx)
Draw a fixed 4x2 table bowl in row-major order.
void DrawChestPlatformVerticalWall(const DrawContext &ctx)
Draw chest platform vertical wall section.
void DrawClosedChestPlatform(const DrawContext &ctx)
Draw closed chest platform (Type 1 object 0xC1)
void DrawSmithyFurnace(const DrawContext &ctx)
Draw the fixed 6x8 smithy furnace in row-major order.
void DrawVitreousGooDamage(const DrawContext &ctx)
Draw the fixed 5x2 grid of repeated 4x4 Vitreous goo stamps.
void DrawSpike2x2In4x4SuperSquare(const DrawContext &ctx)
Draw 2x2 spike pattern in super square units.
void DrawSingle4x3(const DrawContext &ctx)
Draw a single 4x3 block (column-major order)
void DrawBigLightBeamOnFloor(const DrawContext &ctx)
Draw state-gated big floor light beam active variant (8x8 footprint)
void DrawOpenChestPlatform(const DrawContext &ctx)
Draw open chest platform (Type 1 object 0xDC)
void DrawSpiralStairs(const DrawContext &ctx, bool going_up, bool is_upper)
Draw spiral stairs (Type 2 objects 0x138-0x13B)
void DrawFortuneTellerRoom(const DrawContext &ctx)
Draw the fixed 14x14 fortune teller room on BG1.
void DrawSomariaLine(const DrawContext &ctx)
Draw one Somaria path tile at the object anchor.
void DrawWaterfall48(const DrawContext &ctx)
Draw waterfall object 0x48 pattern.
void DrawVerticalTurtleRockPipe(const DrawContext &ctx)
Draw vertical Turtle Rock pipe (two stacked 4x3 sections)
void DrawBombableFloor(const DrawContext &ctx)
Draw bombable floor (Type 3 object 0xFC7 / ASM object 0x247)
void DrawDrenchingWaterFace(const DrawContext &ctx)
Draw drenching water face (Type 3 object 0x202)
void DrawEmptyWaterFace(const DrawContext &ctx)
Draw empty water face (Type 3 object 0x200)
void DrawBossShell4x4(const DrawContext &ctx)
Draw boss shell 4x4.
void CustomDraw(const DrawContext &ctx)
Custom draw routine for special objects.
void DrawBigWallDecor(const DrawContext &ctx)
Draw fixed 8x3 big wall decor in column-major order.
void Draw4x4FloorIn4x4SuperSquare(const DrawContext &ctx)
Draw 4x4 floor pattern in super square units.
void DrawInterRoomFatStairsUp(const DrawContext &ctx)
Draw inter-room fat stairs going up (Type 2 object 0x12D)
void Draw3x3FloorIn4x4SuperSquare(const DrawContext &ctx)
Draw 3x3 floor pattern in super square units.
void DrawArcheryGameTargetDoor(const DrawContext &ctx)
Draw archery game target door (two 3x3 sections)
void DrawStraightInterRoomStairs(const DrawContext &ctx)
Draw straight inter-room stairs (Type 3 objects 0x21E-0x229)
void DrawGanonTriforceFloorDecor(const DrawContext &ctx)
Draw Ganon triforce floor decor (three 4x4 sections)
void RegisterSpecialRoutines(std::vector< DrawRoutineInfo > ®istry)
Register all special/miscellaneous draw routines to the registry.
void DrawInterRoomFatStairsDownB(const DrawContext &ctx)
Draw inter-room fat stairs going down B (Type 2 object 0x12F)
void DrawUtility6x3(const DrawContext &ctx)
Draw utility 6x3 pattern via RoomDraw_1x3N_rightwards.
void DrawRightwards3x6(const DrawContext &ctx)
Draw a 6x3 pattern via RoomDraw_1x3N_rightwards semantics.
void DrawMagicBatAltar(const DrawContext &ctx)
Draw the fixed 8x7 Magic Bat altar in column-major order.
void DrawChestPlatformHorizontalWall(const DrawContext &ctx)
Draw chest platform horizontal wall section.
void DrawWaterOverlay8x8_1to16(const DrawContext &ctx)
Draw water overlay 8x8 pattern.
void DrawRupeeFloor(const DrawContext &ctx)
Draw the blue rupee floor pattern (5x8 bounds with gaps)
void Draw4x4FloorOneIn4x4SuperSquare(const DrawContext &ctx)
Draw single 4x4 floor pattern variant.
void DrawNothing(const DrawContext &ctx)
Draw nothing - represents invisible logic objects or placeholders.
void DrawActual4x4(const DrawContext &ctx)
Draw an actual 4x4 tile8 pattern (column-major order)
void DrawBigKeyLock(const DrawContext &ctx)
Draw a closed big key lock (Yaze 0xF98 / ASM object 0x218)
void DrawPrisonCell(const DrawContext &ctx)
Draw prison cell with bars (Type 3 objects 0x20D, 0x217)
constexpr int DirectionForSize(int size)
constexpr int ObjectCountForSize(int size)
constexpr bool IsFullBothAutoStairsObject(int object_id)
constexpr bool IsSouthMixedStraightInterroomObject(int object_id)
constexpr bool IsMixedStraightInterroomObject(int object_id)
constexpr bool IsFixedBg1StraightInterroomObject(int object_id)
uint16_t CustomObjectRuntimeTileWord(int object_id, uint16_t source_word)
constexpr int kRoomObjectTileAddress
Context passed to draw routines containing all necessary state.
std::span< const gfx::TileInfo > tiles
gfx::BackgroundBuffer & target_bg
const CustomObjectRenderSnapshot * custom_objects
const RoomObject & object
gfx::BackgroundBuffer * secondary_bg
const DungeonState * state
Metadata about a draw routine.