713 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
719 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
725 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
731 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
737 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
743 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
749 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
755 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
761 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
767 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
773 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
779 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
785 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
791 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
797 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
803 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
809 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
815 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
821 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
827 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
834 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
840 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
846 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
852 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
858 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
864 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
870 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
876 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
882 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
888 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
894 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
900 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
906 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
912 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
918 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
924 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
930 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
936 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
942 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
948 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
954 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
960 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
966 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
972 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
978 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
984 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
990 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
996 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1002 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1008 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1014 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1020 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1026 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1032 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1038 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1044 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1055 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1062 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1070 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1077 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1084 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) {
1151 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1158 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1165 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1172 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1179 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1186 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) {
1229 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1236 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1243 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1250 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1261 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1268 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1275 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1282 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1289 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1296 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1303 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1310 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1317 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1324 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1331 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1338 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1345 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1352 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1360 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1367 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1375 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1383 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1391 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1399 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1401 obj, bg, tiles, state);
1408 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1416 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1424 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1432 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1440 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1448 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1450 obj, bg, tiles, state);
1456 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1458 obj, bg, tiles, state);
1464 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1472 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1480 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1490 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1500 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1501 if (obj.
id_ == 0xFB1) {
1502 self->DrawBigChest(obj, bg, tiles, state);
1514 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1524 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1529 auto ensure_index = [
this](
size_t index) {
1533 std::span<const gfx::TileInfo> tiles,
1545 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1553 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1560 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1568 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1576 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1584 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1593 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1601 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1609 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1617 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1625 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1633 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1641 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1649 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1657 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1665 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1673 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1684 std::span<const gfx::TileInfo> tiles,
1714 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: idx=%d type=%d dir=%d pos=%d",
1715 door_index,
static_cast<int>(door.
type),
1719 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: SKIPPED - rom=%p loaded=%d gfx=%p",
1725 auto& bitmap = bg1.
bitmap();
1726 if (!bitmap.is_active() || bitmap.width() == 0) {
1728 "DrawDoor: SKIPPED - bitmap not active or zero width");
1742 int door_height = dims.height_tiles;
1744 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: tile_pos=(%d,%d) dims=%dx%d", tile_x,
1745 tile_y, door_width, door_height);
1747 constexpr int kRoomDrawObjectDataBase = 0x1B52;
1748 constexpr int kDoorwayReplacementDoorGfxBase = 0x1A02;
1749 constexpr int kExplodingWallTilemapPositionBase = 0x19DE;
1750 constexpr int kExplodingWallOpenReplacementType = 0x54;
1751 constexpr int kNorthCurtainClosedOffset = 0x078A;
1752 constexpr int kFancyDungeonExitObjectOffset = 0x2656;
1753 constexpr int kCaveExitLightObjectOffset = 0x26F6;
1754 const auto& rom_data =
rom_->
data();
1761 auto resolve_effective_door_type = [&]() -> uint16_t {
1762 const auto stored_type =
static_cast<uint16_t
>(door.
type);
1763 if (!is_door_open && !always_open) {
1770 const bool is_controlled_shutter =
1773 if (is_controlled_shutter && state !=
nullptr &&
1781 const int replacement_addr =
1782 kDoorwayReplacementDoorGfxBase +
static_cast<int>(door.
type);
1783 if (replacement_addr < 0 ||
1784 replacement_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1788 return static_cast<uint16_t
>(rom_data[replacement_addr] |
1789 (rom_data[replacement_addr + 1] << 8));
1792 const uint16_t effective_door_type = resolve_effective_door_type();
1795 int tile_y, uint16_t tile_word) {
1796 auto& bitmap = target.
bitmap();
1799 const int bitmap_width = bitmap.width();
1801 const int pixel_x = tile_x * 8;
1802 const int pixel_y = tile_y * 8;
1804 target.
SetTileAt(tile_x, tile_y, tile_word);
1809 const uint8_t priority = tile_info.over_ ? 1 : 0;
1810 const auto& bitmap_data = bitmap.vector();
1811 for (
int py = 0; py < 8; py++) {
1812 const int dest_y = pixel_y + py;
1813 if (dest_y < 0 || dest_y >= bitmap.height()) {
1816 for (
int px = 0; px < 8; px++) {
1817 const int dest_x = pixel_x + px;
1818 if (dest_x < 0 || dest_x >= bitmap_width) {
1821 const int dest_index = dest_y * bitmap_width + dest_x;
1822 if (dest_index >= 0 &&
1823 dest_index <
static_cast<int>(coverage_buffer.size())) {
1824 coverage_buffer[dest_index] = 1;
1826 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1827 bitmap_data[dest_index] != 255) {
1828 priority_buffer[dest_index] = priority;
1835 int tile_y,
int tile_data_addr,
int tile_idx,
1836 uint16_t word_mask = 0) ->
bool {
1837 const int addr = tile_data_addr + (tile_idx * 2);
1838 if (addr < 0 || addr + 1 >=
static_cast<int>(
rom_->
size())) {
1842 const uint16_t tile_word =
static_cast<uint16_t
>(
1843 (rom_data[addr] | (rom_data[addr + 1] << 8)) | word_mask);
1844 draw_tile_word(target, tile_x, tile_y, tile_word);
1849 int start_tile_x,
int start_tile_y,
1850 int width,
int height,
int tile_data_addr) {
1853 for (
int dx = 0; dx < width; dx++) {
1854 for (
int dy = 0; dy < height; dy++) {
1855 (void)draw_object_data_tile(target, start_tile_x + dx,
1856 start_tile_y + dy, tile_data_addr,
1862 auto draw_from_object_data_row_major =
1864 int width,
int height,
int tile_data_addr) {
1866 for (
int dy = 0; dy < height; ++dy) {
1867 for (
int dx = 0; dx < width; ++dx) {
1868 (void)draw_object_data_tile(target, start_tile_x + dx,
1869 start_tile_y + dy, tile_data_addr,
1876 int start_tile_y,
int width,
int height,
1877 uint16_t tile_word) {
1878 auto& bitmap = target.
bitmap();
1881 const int bitmap_width = bitmap.width();
1884 for (
int dx = 0; dx < width; dx++) {
1885 for (
int dy = 0; dy < height; dy++) {
1886 const int pixel_x = (start_tile_x + dx) * 8;
1887 const int pixel_y = (start_tile_y + dy) * 8;
1892 target.
SetTileAt(start_tile_x + dx, start_tile_y + dy, tile_word);
1894 const uint8_t priority = tile_info.over_ ? 1 : 0;
1895 const auto& bitmap_data = bitmap.vector();
1896 for (
int py = 0; py < 8; py++) {
1897 const int dest_y = pixel_y + py;
1898 if (dest_y < 0 || dest_y >= bitmap.height()) {
1901 for (
int px = 0; px < 8; px++) {
1902 const int dest_x = pixel_x + px;
1903 if (dest_x < 0 || dest_x >= bitmap_width) {
1906 const int dest_index = dest_y * bitmap_width + dest_x;
1907 if (dest_index >= 0 &&
1908 dest_index <
static_cast<int>(coverage_buffer.size())) {
1909 coverage_buffer[dest_index] = 1;
1911 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1912 bitmap_data[dest_index] != 255) {
1913 priority_buffer[dest_index] = priority;
1921 auto tilemap_offset_to_tile_coords = [](uint16_t offset) {
1922 return std::pair<int, int>{
static_cast<int>((offset % 0x80) / 2),
1923 static_cast<int>(offset / 0x80)};
1925 const int position_index = std::min<int>(door.
position & 0x0F, 11);
1927 enum class DoorPrioritySpan {
1936 auto promote_priority_rect_on_layer =
1939 int start_tile_y,
int width_tiles,
int height_tiles) {
1941 for (
int tile_y = start_tile_y; tile_y < start_tile_y + height_tiles;
1943 for (
int tile_x = start_tile_x; tile_x < start_tile_x + width_tiles;
1945 PromoteTileWordPriority(target, tile_x, tile_y);
1948 for (
int y = start_tile_y * 8; y < (start_tile_y + height_tiles) * 8;
1950 for (
int x = start_tile_x * 8; x < (start_tile_x + width_tiles) * 8;
1960 promote_target(object_buffer);
1961 if (layout_buffer !=
nullptr && layout_buffer != &object_buffer) {
1962 promote_target(*layout_buffer);
1966 auto promote_upper_priority_rect = [&](
int start_tile_x,
int start_tile_y,
1967 int width_tiles,
int height_tiles) {
1971 constexpr int kRows = 64;
1972 const int bg1_rows =
1973 std::max(0, std::min(height_tiles, kRows - start_tile_y));
1974 promote_priority_rect_on_layer(bg1, layout_bg1, start_tile_x, start_tile_y,
1975 width_tiles, bg1_rows);
1976 if (bg1_rows < height_tiles) {
1977 const int bg2_start = std::max(0, start_tile_y - kRows);
1978 promote_priority_rect_on_layer(
1979 bg2, layout_bg2, start_tile_x, bg2_start, width_tiles,
1980 start_tile_y + height_tiles - kRows - bg2_start);
1984 auto promote_lower_priority_rect = [&](
int start_tile_x,
int start_tile_y,
1985 int width_tiles,
int height_tiles) {
1986 promote_priority_rect_on_layer(bg2, layout_bg2, start_tile_x, start_tile_y,
1987 width_tiles, height_tiles);
1990 auto promote_door_priority = [&](
DoorDirection render_direction,
1991 int render_tile_x,
int render_tile_y,
1992 DoorPrioritySpan span) {
1993 constexpr int kSectionSize = 32;
1994 const auto section_start = [](
int coordinate) {
1995 return (coordinate / kSectionSize) * kSectionSize;
1997 const auto section_end = [&](
int coordinate) {
1998 return section_start(coordinate) + kSectionSize;
2001 int start_x = render_tile_x;
2002 int start_y = render_tile_y;
2007 case DoorPrioritySpan::kNormalLower:
2008 switch (render_direction) {
2010 start_y = section_start(render_tile_y);
2017 start_y = render_tile_y + 3;
2022 start_x = section_start(render_tile_x);
2029 start_x = render_tile_x + 3;
2036 case DoorPrioritySpan::kLowerLayerOnly:
2037 switch (render_direction) {
2039 start_y = section_start(render_tile_y);
2041 height = render_tile_y - start_y + 1;
2044 start_y = render_tile_y + 1;
2046 height = section_end(render_tile_y) - start_y;
2049 start_x = section_start(render_tile_x);
2050 width = render_tile_x - start_x + 1;
2054 start_x = render_tile_x + 1;
2055 width = section_end(render_tile_x) - start_x;
2061 case DoorPrioritySpan::kHighRange:
2062 switch (render_direction) {
2064 start_y = section_start(render_tile_y);
2066 height = render_tile_y - start_y;
2069 start_y = render_tile_y + 3;
2071 height = section_end(render_tile_y) - start_y;
2074 start_x = section_start(render_tile_x);
2075 width = render_tile_x - start_x;
2081 start_x = render_tile_x + 3;
2082 width = section_end(render_tile_x) - start_x;
2089 if (width > 0 && height > 0) {
2090 promote_upper_priority_rect(start_x, start_y, width, height);
2103 if (is_nonvisual_marker) {
2110 promote_door_priority(door.
direction, tile_x, tile_y,
2111 DoorPrioritySpan::kLowerLayerOnly);
2119 const auto [raw_tile_x, raw_tile_y] =
2122 const int fancy_data_addr =
2123 kRoomDrawObjectDataBase + kFancyDungeonExitObjectOffset;
2124 const int cave_light_data_addr =
2125 kRoomDrawObjectDataBase + kCaveExitLightObjectOffset;
2126 const auto has_object_words = [&](
int address,
int word_count) {
2127 return address >= 0 &&
2128 address + word_count * 2 <=
static_cast<int>(
rom_->
size());
2131 switch (door.
type) {
2133 if (!has_object_words(fancy_data_addr, 80)) {
2139 draw_from_object_data_row_major(bg1, raw_tile_x - 3, raw_tile_y - 4,
2145 if (!has_object_words(fancy_data_addr, 80)) {
2151 draw_from_object_data_row_major(bg2, raw_tile_x - 3, raw_tile_y - 4,
2156 for (
int dx = 0; dx < 10; ++dx) {
2157 (void)draw_object_data_tile(
2158 bg1, raw_tile_x - 3 + dx, raw_tile_y + 3, fancy_data_addr,
2164 if (!has_object_words(cave_light_data_addr, 16)) {
2169 promote_lower_priority_rect(raw_tile_x, raw_tile_y + 4,
2171 draw_from_object_data(bg2, raw_tile_x, raw_tile_y, 4,
2172 4, cave_light_data_addr);
2173 for (
int dx = 0; dx < 4; ++dx) {
2174 (void)draw_object_data_tile(
2175 bg1, raw_tile_x + dx, raw_tile_y + 3, cave_light_data_addr,
2176 dx * 4 + 3, 0x2000);
2181 if (!has_object_words(cave_light_data_addr, 16)) {
2186 draw_from_object_data(bg1, raw_tile_x, raw_tile_y, 4,
2187 4, cave_light_data_addr);
2191 if (!has_object_words(cave_light_data_addr, 16)) {
2196 promote_upper_priority_rect(raw_tile_x, raw_tile_y + 4,
2198 draw_from_object_data(bg1, raw_tile_x, raw_tile_y, 4,
2199 4, cave_light_data_addr);
2207 auto resolve_render_type = [](
DoorDirection render_direction,
2208 uint16_t render_type) -> uint16_t {
2209 switch (render_type) {
2241 auto resolve_table_door_data = [&](
DoorDirection render_direction,
2242 uint16_t render_type,
2243 int* tile_data_addr) ->
bool {
2244 int offset_table_addr = 0;
2245 switch (render_direction) {
2260 const uint16_t resolved_type =
2261 resolve_render_type(render_direction, render_type);
2265 const int table_entry_addr = offset_table_addr + resolved_type;
2266 if (table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2270 const uint16_t tile_offset =
2271 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2272 *tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2274 const int data_size = dims.width_tiles * dims.height_tiles * 2;
2275 if (*tile_data_addr < 0 ||
2276 *tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2285 int start_tile_y, uint16_t render_type) ->
bool {
2286 int tile_data_addr = 0;
2287 if (!resolve_table_door_data(render_direction, render_type,
2293 draw_from_object_data(target, start_tile_x, start_tile_y,
2294 render_dims.width_tiles, render_dims.height_tiles,
2299 auto draw_high_range_table_door = [&](
DoorDirection render_direction,
2300 int start_tile_x,
int start_tile_y,
2301 uint16_t render_type) ->
bool {
2302 int tile_data_addr = 0;
2303 if (!resolve_table_door_data(render_direction, render_type,
2310 for (
int dx = 0; dx < dims.width_tiles; ++dx) {
2311 for (
int dy = 0; dy < dims.height_tiles; ++dy) {
2314 bool writes_upper =
false;
2315 switch (render_direction) {
2317 writes_upper = dy == 0;
2320 writes_upper = dy == dims.height_tiles - 1;
2323 writes_upper = dx == 0;
2326 writes_upper = dx == dims.width_tiles - 1;
2330 if (!draw_object_data_tile(target, start_tile_x + dx, start_tile_y + dy,
2331 tile_data_addr, tile_idx++)) {
2339 auto draw_ranged_door = [&](
DoorDirection render_direction,
int start_tile_x,
2340 int start_tile_y,
DoorType original_type,
2341 uint16_t table_type) ->
bool {
2345 promote_door_priority(render_direction, start_tile_x, start_tile_y,
2346 DoorPrioritySpan::kNormalLower);
2352 const int render_type_value =
static_cast<int>(original_type);
2353 const bool is_high_range =
2354 render_type_value >= 0x40 && render_type_value <= 0x66;
2355 if (is_high_range) {
2356 const bool drew = draw_high_range_table_door(
2357 render_direction, start_tile_x, start_tile_y, table_type);
2362 promote_door_priority(render_direction, start_tile_x, start_tile_y,
2363 DoorPrioritySpan::kHighRange);
2367 return draw_table_door(bg1, render_direction, start_tile_x, start_tile_y,
2376 "DrawDoor: closed exploding wall intentionally draws nothing");
2383 const int tile_data_addr =
2384 kRoomDrawObjectDataBase + kNorthCurtainClosedOffset;
2385 const int data_size = 16 * 2;
2386 if (tile_data_addr < 0 ||
2387 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2392 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
2399 const int table_entry_addr =
2400 kDoorGfxUp +
static_cast<int>(effective_door_type);
2401 if (table_entry_addr < 0 ||
2402 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2408 const uint16_t tile_offset =
2409 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2410 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2411 const int data_size = 16 * 2;
2412 if (tile_data_addr < 0 ||
2413 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
2419 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
2426 const int position_index = std::min<int>(door.
position & 0x0F, 5);
2427 const int tilemap_entry_addr =
2428 kExplodingWallTilemapPositionBase + (position_index * 2);
2429 if (tilemap_entry_addr < 0 ||
2430 tilemap_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2436 const uint16_t tilemap_offset =
2437 rom_data[tilemap_entry_addr] | (rom_data[tilemap_entry_addr + 1] << 8);
2438 const auto explosion_tile_coords =
2439 tilemap_offset_to_tile_coords(tilemap_offset);
2440 const int explosion_tile_x = explosion_tile_coords.first;
2441 const int explosion_tile_y = explosion_tile_coords.second;
2443 auto draw_exploding_wall_segment = [&](
int table_entry_addr,
2444 int segment_tile_y) ->
bool {
2445 if (table_entry_addr < 0 ||
2446 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
2450 const uint16_t tile_offset =
2451 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
2452 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
2455 constexpr int kFillWordIndex = 12;
2456 constexpr int kRightColumnWordIndex = 13;
2457 const int min_data_size = (kRightColumnWordIndex + 12) * 2;
2458 if (tile_data_addr < 0 ||
2459 tile_data_addr + min_data_size >
static_cast<int>(
rom_->
size())) {
2463 draw_from_object_data(bg1, explosion_tile_x, segment_tile_y,
2464 2, 6, tile_data_addr);
2465 const uint16_t fill_word =
2466 rom_data[tile_data_addr + (kFillWordIndex * 2)] |
2467 (rom_data[tile_data_addr + (kFillWordIndex * 2) + 1] << 8);
2468 draw_repeated_tile(bg1, explosion_tile_x + 2, segment_tile_y,
2470 draw_from_object_data(bg1, explosion_tile_x + 20, segment_tile_y,
2472 tile_data_addr + (kRightColumnWordIndex * 2));
2476 const int south_table_entry_addr =
2478 const int north_table_entry_addr =
2479 kDoorGfxUp + kExplodingWallOpenReplacementType;
2480 if (!draw_exploding_wall_segment(south_table_entry_addr,
2481 explosion_tile_y) ||
2482 !draw_exploding_wall_segment(north_table_entry_addr,
2483 explosion_tile_y + 6)) {
2493 const bool is_north_key_stairs =
2499 if (is_north_key_stairs && is_door_open) {
2506 const bool is_north_lower_key_stairs =
2510 if (is_north_lower_key_stairs) {
2511 if (!draw_table_door(bg2, door.
direction, tile_x, tile_y,
2512 static_cast<uint16_t
>(door.
type))) {
2522 const bool uses_normal_ranged_writer =
2523 static_cast<int>(door.
type) <
2525 if (uses_normal_ranged_writer &&
2546 position_index >= 6 && !north_explicit_door && !north_key_stairs) {
2554 const auto [counterpart_tile_x, counterpart_tile_y] =
2556 static_cast<uint8_t
>(position_index - 6), counterpart_direction);
2557 (void)draw_ranged_door(counterpart_direction, counterpart_tile_x,
2558 counterpart_tile_y, door.
type, effective_door_type);
2561 const bool drew_current = draw_ranged_door(door.
direction, tile_x, tile_y,
2562 door.
type, effective_door_type);
2563 if (!drew_current) {
2565 "DrawDoor: INVALID ADDRESS - falling back to indicator");
2572 "DrawDoor: type=%s effective=0x%04X dir=%s pos=%d at tile(%d,%d) "
2575 static_cast<unsigned int>(effective_door_type),
2577 door.
position, tile_x, tile_y, door_width, door_height);
3183 const int anchor =
object.y_ * 0x80 +
object.x_ * 2;
3184 auto read_word = [&](
int data_offset) -> uint16_t {
3186 if (address < 0 || address + 1 >=
static_cast<int>(rom_data.size())) {
3189 return static_cast<uint16_t
>(rom_data[address] |
3190 (rom_data[address + 1] << 8));
3192 auto tile_at = [&](
int store,
int x) -> std::pair<int, int> {
3193 return {((store - 0x2000 + x) / 2) % 64, ((store - 0x2000 + x) / 2) / 64};
3195 auto store = [&](
int address,
int x, uint16_t word) {
3196 auto [tile_x, tile_y] = tile_at(address, x);
3200 }
else if (tile_y < 128) {
3205 constexpr uint16_t kHFlip = 0x4000;
3208 for (
int i = 0, x = anchor, y = 0x1BF2; i < 6; ++i, x += 2, y += 8) {
3209 for (
int row = 0; row < 4; ++row) {
3210 const uint16_t word = read_word(y + row * 2);
3211 for (
int base : {0x220E, 0x221A, 0x2226}) {
3212 store(base + row * 0x80, x, word);
3217 for (
int i = 0, x = anchor, y = 0x1C22; i < 5; ++i, x += 0x80, y += 2) {
3218 const uint16_t word = read_word(y);
3219 for (
int base : {0x2504, 0x2486, 0x2408, 0x238A, 0x230C, 0x228E, 0x2210}) {
3220 store(base, x, word);
3222 for (
int base : {0x222E, 0x22B0, 0x2332, 0x23B4, 0x2436, 0x24B8, 0x253A}) {
3223 store(base, x, word | kHFlip);
3228 for (
int i = 0, x = anchor, y = 0x1C2C; i < 6; ++i, x += 0x80, y += 8) {
3229 for (
int k = 0; k < 4; ++k) {
3230 const uint16_t word = read_word(y + k * 2);
3231 for (
int base : {0x2584, 0x2884, 0x2B84}) {
3232 store(base + k * 2, x, word);
3234 for (
int base : {0x25BA, 0x28BA, 0x2BBA}) {
3235 store(base - k * 2, x, word | kHFlip);
3240 for (
int i = 0, x = anchor, y = 0x1C5C; i < 6; ++i, x += 2, y += 2) {
3241 const uint16_t top = read_word(y);
3242 const uint16_t bottom = read_word(y + 12);
3243 store(0x2498, x, top);
3244 store(0x24A4, x, top);
3245 store(0x2518, x, bottom);
3246 store(0x2524, x, bottom);
3249 for (
int i = 0, x = anchor, y = 0x1C74; i < 6; ++i, x += 0x80, y += 4) {
3250 store(0x270E, x, read_word(y));
3251 store(0x2A0E, x, read_word(y));
3252 store(0x2710, x, read_word(y + 2));
3253 store(0x2A10, x, read_word(y + 2));
3256 for (
int i = 0, x = anchor, y = 0x1C8C; i < 5; ++i, x += 2, y += 10) {
3257 for (
int row = 0; row < 5; ++row) {
3258 store(0x248E + row * 0x80, x, read_word(y + row * 2));
3263 for (
int i = 0, x = anchor; i < 4; ++i, x += 2) {
3264 for (
int base : {0x2E1C, 0x2E9C}) {
3265 auto [tile_x, tile_y] = tile_at(base, x);
3267 PromoteTilePriorityOnOwners(bg1, layout_bg1, tile_x, tile_y);
3268 }
else if (tile_y < 128) {
3269 PromoteTilePriorityOnOwners(bg2, layout_bg2, tile_x, tile_y - 64);
3274 return absl::OkStatus();