443 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
449 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
455 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
461 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
467 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
473 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
479 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
485 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
491 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
497 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
503 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
509 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
515 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
521 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
527 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
533 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
539 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
545 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
551 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
557 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
564 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
570 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
576 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
582 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
588 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
594 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
600 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
606 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
612 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
618 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
624 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
630 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
636 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
642 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
648 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
654 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
660 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
666 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
672 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
678 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
684 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
690 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
696 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
702 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
708 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
714 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
720 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
726 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
732 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
738 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
744 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
750 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
756 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
762 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
768 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
774 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
785 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
792 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
800 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
807 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
814 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
821 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
828 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
835 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
842 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
853 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
860 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
867 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) {
888 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
895 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
902 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
909 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
916 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
927 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
934 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
941 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
948 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
959 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
966 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
973 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
980 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) {
1012 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1019 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1026 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1033 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1040 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1047 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1054 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1061 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1068 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1075 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1082 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) {
1106 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1114 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1122 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1130 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1132 obj, bg, tiles, state);
1139 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1147 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1155 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1163 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) {
1181 obj, bg, tiles, state);
1187 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1189 obj, bg, tiles, state);
1195 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1203 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1211 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1221 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1231 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1241 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1251 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1256 auto ensure_index = [
this](
size_t index) {
1260 std::span<const gfx::TileInfo> tiles,
1272 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1280 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1290 std::span<const gfx::TileInfo> tiles,
1315 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: idx=%d type=%d dir=%d pos=%d",
1316 door_index,
static_cast<int>(door.
type),
1320 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: SKIPPED - rom=%p loaded=%d gfx=%p",
1326 auto& bitmap = bg1.
bitmap();
1327 if (!bitmap.is_active() || bitmap.width() == 0) {
1329 "DrawDoor: SKIPPED - bitmap not active or zero width");
1333 const bool is_door_open =
1340 int door_height = dims.height_tiles;
1342 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: tile_pos=(%d,%d) dims=%dx%d", tile_x,
1343 tile_y, door_width, door_height);
1345 constexpr int kRoomDrawObjectDataBase = 0x1B52;
1346 constexpr int kDoorwayReplacementDoorGfxBase = 0x1A02;
1347 constexpr int kExplodingWallTilemapPositionBase = 0x19DE;
1348 constexpr int kExplodingWallOpenReplacementType = 0x54;
1349 constexpr int kNorthCurtainClosedOffset = 0x078A;
1350 const auto& rom_data =
rom_->
data();
1352 auto draw_from_object_data =
1354 int width,
int height,
int tile_data_addr) {
1355 auto& bitmap = target.
bitmap();
1358 const int bitmap_width = bitmap.width();
1361 for (
int dx = 0; dx < width; dx++) {
1362 for (
int dy = 0; dy < height; dy++) {
1363 const int addr = tile_data_addr + (tile_idx * 2);
1364 const uint16_t tile_word =
1365 rom_data[addr] | (rom_data[addr + 1] << 8);
1367 const int pixel_x = (start_tile_x + dx) * 8;
1368 const int pixel_y = (start_tile_y + dy) * 8;
1373 const uint8_t priority = tile_info.over_ ? 1 : 0;
1374 const auto& bitmap_data = bitmap.vector();
1375 for (
int py = 0; py < 8; py++) {
1376 const int dest_y = pixel_y + py;
1377 if (dest_y < 0 || dest_y >= bitmap.height()) {
1380 for (
int px = 0; px < 8; px++) {
1381 const int dest_x = pixel_x + px;
1382 if (dest_x < 0 || dest_x >= bitmap_width) {
1385 const int dest_index = dest_y * bitmap_width + dest_x;
1386 if (dest_index >= 0 &&
1387 dest_index <
static_cast<int>(coverage_buffer.size())) {
1388 coverage_buffer[dest_index] = 1;
1390 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1391 bitmap_data[dest_index] != 255) {
1392 priority_buffer[dest_index] = priority;
1402 auto draw_repeated_tile =
1404 int width,
int height, uint16_t tile_word) {
1405 auto& bitmap = target.
bitmap();
1408 const int bitmap_width = bitmap.width();
1411 for (
int dx = 0; dx < width; dx++) {
1412 for (
int dy = 0; dy < height; dy++) {
1413 const int pixel_x = (start_tile_x + dx) * 8;
1414 const int pixel_y = (start_tile_y + dy) * 8;
1419 const uint8_t priority = tile_info.over_ ? 1 : 0;
1420 const auto& bitmap_data = bitmap.vector();
1421 for (
int py = 0; py < 8; py++) {
1422 const int dest_y = pixel_y + py;
1423 if (dest_y < 0 || dest_y >= bitmap.height()) {
1426 for (
int px = 0; px < 8; px++) {
1427 const int dest_x = pixel_x + px;
1428 if (dest_x < 0 || dest_x >= bitmap_width) {
1431 const int dest_index = dest_y * bitmap_width + dest_x;
1432 if (dest_index >= 0 &&
1433 dest_index <
static_cast<int>(coverage_buffer.size())) {
1434 coverage_buffer[dest_index] = 1;
1436 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1437 bitmap_data[dest_index] != 255) {
1438 priority_buffer[dest_index] = priority;
1446 auto tilemap_offset_to_tile_coords = [](uint16_t offset) {
1447 return std::pair<int, int>{
static_cast<int>((offset % 0x80) / 2),
1448 static_cast<int>(offset / 0x80) - 4};
1450 const int position_index = std::min<int>(door.
position & 0x0F, 11);
1452 auto resolve_render_type = [](
DoorDirection render_direction,
1454 switch (render_type) {
1482 int start_tile_y,
DoorType render_type) ->
bool {
1483 int offset_table_addr = 0;
1484 switch (render_direction) {
1500 resolve_render_type(render_direction, render_type);
1501 const int render_type_value =
static_cast<int>(resolved_type);
1502 const int type_index = render_type_value / 2;
1503 const int table_entry_addr = offset_table_addr + (type_index * 2);
1504 if (table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1508 const uint16_t tile_offset =
1509 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1510 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1512 const int data_size = dims.width_tiles * dims.height_tiles * 2;
1513 if (tile_data_addr < 0 ||
1514 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1518 draw_from_object_data(target, start_tile_x, start_tile_y, dims.width_tiles,
1519 dims.height_tiles, tile_data_addr);
1528 "DrawDoor: closed exploding wall intentionally draws nothing");
1535 const int tile_data_addr =
1536 kRoomDrawObjectDataBase + kNorthCurtainClosedOffset;
1537 const int data_size = 16 * 2;
1538 if (tile_data_addr < 0 ||
1539 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1544 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
1551 const int replacement_type_addr =
1552 kDoorwayReplacementDoorGfxBase +
static_cast<int>(door.
type);
1553 if (replacement_type_addr < 0 ||
1554 replacement_type_addr >=
static_cast<int>(
rom_->
size())) {
1560 const int replacement_type = rom_data[replacement_type_addr];
1561 const int table_entry_addr =
kDoorGfxUp + replacement_type;
1562 if (table_entry_addr < 0 ||
1563 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1569 const uint16_t tile_offset =
1570 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1571 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1572 const int data_size = 16 * 2;
1573 if (tile_data_addr < 0 ||
1574 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1580 draw_from_object_data(bg1, tile_x, tile_y, 4, 4,
1587 const int position_index = std::min<int>(door.
position & 0x0F, 5);
1588 const int tilemap_entry_addr =
1589 kExplodingWallTilemapPositionBase + (position_index * 2);
1590 if (tilemap_entry_addr < 0 ||
1591 tilemap_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1597 const uint16_t tilemap_offset =
1598 rom_data[tilemap_entry_addr] | (rom_data[tilemap_entry_addr + 1] << 8);
1599 const auto [explosion_tile_x, explosion_tile_y] =
1600 tilemap_offset_to_tile_coords(tilemap_offset);
1602 auto draw_exploding_wall_segment =
1603 [&](
int table_entry_addr,
int segment_tile_y) ->
bool {
1604 if (table_entry_addr < 0 ||
1605 table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1609 const uint16_t tile_offset =
1610 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1611 const int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1612 constexpr int kFillWordIndex = 12;
1613 const int min_data_size = (kFillWordIndex + 1) * 2;
1614 if (tile_data_addr < 0 ||
1615 tile_data_addr + min_data_size >
static_cast<int>(
rom_->
size())) {
1619 draw_from_object_data(bg1, explosion_tile_x, segment_tile_y,
1620 2, 6, tile_data_addr);
1621 const uint16_t fill_word =
1622 rom_data[tile_data_addr + (kFillWordIndex * 2)] |
1623 (rom_data[tile_data_addr + (kFillWordIndex * 2) + 1] << 8);
1624 draw_repeated_tile(bg1, explosion_tile_x + 2, segment_tile_y,
1629 const int south_table_entry_addr =
1631 const int north_table_entry_addr =
1632 kDoorGfxUp + kExplodingWallOpenReplacementType;
1633 if (!draw_exploding_wall_segment(south_table_entry_addr,
1634 explosion_tile_y) ||
1635 !draw_exploding_wall_segment(north_table_entry_addr,
1636 explosion_tile_y + 6)) {
1654 const int counterpart_tile_x =
1656 const int counterpart_tile_y =
1658 (void)draw_table_door(bg1, counterpart_direction, counterpart_tile_x,
1659 counterpart_tile_y, door.
type);
1662 const bool drew_current =
1663 draw_table_door(bg1, door.
direction, tile_x, tile_y, door.
type);
1664 if (!drew_current) {
1666 "DrawDoor: INVALID ADDRESS - falling back to indicator");
1673 "DrawDoor: type=%s dir=%s pos=%d at tile(%d,%d) size=%dx%d",
1676 door.
position, tile_x, tile_y, door_width, door_height);