607 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
613 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
619 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
625 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
631 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
637 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
643 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
649 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
655 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
661 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
667 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
673 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
679 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
685 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
691 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
697 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
703 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
709 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
715 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
721 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
728 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
734 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
740 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
746 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
752 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
758 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
764 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
770 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
776 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
782 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
788 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
794 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
800 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
806 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
812 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
818 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
824 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
830 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
836 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
842 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
848 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
854 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
860 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
866 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
872 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
878 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
884 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
890 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
896 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
902 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
908 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
914 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
920 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
926 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
932 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
938 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
949 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
956 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
964 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
971 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
978 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
985 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
992 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
999 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1006 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1017 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1024 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1031 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1038 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1045 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1052 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1059 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1066 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1073 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1080 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1091 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1098 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1105 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1112 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1123 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1130 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1137 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1144 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1155 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1162 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1169 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1176 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1183 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1190 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1197 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1204 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1211 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1218 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1225 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1232 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1239 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1246 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1254 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1261 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1269 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1277 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1285 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1293 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1295 obj, bg, tiles, state);
1302 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1310 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1318 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1326 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1334 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1342 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1344 obj, bg, tiles, state);
1350 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1352 obj, bg, tiles, state);
1358 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1366 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1374 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1384 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1394 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1395 if (obj.
id_ == 0xFB1) {
1396 self->DrawBigChest(obj, bg, tiles, state);
1408 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1418 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1423 auto ensure_index = [
this](
size_t index) {
1427 std::span<const gfx::TileInfo> tiles,
1439 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1447 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1454 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1462 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1470 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1478 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1487 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1495 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1503 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1511 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1519 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1527 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1535 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1543 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1551 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1559 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1567 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1578 std::span<const gfx::TileInfo> tiles,
1605 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: idx=%d type=%d dir=%d pos=%d",
1606 door_index,
static_cast<int>(door.
type),
1610 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: SKIPPED - rom=%p loaded=%d gfx=%p",
1616 auto& bitmap = bg1.
bitmap();
1617 if (!bitmap.is_active() || bitmap.width() == 0) {
1619 "DrawDoor: SKIPPED - bitmap not active or zero width");
1633 int door_height = dims.height_tiles;
1635 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: tile_pos=(%d,%d) dims=%dx%d", tile_x,
1636 tile_y, door_width, door_height);
1638 constexpr int kRoomDrawObjectDataBase = 0x1B52;
1639 constexpr int kDoorwayReplacementDoorGfxBase = 0x1A02;
1640 constexpr int kExplodingWallTilemapPositionBase = 0x19DE;
1641 constexpr int kExplodingWallOpenReplacementType = 0x54;
1642 constexpr int kNorthCurtainClosedOffset = 0x078A;
1643 constexpr int kFancyDungeonExitObjectOffset = 0x2656;
1644 constexpr int kCaveExitLightObjectOffset = 0x26F6;
1645 const auto& rom_data =
rom_->
data();
1647 auto resolve_effective_door_type = [&]() -> uint16_t {
1648 const auto stored_type =
static_cast<uint16_t
>(door.
type);
1649 if (!is_door_open) {
1656 const bool is_controlled_shutter =
1666 const int replacement_addr =
1667 kDoorwayReplacementDoorGfxBase +
static_cast<int>(door.
type);
1668 if (replacement_addr < 0 ||
1669 replacement_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1673 return static_cast<uint16_t
>(rom_data[replacement_addr] |
1674 (rom_data[replacement_addr + 1] << 8));
1677 const uint16_t effective_door_type = resolve_effective_door_type();
1680 int tile_y, uint16_t tile_word) {
1681 auto& bitmap = target.
bitmap();
1684 const int bitmap_width = bitmap.width();
1686 const int pixel_x = tile_x * 8;
1687 const int pixel_y = tile_y * 8;
1689 target.
SetTileAt(tile_x, tile_y, tile_word);
1694 const uint8_t priority = tile_info.over_ ? 1 : 0;
1695 const auto& bitmap_data = bitmap.vector();
1696 for (
int py = 0; py < 8; py++) {
1697 const int dest_y = pixel_y + py;
1698 if (dest_y < 0 || dest_y >= bitmap.height()) {
1701 for (
int px = 0; px < 8; px++) {
1702 const int dest_x = pixel_x + px;
1703 if (dest_x < 0 || dest_x >= bitmap_width) {
1706 const int dest_index = dest_y * bitmap_width + dest_x;
1707 if (dest_index >= 0 &&
1708 dest_index <
static_cast<int>(coverage_buffer.size())) {
1709 coverage_buffer[dest_index] = 1;
1711 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1712 bitmap_data[dest_index] != 255) {
1713 priority_buffer[dest_index] = priority;
1720 int tile_y,
int tile_data_addr,
int tile_idx,
1721 uint16_t word_mask = 0) ->
bool {
1722 const int addr = tile_data_addr + (tile_idx * 2);
1723 if (addr < 0 || addr + 1 >=
static_cast<int>(
rom_->
size())) {
1727 const uint16_t tile_word =
static_cast<uint16_t
>(
1728 (rom_data[addr] | (rom_data[addr + 1] << 8)) | word_mask);
1729 draw_tile_word(target, tile_x, tile_y, tile_word);
1734 int start_tile_x,
int start_tile_y,
1735 int width,
int height,
int tile_data_addr) {
1738 for (
int dx = 0; dx < width; dx++) {
1739 for (
int dy = 0; dy < height; dy++) {
1740 (void)draw_object_data_tile(target, start_tile_x + dx,
1741 start_tile_y + dy, tile_data_addr,
1747 auto draw_from_object_data_row_major =
1749 int width,
int height,
int tile_data_addr) {
1751 for (
int dy = 0; dy < height; ++dy) {
1752 for (
int dx = 0; dx < width; ++dx) {
1753 (void)draw_object_data_tile(target, start_tile_x + dx,
1754 start_tile_y + dy, tile_data_addr,
1761 int start_tile_y,
int width,
int height,
1762 uint16_t tile_word) {
1763 auto& bitmap = target.
bitmap();
1766 const int bitmap_width = bitmap.width();
1769 for (
int dx = 0; dx < width; dx++) {
1770 for (
int dy = 0; dy < height; dy++) {
1771 const int pixel_x = (start_tile_x + dx) * 8;
1772 const int pixel_y = (start_tile_y + dy) * 8;
1778 const uint8_t priority = tile_info.over_ ? 1 : 0;
1779 const auto& bitmap_data = bitmap.vector();
1780 for (
int py = 0; py < 8; py++) {
1781 const int dest_y = pixel_y + py;
1782 if (dest_y < 0 || dest_y >= bitmap.height()) {
1785 for (
int px = 0; px < 8; px++) {
1786 const int dest_x = pixel_x + px;
1787 if (dest_x < 0 || dest_x >= bitmap_width) {
1790 const int dest_index = dest_y * bitmap_width + dest_x;
1791 if (dest_index >= 0 &&
1792 dest_index <
static_cast<int>(coverage_buffer.size())) {
1793 coverage_buffer[dest_index] = 1;
1795 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1796 bitmap_data[dest_index] != 255) {
1797 priority_buffer[dest_index] = priority;
1805 auto tilemap_offset_to_tile_coords = [](uint16_t offset) {
1806 return std::pair<int, int>{
static_cast<int>((offset % 0x80) / 2),
1807 static_cast<int>(offset / 0x80) - 4};
1809 const int position_index = std::min<int>(door.
position & 0x0F, 11);
1811 enum class DoorPrioritySpan {
1820 auto promote_priority_rect_on_layer =
1823 int start_tile_y,
int width_tiles,
int height_tiles) {
1825 for (
int y = start_tile_y * 8; y < (start_tile_y + height_tiles) * 8;
1827 for (
int x = start_tile_x * 8; x < (start_tile_x + width_tiles) * 8;
1837 promote_target(object_buffer);
1838 if (layout_buffer !=
nullptr && layout_buffer != &object_buffer) {
1839 promote_target(*layout_buffer);
1843 auto promote_upper_priority_rect = [&](
int start_tile_x,
int start_tile_y,
1844 int width_tiles,
int height_tiles) {
1845 promote_priority_rect_on_layer(bg1, layout_bg1, start_tile_x, start_tile_y,
1846 width_tiles, height_tiles);
1849 auto promote_lower_priority_rect = [&](
int start_tile_x,
int start_tile_y,
1850 int width_tiles,
int height_tiles) {
1851 promote_priority_rect_on_layer(bg2, layout_bg2, start_tile_x, start_tile_y,
1852 width_tiles, height_tiles);
1855 auto promote_door_priority = [&](
DoorDirection render_direction,
1856 int render_tile_x,
int render_tile_y,
1857 DoorPrioritySpan span) {
1858 constexpr int kSectionSize = 32;
1859 const auto section_start = [](
int coordinate) {
1860 return (coordinate / kSectionSize) * kSectionSize;
1862 const auto section_end = [&](
int coordinate) {
1863 return section_start(coordinate) + kSectionSize;
1866 int start_x = render_tile_x;
1867 int start_y = render_tile_y;
1872 case DoorPrioritySpan::kNormalLower:
1873 switch (render_direction) {
1875 start_y = section_start(render_tile_y);
1882 start_y = render_tile_y + 3;
1887 start_x = section_start(render_tile_x);
1894 start_x = render_tile_x + 3;
1901 case DoorPrioritySpan::kLowerLayerOnly:
1902 switch (render_direction) {
1904 start_y = section_start(render_tile_y);
1906 height = render_tile_y - start_y + 1;
1909 start_y = render_tile_y + 1;
1911 height = section_end(render_tile_y) - start_y;
1914 start_x = section_start(render_tile_x);
1915 width = render_tile_x - start_x + 1;
1919 start_x = render_tile_x + 1;
1920 width = section_end(render_tile_x) - start_x;
1926 case DoorPrioritySpan::kHighRange:
1927 switch (render_direction) {
1929 start_y = section_start(render_tile_y);
1931 height = render_tile_y - start_y;
1934 start_y = render_tile_y + 3;
1936 height = section_end(render_tile_y) - start_y;
1939 start_x = section_start(render_tile_x);
1940 width = render_tile_x - start_x;
1946 start_x = render_tile_x + 3;
1947 width = section_end(render_tile_x) - start_x;
1954 if (width > 0 && height > 0) {
1955 promote_upper_priority_rect(start_x, start_y, width, height);
1968 if (is_nonvisual_marker) {
1975 promote_door_priority(door.
direction, tile_x, tile_y,
1976 DoorPrioritySpan::kLowerLayerOnly);
1984 const auto [raw_tile_x, raw_tile_y] =
1987 const int fancy_data_addr =
1988 kRoomDrawObjectDataBase + kFancyDungeonExitObjectOffset;
1989 const int cave_light_data_addr =
1990 kRoomDrawObjectDataBase + kCaveExitLightObjectOffset;
1991 const auto has_object_words = [&](
int address,
int word_count) {
1992 return address >= 0 &&
1993 address + word_count * 2 <=
static_cast<int>(
rom_->
size());
1996 switch (door.
type) {
1998 if (!has_object_words(fancy_data_addr, 80)) {
2004 draw_from_object_data_row_major(bg1, raw_tile_x - 3, raw_tile_y - 4,
2010 if (!has_object_words(fancy_data_addr, 80)) {
2016 draw_from_object_data_row_major(bg2, raw_tile_x - 3, raw_tile_y - 4,
2021 for (
int dx = 0; dx < 10; ++dx) {
2022 (void)draw_object_data_tile(
2023 bg1, raw_tile_x - 3 + dx, raw_tile_y + 3, fancy_data_addr,
2029 if (!has_object_words(cave_light_data_addr, 16)) {
2034 promote_lower_priority_rect(raw_tile_x, raw_tile_y + 4,
2036 draw_from_object_data(bg2, raw_tile_x, raw_tile_y, 4,
2037 4, cave_light_data_addr);
2038 for (
int dx = 0; dx < 4; ++dx) {
2039 (void)draw_object_data_tile(
2040 bg1, raw_tile_x + dx, raw_tile_y + 3, cave_light_data_addr,
2041 dx * 4 + 3, 0x2000);
2046 if (!has_object_words(cave_light_data_addr, 16)) {
2051 draw_from_object_data(bg1, raw_tile_x, raw_tile_y, 4,
2052 4, cave_light_data_addr);
2056 if (!has_object_words(cave_light_data_addr, 16)) {
2061 promote_upper_priority_rect(raw_tile_x, raw_tile_y + 4,
2063 draw_from_object_data(bg1, raw_tile_x, raw_tile_y, 4,
2064 4, cave_light_data_addr);
2072 auto resolve_render_type = [](
DoorDirection render_direction,
2073 uint16_t render_type) -> uint16_t {
2074 switch (render_type) {
2106 auto resolve_table_door_data = [&](
DoorDirection render_direction,
2107 uint16_t render_type,
2108 int* tile_data_addr) ->
bool {
2109 int offset_table_addr = 0;
2110 switch (render_direction) {
2125 const uint16_t resolved_type =
2126 resolve_render_type(render_direction, render_type);
2130 const int table_entry_addr = offset_table_addr + resolved_type;
2131 if (table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2135 const uint16_t tile_offset =
2136 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2137 *tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2139 const int data_size = dims.width_tiles * dims.height_tiles * 2;
2140 if (*tile_data_addr < 0 ||
2141 *tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2150 int start_tile_y, uint16_t render_type) ->
bool {
2151 int tile_data_addr = 0;
2152 if (!resolve_table_door_data(render_direction, render_type,
2158 draw_from_object_data(target, start_tile_x, start_tile_y,
2159 render_dims.width_tiles, render_dims.height_tiles,
2164 auto draw_high_range_table_door = [&](
DoorDirection render_direction,
2165 int start_tile_x,
int start_tile_y,
2166 uint16_t render_type) ->
bool {
2167 int tile_data_addr = 0;
2168 if (!resolve_table_door_data(render_direction, render_type,
2175 for (
int dx = 0; dx < dims.width_tiles; ++dx) {
2176 for (
int dy = 0; dy < dims.height_tiles; ++dy) {
2179 bool writes_upper =
false;
2180 switch (render_direction) {
2182 writes_upper = dy == 0;
2185 writes_upper = dy == dims.height_tiles - 1;
2188 writes_upper = dx == 0;
2191 writes_upper = dx == dims.width_tiles - 1;
2195 if (!draw_object_data_tile(target, start_tile_x + dx, start_tile_y + dy,
2196 tile_data_addr, tile_idx++)) {
2204 auto draw_ranged_door = [&](
DoorDirection render_direction,
int start_tile_x,
2205 int start_tile_y,
DoorType original_type,
2206 uint16_t table_type) ->
bool {
2210 promote_door_priority(render_direction, start_tile_x, start_tile_y,
2211 DoorPrioritySpan::kNormalLower);
2217 const int render_type_value =
static_cast<int>(original_type);
2218 const bool is_high_range =
2219 render_type_value >= 0x40 && render_type_value <= 0x66;
2220 if (is_high_range) {
2221 const bool drew = draw_high_range_table_door(
2222 render_direction, start_tile_x, start_tile_y, table_type);
2227 promote_door_priority(render_direction, start_tile_x, start_tile_y,
2228 DoorPrioritySpan::kHighRange);
2232 return draw_table_door(bg1, render_direction, start_tile_x, start_tile_y,
2241 "DrawDoor: closed exploding wall intentionally draws nothing");
2248 const int tile_data_addr =
2249 kRoomDrawObjectDataBase + kNorthCurtainClosedOffset;
2250 const int data_size = 16 * 2;
2251 if (tile_data_addr < 0 ||
2252 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2257 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
2264 const int table_entry_addr =
2265 kDoorGfxUp +
static_cast<int>(effective_door_type);
2266 if (table_entry_addr < 0 ||
2267 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2273 const uint16_t tile_offset =
2274 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2275 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2276 const int data_size = 16 * 2;
2277 if (tile_data_addr < 0 ||
2278 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2284 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
2291 const int position_index = std::min<int>(door.
position & 0x0F, 5);
2292 const int tilemap_entry_addr =
2293 kExplodingWallTilemapPositionBase + (position_index * 2);
2294 if (tilemap_entry_addr < 0 ||
2295 tilemap_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2301 const uint16_t tilemap_offset =
2302 rom_data[tilemap_entry_addr] | (rom_data[tilemap_entry_addr + 1] << 8);
2303 const auto explosion_tile_coords =
2304 tilemap_offset_to_tile_coords(tilemap_offset);
2305 const int explosion_tile_x = explosion_tile_coords.first;
2306 const int explosion_tile_y = explosion_tile_coords.second;
2308 auto draw_exploding_wall_segment = [&](
int table_entry_addr,
2309 int segment_tile_y) ->
bool {
2310 if (table_entry_addr < 0 ||
2311 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2315 const uint16_t tile_offset =
2316 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2317 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2318 constexpr int kFillWordIndex = 12;
2319 const int min_data_size = (kFillWordIndex + 1) * 2;
2320 if (tile_data_addr < 0 ||
2321 tile_data_addr + min_data_size >
static_cast<int>(
rom_->
size())) {
2325 draw_from_object_data(bg1, explosion_tile_x, segment_tile_y,
2326 2, 6, tile_data_addr);
2327 const uint16_t fill_word =
2328 rom_data[tile_data_addr + (kFillWordIndex * 2)] |
2329 (rom_data[tile_data_addr + (kFillWordIndex * 2) + 1] << 8);
2330 draw_repeated_tile(bg1, explosion_tile_x + 2, segment_tile_y,
2335 const int south_table_entry_addr =
2337 const int north_table_entry_addr =
2338 kDoorGfxUp + kExplodingWallOpenReplacementType;
2339 if (!draw_exploding_wall_segment(south_table_entry_addr,
2340 explosion_tile_y) ||
2341 !draw_exploding_wall_segment(north_table_entry_addr,
2342 explosion_tile_y + 6)) {
2352 const bool is_north_key_stairs =
2358 if (is_north_key_stairs && is_door_open) {
2365 const bool is_north_lower_key_stairs =
2369 if (is_north_lower_key_stairs) {
2370 if (!draw_table_door(bg2, door.
direction, tile_x, tile_y,
2371 static_cast<uint16_t
>(door.
type))) {
2381 const bool uses_normal_ranged_writer =
2382 static_cast<int>(door.
type) <
2384 if (uses_normal_ranged_writer &&
2399 position_index >= 6 && !north_explicit_door) {
2407 const auto [counterpart_tile_x, counterpart_tile_y] =
2409 static_cast<uint8_t
>(position_index - 6), counterpart_direction);
2410 (void)draw_ranged_door(counterpart_direction, counterpart_tile_x,
2411 counterpart_tile_y, door.
type, effective_door_type);
2414 const bool drew_current = draw_ranged_door(door.
direction, tile_x, tile_y,
2415 door.
type, effective_door_type);
2416 if (!drew_current) {
2418 "DrawDoor: INVALID ADDRESS - falling back to indicator");
2425 "DrawDoor: type=%s effective=0x%04X dir=%s pos=%d at tile(%d,%d) "
2428 static_cast<unsigned int>(effective_door_type),
2430 door.
position, tile_x, tile_y, door_width, door_height);