532 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
538 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
544 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
550 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
556 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
562 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
568 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
574 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
580 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
586 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
592 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
598 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
604 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
610 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
616 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
622 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
628 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
634 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
640 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
646 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
653 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
659 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
665 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
671 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
677 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
683 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
689 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
695 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
701 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
707 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
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) {
833 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
839 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
845 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
851 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
857 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
863 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
874 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
881 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
889 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
896 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
903 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
910 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
917 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
924 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
931 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
942 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) {
963 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
970 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
977 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
984 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
991 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
998 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1005 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1016 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1023 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1030 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1037 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1048 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) {
1069 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1080 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1087 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1094 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1101 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1108 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1115 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1122 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1129 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1136 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1143 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1150 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1157 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1164 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1171 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) {
1194 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1202 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1210 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1218 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1220 obj, bg, tiles, state);
1227 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1235 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1243 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1251 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1259 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1267 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1269 obj, bg, tiles, state);
1275 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1277 obj, bg, tiles, state);
1283 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1291 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1299 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1309 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1319 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1320 if (obj.
id_ == 0xFB1) {
1321 self->DrawBigChest(obj, bg, tiles, state);
1333 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1343 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1348 auto ensure_index = [
this](
size_t index) {
1352 std::span<const gfx::TileInfo> tiles,
1364 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1372 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1379 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1387 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1395 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1403 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1412 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1420 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1428 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1436 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1444 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1452 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1460 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1468 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1476 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1484 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1495 std::span<const gfx::TileInfo> tiles,
1520 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: idx=%d type=%d dir=%d pos=%d",
1521 door_index,
static_cast<int>(door.
type),
1525 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: SKIPPED - rom=%p loaded=%d gfx=%p",
1531 auto& bitmap = bg1.
bitmap();
1532 if (!bitmap.is_active() || bitmap.width() == 0) {
1534 "DrawDoor: SKIPPED - bitmap not active or zero width");
1544 int door_height = dims.height_tiles;
1546 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: tile_pos=(%d,%d) dims=%dx%d", tile_x,
1547 tile_y, door_width, door_height);
1549 constexpr int kRoomDrawObjectDataBase = 0x1B52;
1550 constexpr int kDoorwayReplacementDoorGfxBase = 0x1A02;
1551 constexpr int kExplodingWallTilemapPositionBase = 0x19DE;
1552 constexpr int kExplodingWallOpenReplacementType = 0x54;
1553 constexpr int kNorthCurtainClosedOffset = 0x078A;
1554 const auto& rom_data =
rom_->
data();
1557 int start_tile_x,
int start_tile_y,
1558 int width,
int height,
int tile_data_addr) {
1559 auto& bitmap = target.
bitmap();
1562 const int bitmap_width = bitmap.width();
1565 for (
int dx = 0; dx < width; dx++) {
1566 for (
int dy = 0; dy < height; dy++) {
1567 const int addr = tile_data_addr + (tile_idx * 2);
1568 const uint16_t tile_word = rom_data[addr] | (rom_data[addr + 1] << 8);
1570 const int pixel_x = (start_tile_x + dx) * 8;
1571 const int pixel_y = (start_tile_y + dy) * 8;
1577 const uint8_t priority = tile_info.over_ ? 1 : 0;
1578 const auto& bitmap_data = bitmap.vector();
1579 for (
int py = 0; py < 8; py++) {
1580 const int dest_y = pixel_y + py;
1581 if (dest_y < 0 || dest_y >= bitmap.height()) {
1584 for (
int px = 0; px < 8; px++) {
1585 const int dest_x = pixel_x + px;
1586 if (dest_x < 0 || dest_x >= bitmap_width) {
1589 const int dest_index = dest_y * bitmap_width + dest_x;
1590 if (dest_index >= 0 &&
1591 dest_index <
static_cast<int>(coverage_buffer.size())) {
1592 coverage_buffer[dest_index] = 1;
1594 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1595 bitmap_data[dest_index] != 255) {
1596 priority_buffer[dest_index] = priority;
1607 int start_tile_y,
int width,
int height,
1608 uint16_t tile_word) {
1609 auto& bitmap = target.
bitmap();
1612 const int bitmap_width = bitmap.width();
1615 for (
int dx = 0; dx < width; dx++) {
1616 for (
int dy = 0; dy < height; dy++) {
1617 const int pixel_x = (start_tile_x + dx) * 8;
1618 const int pixel_y = (start_tile_y + dy) * 8;
1624 const uint8_t priority = tile_info.over_ ? 1 : 0;
1625 const auto& bitmap_data = bitmap.vector();
1626 for (
int py = 0; py < 8; py++) {
1627 const int dest_y = pixel_y + py;
1628 if (dest_y < 0 || dest_y >= bitmap.height()) {
1631 for (
int px = 0; px < 8; px++) {
1632 const int dest_x = pixel_x + px;
1633 if (dest_x < 0 || dest_x >= bitmap_width) {
1636 const int dest_index = dest_y * bitmap_width + dest_x;
1637 if (dest_index >= 0 &&
1638 dest_index <
static_cast<int>(coverage_buffer.size())) {
1639 coverage_buffer[dest_index] = 1;
1641 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1642 bitmap_data[dest_index] != 255) {
1643 priority_buffer[dest_index] = priority;
1651 auto tilemap_offset_to_tile_coords = [](uint16_t offset) {
1652 return std::pair<int, int>{
static_cast<int>((offset % 0x80) / 2),
1653 static_cast<int>(offset / 0x80) - 4};
1655 const int position_index = std::min<int>(door.
position & 0x0F, 11);
1657 auto resolve_render_type = [](
DoorDirection render_direction,
1659 switch (render_type) {
1687 int start_tile_y,
DoorType render_type) ->
bool {
1688 int offset_table_addr = 0;
1689 switch (render_direction) {
1705 resolve_render_type(render_direction, render_type);
1706 const int render_type_value =
static_cast<int>(resolved_type);
1707 const int type_index = render_type_value / 2;
1708 const int table_entry_addr = offset_table_addr + (type_index * 2);
1709 if (table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1713 const uint16_t tile_offset =
1714 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1715 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1717 const int data_size = dims.width_tiles * dims.height_tiles * 2;
1718 if (tile_data_addr < 0 ||
1719 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1723 draw_from_object_data(target, start_tile_x, start_tile_y, dims.width_tiles,
1724 dims.height_tiles, tile_data_addr);
1733 "DrawDoor: closed exploding wall intentionally draws nothing");
1740 const int tile_data_addr =
1741 kRoomDrawObjectDataBase + kNorthCurtainClosedOffset;
1742 const int data_size = 16 * 2;
1743 if (tile_data_addr < 0 ||
1744 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1749 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
1756 const int replacement_type_addr =
1757 kDoorwayReplacementDoorGfxBase +
static_cast<int>(door.
type);
1758 if (replacement_type_addr < 0 ||
1759 replacement_type_addr >=
static_cast<int>(
rom_->
size())) {
1765 const int replacement_type = rom_data[replacement_type_addr];
1766 const int table_entry_addr =
kDoorGfxUp + replacement_type;
1767 if (table_entry_addr < 0 ||
1768 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1774 const uint16_t tile_offset =
1775 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1776 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1777 const int data_size = 16 * 2;
1778 if (tile_data_addr < 0 ||
1779 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1785 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
1792 const int position_index = std::min<int>(door.
position & 0x0F, 5);
1793 const int tilemap_entry_addr =
1794 kExplodingWallTilemapPositionBase + (position_index * 2);
1795 if (tilemap_entry_addr < 0 ||
1796 tilemap_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1802 const uint16_t tilemap_offset =
1803 rom_data[tilemap_entry_addr] | (rom_data[tilemap_entry_addr + 1] << 8);
1804 const auto explosion_tile_coords =
1805 tilemap_offset_to_tile_coords(tilemap_offset);
1806 const int explosion_tile_x = explosion_tile_coords.first;
1807 const int explosion_tile_y = explosion_tile_coords.second;
1809 auto draw_exploding_wall_segment = [&](
int table_entry_addr,
1810 int segment_tile_y) ->
bool {
1811 if (table_entry_addr < 0 ||
1812 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1816 const uint16_t tile_offset =
1817 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1818 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1819 constexpr int kFillWordIndex = 12;
1820 const int min_data_size = (kFillWordIndex + 1) * 2;
1821 if (tile_data_addr < 0 ||
1822 tile_data_addr + min_data_size >
static_cast<int>(
rom_->
size())) {
1826 draw_from_object_data(bg1, explosion_tile_x, segment_tile_y,
1827 2, 6, tile_data_addr);
1828 const uint16_t fill_word =
1829 rom_data[tile_data_addr + (kFillWordIndex * 2)] |
1830 (rom_data[tile_data_addr + (kFillWordIndex * 2) + 1] << 8);
1831 draw_repeated_tile(bg1, explosion_tile_x + 2, segment_tile_y,
1836 const int south_table_entry_addr =
1838 const int north_table_entry_addr =
1839 kDoorGfxUp + kExplodingWallOpenReplacementType;
1840 if (!draw_exploding_wall_segment(south_table_entry_addr,
1841 explosion_tile_y) ||
1842 !draw_exploding_wall_segment(north_table_entry_addr,
1843 explosion_tile_y + 6)) {
1861 const int counterpart_tile_x =
1863 const int counterpart_tile_y =
1865 (void)draw_table_door(bg1, counterpart_direction, counterpart_tile_x,
1866 counterpart_tile_y, door.
type);
1869 const bool drew_current =
1870 draw_table_door(bg1, door.
direction, tile_x, tile_y, door.
type);
1871 if (!drew_current) {
1873 "DrawDoor: INVALID ADDRESS - falling back to indicator");
1880 "DrawDoor: type=%s dir=%s pos=%d at tile(%d,%d) size=%dx%d",
1883 door.
position, tile_x, tile_y, door_width, door_height);