6#include "absl/strings/str_format.h"
21 const uint8_t* room_gfx_buffer)
22 : rom_(rom), room_id_(room_id), room_gfx_buffer_(room_gfx_buffer) {
56 flags |=
static_cast<uint8_t
>((tile_info.
palette_ & 0x7) << 3);
62 trace.x_tile =
static_cast<int16_t
>(tile_x);
63 trace.y_tile =
static_cast<int16_t
>(tile_y);
64 trace.tile_id = tile_info.
id_;
76 drawer->
PushTrace(tile_x, tile_y, tile_info);
81 std::span<const gfx::TileInfo> tiles,
89 if (info ==
nullptr) {
90 LOG_DEBUG(
"ObjectDrawer",
"DrawUsingRegistryRoutine: unknown routine %d",
95 struct CapturedWrite {
101 std::vector<CapturedWrite> writes;
107 auto* out =
static_cast<std::vector<CapturedWrite>*
>(user_data);
111 out->push_back(CapturedWrite{tile_x, tile_y, tile_info});
124 .secondary_bg =
nullptr,
130 for (
const auto& w : writes) {
141 return absl::FailedPreconditionError(
"ROM not loaded");
145 return absl::FailedPreconditionError(
"Draw routines not initialized");
149 auto mutable_obj =
const_cast<RoomObject&
>(object);
151 mutable_obj.EnsureTilesLoaded();
158 auto& target_bg = use_bg2 ? bg2 : bg1;
161 LOG_DEBUG(
"ObjectDrawer",
"Object 0x%03X layer=%d -> drawing to %s buffer",
162 object.id_,
static_cast<int>(
object.layer_),
163 use_bg2 ?
"BG2 (behind layout)" :
"BG1 (on top of layout)");
167 int subtype =
object.size_ & 0x1F;
168 bool is_custom_object =
false;
171 is_custom_object =
true;
181 if (
object.all_bgs_) {
190 return absl::OkStatus();
194 if (!is_custom_object && mutable_obj.tiles().empty()) {
196 "Object 0x%03X at (%d,%d) has NO TILES - skipping",
object.id_,
197 object.x_,
object.y_);
198 return absl::OkStatus();
206 "Object 0x%03X at (%d,%d) size=%d -> routine=%d tiles=%zu",
207 object.id_,
object.x_,
object.y_,
object.size_, routine_id,
208 mutable_obj.tiles().size());
210 if (routine_id < 0 || routine_id >=
static_cast<int>(
draw_routines_.size())) {
212 "Object 0x%03X: NO ROUTINE (id=%d, max=%zu) - using fallback 1x1",
215 if (!mutable_obj.tiles().empty()) {
216 const auto& tile_info = mutable_obj.tiles()[0];
219 WriteTile8(target_bg,
object.x_,
object.y_, tile_info);
221 return absl::OkStatus();
229 if (routine_info && routine_info->
min_tiles > 0 &&
230 static_cast<int>(mutable_obj.tiles().size()) <
233 "Object 0x%03X at (%d,%d): tile payload too small "
234 "(%zu < %d required by routine '%s') - skipping",
235 object.id_,
object.x_,
object.y_,
236 mutable_obj.tiles().size(), routine_info->
min_tiles,
237 routine_info->
name.c_str());
239 if (!mutable_obj.tiles().empty()) {
240 const auto& tile_info = mutable_obj.tiles()[0];
243 WriteTile8(target_bg,
object.x_,
object.y_, tile_info);
245 return absl::OkStatus();
248 bool trace_hook_active =
false;
252 trace_hook_active =
true;
263 draw_routines_[routine_id](
this, object, bg1, mutable_obj.tiles(), state);
265 draw_routines_[routine_id](
this, object, bg2, mutable_obj.tiles(), state);
274 if (trace_hook_active) {
287 bool is_pit_or_mask =
288 (
object.id_ == 0xA4) ||
289 (
object.id_ >= 0xA5 &&
object.id_ <= 0xAC) ||
290 (
object.id_ == 0xC0) ||
291 (
object.id_ == 0xC2) ||
292 (
object.id_ == 0xC3) ||
293 (
object.id_ == 0xC8) ||
294 (
object.id_ == 0xC6) ||
295 (
object.id_ == 0xD7) ||
296 (
object.id_ == 0xD8) ||
297 (
object.id_ == 0xD9) ||
298 (
object.id_ == 0xDA) ||
299 (
object.id_ == 0xFE6) ||
300 (
object.id_ == 0xFF3);
309 "Pit mask 0x%03X at (%d,%d) -> marking %dx%d pixels transparent in BG1",
310 object.id_,
object.x_,
object.y_, pixel_width, pixel_height);
315 if (layout_bg1 !=
nullptr) {
321 return absl::OkStatus();
330 absl::Status status = absl::OkStatus();
333 int to_bg1 = 0, to_bg2 = 0, both_bgs = 0;
335 for (
const auto&
object : objects) {
343 }
else if (use_bg2) {
349 auto s =
DrawObject(
object, bg1, bg2, palette_group, state, layout_bg1);
350 if (!s.ok() && status.ok()) {
355 LOG_DEBUG(
"ObjectDrawer",
"Buffer routing: to_BG1=%d, to_BG2=%d, BothBGs=%d",
356 to_bg1, to_bg2, both_bgs);
360 auto& bg1_bmp = bg1.
bitmap();
361 auto& bg2_bmp = bg2.
bitmap();
364 if (bg1_bmp.modified() && bg1_bmp.surface() &&
365 bg1_bmp.mutable_data().size() > 0) {
366 SDL_LockSurface(bg1_bmp.surface());
369 size_t surface_size = bg1_bmp.surface()->h * bg1_bmp.surface()->pitch;
370 size_t buffer_size = bg1_bmp.mutable_data().size();
372 if (surface_size >= buffer_size) {
375 memcpy(bg1_bmp.surface()->pixels, bg1_bmp.mutable_data().data(),
378 LOG_DEBUG(
"ObjectDrawer",
"BG1 Surface too small: surf=%zu buf=%zu",
379 surface_size, buffer_size);
381 SDL_UnlockSurface(bg1_bmp.surface());
384 if (bg2_bmp.modified() && bg2_bmp.surface() &&
385 bg2_bmp.mutable_data().size() > 0) {
386 SDL_LockSurface(bg2_bmp.surface());
387 size_t surface_size = bg2_bmp.surface()->h * bg2_bmp.surface()->pitch;
388 size_t buffer_size = bg2_bmp.mutable_data().size();
390 if (surface_size >= buffer_size) {
391 memcpy(bg2_bmp.surface()->pixels, bg2_bmp.mutable_data().data(),
394 LOG_DEBUG(
"ObjectDrawer",
"BG2 Surface too small: surf=%zu buf=%zu",
395 surface_size, buffer_size);
397 SDL_UnlockSurface(bg2_bmp.surface());
441 std::span<const gfx::TileInfo> tiles,
448 std::span<const gfx::TileInfo> tiles,
455 std::span<const gfx::TileInfo> tiles,
462 std::span<const gfx::TileInfo> tiles,
469 std::span<const gfx::TileInfo> tiles,
476 std::span<const gfx::TileInfo> tiles,
483 std::span<const gfx::TileInfo> tiles,
490 std::span<const gfx::TileInfo> tiles,
497 std::span<const gfx::TileInfo> tiles,
504 std::span<const gfx::TileInfo> tiles,
511 std::span<const gfx::TileInfo> tiles,
518 std::span<const gfx::TileInfo> tiles,
525 std::span<const gfx::TileInfo> tiles,
532 std::span<const gfx::TileInfo> tiles,
539 std::span<const gfx::TileInfo> tiles,
546 std::span<const gfx::TileInfo> tiles,
553 std::span<const gfx::TileInfo> tiles,
560 std::span<const gfx::TileInfo> tiles,
567 std::span<const gfx::TileInfo> tiles,
574 std::span<const gfx::TileInfo> tiles,
582 std::span<const gfx::TileInfo> tiles,
589 std::span<const gfx::TileInfo> tiles,
596 std::span<const gfx::TileInfo> tiles,
603 std::span<const gfx::TileInfo> tiles,
610 std::span<const gfx::TileInfo> tiles,
617 std::span<const gfx::TileInfo> tiles,
624 std::span<const gfx::TileInfo> tiles,
631 std::span<const gfx::TileInfo> tiles,
638 std::span<const gfx::TileInfo> tiles,
645 std::span<const gfx::TileInfo> tiles,
652 std::span<const gfx::TileInfo> tiles,
659 std::span<const gfx::TileInfo> tiles,
666 std::span<const gfx::TileInfo> tiles,
673 std::span<const gfx::TileInfo> tiles,
680 std::span<const gfx::TileInfo> tiles,
687 std::span<const gfx::TileInfo> tiles,
694 std::span<const gfx::TileInfo> tiles,
701 std::span<const gfx::TileInfo> tiles,
708 std::span<const gfx::TileInfo> tiles,
715 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
721 std::span<const gfx::TileInfo> tiles,
728 std::span<const gfx::TileInfo> tiles,
735 std::span<const gfx::TileInfo> tiles,
742 std::span<const gfx::TileInfo> tiles,
749 std::span<const gfx::TileInfo> tiles,
756 std::span<const gfx::TileInfo> tiles,
763 std::span<const gfx::TileInfo> tiles,
770 std::span<const gfx::TileInfo> tiles,
777 std::span<const gfx::TileInfo> tiles,
784 std::span<const gfx::TileInfo> tiles,
791 std::span<const gfx::TileInfo> tiles,
798 std::span<const gfx::TileInfo> tiles,
805 std::span<const gfx::TileInfo> tiles,
812 std::span<const gfx::TileInfo> tiles,
819 std::span<const gfx::TileInfo> tiles,
826 std::span<const gfx::TileInfo> tiles,
838 std::span<const gfx::TileInfo> tiles,
846 std::span<const gfx::TileInfo> tiles,
855 std::span<const gfx::TileInfo> tiles,
863 std::span<const gfx::TileInfo> tiles,
871 std::span<const gfx::TileInfo> tiles,
879 std::span<const gfx::TileInfo> tiles,
887 std::span<const gfx::TileInfo> tiles,
895 std::span<const gfx::TileInfo> tiles,
903 std::span<const gfx::TileInfo> tiles,
915 std::span<const gfx::TileInfo> tiles,
923 std::span<const gfx::TileInfo> tiles,
931 std::span<const gfx::TileInfo> tiles,
939 std::span<const gfx::TileInfo> tiles,
947 std::span<const gfx::TileInfo> tiles,
955 std::span<const gfx::TileInfo> tiles,
963 std::span<const gfx::TileInfo> tiles,
971 std::span<const gfx::TileInfo> tiles,
979 std::span<const gfx::TileInfo> tiles,
987 std::span<const gfx::TileInfo> tiles,
999 std::span<const gfx::TileInfo> tiles,
1007 std::span<const gfx::TileInfo> tiles,
1015 std::span<const gfx::TileInfo> tiles,
1023 std::span<const gfx::TileInfo> tiles,
1035 std::span<const gfx::TileInfo> tiles,
1043 std::span<const gfx::TileInfo> tiles,
1051 std::span<const gfx::TileInfo> tiles,
1059 std::span<const gfx::TileInfo> tiles,
1071 std::span<const gfx::TileInfo> tiles,
1079 std::span<const gfx::TileInfo> tiles,
1087 std::span<const gfx::TileInfo> tiles,
1095 std::span<const gfx::TileInfo> tiles,
1103 std::span<const gfx::TileInfo> tiles,
1111 std::span<const gfx::TileInfo> tiles,
1119 std::span<const gfx::TileInfo> tiles,
1127 std::span<const gfx::TileInfo> tiles,
1135 std::span<const gfx::TileInfo> tiles,
1143 std::span<const gfx::TileInfo> tiles,
1151 std::span<const gfx::TileInfo> tiles,
1159 std::span<const gfx::TileInfo> tiles,
1167 std::span<const gfx::TileInfo> tiles,
1175 std::span<const gfx::TileInfo> tiles,
1185 std::span<const gfx::TileInfo> tiles,
1193 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1200 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1207 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1214 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1221 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1228 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1235 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1242 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1249 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1256 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1263 std::span<const gfx::TileInfo> tiles,
const DungeonState* state) {
1270 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,
1287 int size = obj.
size_ & 0x0F;
1288 int count = (size + 1) * 2;
1290 if (tiles.size() < 5)
1294 for (
int row = 0; row < 5; row++) {
1299 for (
int s = 0; s < count; s++) {
1300 int col_x = obj.
x_ + 1 + s;
1301 for (
int row = 0; row < 5; row++) {
1303 size_t tile_idx = std::min(
size_t(5 + row), tiles.size() - 1);
1304 self->
WriteTile8(bg, col_x, obj.
y_ + row, tiles[tile_idx]);
1309 int last_x = obj.
x_ + 1 + count;
1310 for (
int row = 0; row < 5; row++) {
1311 size_t tile_idx = std::min(
size_t(10 + row), tiles.size() - 1);
1312 self->
WriteTile8(bg, last_x, obj.
y_ + row, tiles[tile_idx]);
1320 std::span<const gfx::TileInfo> tiles,
1322 int size = obj.
size_ & 0x0F;
1323 int count = (size + 1) * 2;
1325 if (tiles.size() < 3)
1329 for (
int row = 0; row < 3; row++) {
1334 for (
int s = 0; s < count; s++) {
1335 int col_x = obj.
x_ + 1 + s;
1336 for (
int row = 0; row < 3; row++) {
1342 int last_x = obj.
x_ + 1 + count;
1343 for (
int row = 0; row < 3; row++) {
1344 size_t tile_idx = std::min(
size_t(3 + row), tiles.size() - 1);
1345 self->
WriteTile8(bg, last_x, obj.
y_ + row, tiles[tile_idx]);
1354 std::span<const gfx::TileInfo> tiles,
1364 std::span<const gfx::TileInfo> tiles,
1374 std::span<const gfx::TileInfo> tiles,
1384 std::span<const gfx::TileInfo> tiles,
1389 auto ensure_index = [
this](
size_t index) {
1393 std::span<const gfx::TileInfo> tiles,
1405 std::span<const gfx::TileInfo> tiles,
1414 std::span<const gfx::TileInfo> tiles,
1425 std::span<const gfx::TileInfo> tiles,
1450 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: idx=%d type=%d dir=%d pos=%d",
1451 door_index,
static_cast<int>(door.
type),
1455 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: SKIPPED - rom=%p loaded=%d gfx=%p",
1461 auto& bitmap = bg1.
bitmap();
1462 if (!bitmap.is_active() || bitmap.width() == 0) {
1464 "DrawDoor: SKIPPED - bitmap not active or zero width");
1472 int door_height = dims.height_tiles;
1474 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: tile_pos=(%d,%d) dims=%dx%d", tile_x,
1475 tile_y, door_width, door_height);
1484 int offset_table_addr = 0;
1501 int type_value =
static_cast<int>(door.
type);
1502 int type_index = type_value / 2;
1505 int table_entry_addr = offset_table_addr + (type_index * 2);
1506 if (table_entry_addr + 1 >=
static_cast<int>(
rom_->
size())) {
1512 const auto& rom_data =
rom_->
data();
1513 uint16_t tile_offset =
1514 rom_data[table_entry_addr] | (rom_data[table_entry_addr + 1] << 8);
1517 constexpr int kRoomDrawObjectDataBase = 0x1B52;
1518 int tile_data_addr = kRoomDrawObjectDataBase + tile_offset;
1522 "DrawDoor: offset_table=0x%X type_idx=%d tile_offset=0x%X tile_addr=0x%X",
1523 offset_table_addr, type_index, tile_offset, tile_data_addr);
1526 int tiles_per_door = door_width * door_height;
1527 int data_size = tiles_per_door * 2;
1528 if (tile_data_addr < 0 ||
1529 tile_data_addr + data_size >
static_cast<int>(
rom_->
size())) {
1531 "DrawDoor: INVALID ADDRESS - falling back to indicator");
1540 LOG_DEBUG(
"ObjectDrawer",
"DrawDoor: Reading %d tiles from 0x%X",
1541 tiles_per_door, tile_data_addr);
1545 int bitmap_width = bitmap.width();
1547 for (
int dx = 0; dx < door_width; dx++) {
1548 for (
int dy = 0; dy < door_height; dy++) {
1549 int addr = tile_data_addr + (tile_idx * 2);
1550 uint16_t tile_word = rom_data[addr] | (rom_data[addr + 1] << 8);
1553 int pixel_x = (tile_x + dx) * 8;
1554 int pixel_y = (tile_y + dy) * 8;
1558 "DrawDoor: tile[%d] word=0x%04X id=%d pal=%d pixel=(%d,%d)",
1559 tile_idx, tile_word, tile_info.id_, tile_info.palette_,
1566 uint8_t priority = tile_info.over_ ? 1 : 0;
1567 const auto& bitmap_data = bitmap.vector();
1568 for (
int py = 0; py < 8; py++) {
1569 int dest_y = pixel_y + py;
1570 if (dest_y < 0 || dest_y >= bitmap.height())
1572 for (
int px = 0; px < 8; px++) {
1573 int dest_x = pixel_x + px;
1574 if (dest_x < 0 || dest_x >= bitmap_width)
1576 int dest_index = dest_y * bitmap_width + dest_x;
1577 if (dest_index >= 0 &&
1578 dest_index <
static_cast<int>(coverage_buffer.size())) {
1579 coverage_buffer[dest_index] = 1;
1581 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
1582 bitmap_data[dest_index] != 255) {
1583 priority_buffer[dest_index] = priority;
1593 "DrawDoor: type=%s dir=%s pos=%d at tile(%d,%d) size=%dx%d "
1594 "offset_table=0x%X tile_offset=0x%X tile_addr=0x%X",
1597 door.
position, tile_x, tile_y, door_width, door_height,
1598 offset_table_addr, tile_offset, tile_data_addr);
1602 int tile_y,
int width,
int height,
1607 auto& bitmap = bg.
bitmap();
1673 int pixel_x = tile_x * 8;
1674 int pixel_y = tile_y * 8;
1675 int pixel_width = width * 8;
1676 int pixel_height = height * 8;
1678 int bitmap_width = bitmap.width();
1679 int bitmap_height = bitmap.height();
1682 for (
int py = 0; py < pixel_height; py++) {
1683 for (
int px = 0; px < pixel_width; px++) {
1684 int dest_x = pixel_x + px;
1685 int dest_y = pixel_y + py;
1687 if (dest_x >= 0 && dest_x < bitmap_width && dest_y >= 0 &&
1688 dest_y < bitmap_height) {
1690 bool is_border = (px < 2 || px >= pixel_width - 2 || py < 2 ||
1691 py >= pixel_height - 2);
1692 uint8_t final_color = is_border ? (color_idx + 5) : color_idx;
1694 int offset = (dest_y * bitmap_width) + dest_x;
1695 bitmap.WriteToPixel(offset, final_color);
1698 offset <
static_cast<int>(coverage_buffer.size())) {
1699 coverage_buffer[offset] = 1;
1707 std::span<const gfx::TileInfo> tiles,
1714 bool is_open =
false;
1726 if (is_open && tiles.size() >= 8) {
1728 if (tiles.size() >= 8) {
1738 if (tiles.size() >= 4) {
1747 std::span<const gfx::TileInfo> tiles,
1752 LOG_DEBUG(
"ObjectDrawer",
"DrawNothing for object 0x%02X (logic/invisible)",
1758 std::span<const gfx::TileInfo> tiles,
1763 int size = obj.
size_;
1768 bool is_ceiling = (obj.
id_ == 0x00 || obj.
id_ == 0xB8 || obj.
id_ == 0xB9);
1769 if (is_ceiling && tiles.size() >= 4) {
1771 "Ceiling Draw: obj=0x%02X pos=(%d,%d) size=%d tiles=%zu", obj.
id_,
1772 obj.
x_, obj.
y_, size, tiles.size());
1773 LOG_DEBUG(
"ObjectDrawer",
" Tile IDs: [%d, %d, %d, %d]", tiles[0].id_,
1774 tiles[1].id_, tiles[2].id_, tiles[3].id_);
1775 LOG_DEBUG(
"ObjectDrawer",
" Palettes: [%d, %d, %d, %d]", tiles[0].palette_,
1776 tiles[1].palette_, tiles[2].palette_, tiles[3].palette_);
1780 "DrawRightwards2x2: obj=%04X pos=(%d,%d) size=%d tiles=%zu",
1781 obj.
id_, obj.
x_, obj.
y_, size, tiles.size());
1783 for (
int s = 0; s < size; s++) {
1784 if (tiles.size() >= 4) {
1797 "DrawRightwards2x2: SKIPPING - tiles.size()=%zu < 4",
1805 std::span<const gfx::TileInfo> tiles,
1810 int size = obj.
size_;
1815 "Wall Draw 2x4: obj=0x%03X pos=(%d,%d) size=%d tiles=%zu", obj.
id_,
1816 obj.
x_, obj.
y_, size, tiles.size());
1818 "DrawRightwards2x4: obj=%04X pos=(%d,%d) size=%d tiles=%zu",
1819 obj.
id_, obj.
x_, obj.
y_, size, tiles.size());
1821 for (
int s = 0; s < size; s++) {
1822 if (tiles.size() >= 8) {
1837 }
else if (tiles.size() >= 4) {
1849 std::span<const gfx::TileInfo> tiles,
1854 int size = obj.
size_ & 0x0F;
1855 int count = size + 1;
1858 "DrawRightwards2x4_1to16: obj=%04X pos=(%d,%d) size=%d count=%d "
1860 obj.
id_, obj.
x_, obj.
y_, size, count, tiles.size());
1862 for (
int s = 0; s < count; s++) {
1863 if (tiles.size() >= 8) {
1878 }
else if (tiles.size() >= 4) {
1890 std::span<const gfx::TileInfo> tiles,
1899 std::span<const gfx::TileInfo> tiles,
1903 int size = obj.
size_ & 0x0F;
1907 int count = size + 1;
1909 for (
int s = 0; s < count; s++) {
1910 if (tiles.size() >= 4) {
1923 std::span<const gfx::TileInfo> tiles,
1928 int size = obj.
size_ & 0x0F;
1933 int count = size + 7;
1935 if (tiles.size() < 5)
1938 for (
int s = 0; s < count; s++) {
1942 int tile_x = obj.
x_ + s;
1943 int tile_y = obj.
y_ - s;
1945 WriteTile8(bg, tile_x, tile_y + 0, tiles[0]);
1946 WriteTile8(bg, tile_x, tile_y + 1, tiles[1]);
1947 WriteTile8(bg, tile_x, tile_y + 2, tiles[2]);
1948 WriteTile8(bg, tile_x, tile_y + 3, tiles[3]);
1949 WriteTile8(bg, tile_x, tile_y + 4, tiles[4]);
1955 std::span<const gfx::TileInfo> tiles,
1960 int size = obj.
size_ & 0x0F;
1965 int count = size + 7;
1967 if (tiles.size() < 5)
1970 for (
int s = 0; s < count; s++) {
1974 int tile_x = obj.
x_ + s;
1975 int tile_y = obj.
y_ + s;
1977 WriteTile8(bg, tile_x, tile_y + 0, tiles[0]);
1978 WriteTile8(bg, tile_x, tile_y + 1, tiles[1]);
1979 WriteTile8(bg, tile_x, tile_y + 2, tiles[2]);
1980 WriteTile8(bg, tile_x, tile_y + 3, tiles[3]);
1981 WriteTile8(bg, tile_x, tile_y + 4, tiles[4]);
1987 std::span<const gfx::TileInfo> tiles,
1994 int size = obj.
size_ & 0x0F;
1995 int count = size + 6;
1997 if (tiles.size() < 5)
2000 for (
int s = 0; s < count; s++) {
2001 int tile_x = obj.
x_ + s;
2002 int tile_y = obj.
y_ - s;
2004 WriteTile8(bg, tile_x, tile_y + 0, tiles[0]);
2005 WriteTile8(bg, tile_x, tile_y + 1, tiles[1]);
2006 WriteTile8(bg, tile_x, tile_y + 2, tiles[2]);
2007 WriteTile8(bg, tile_x, tile_y + 3, tiles[3]);
2008 WriteTile8(bg, tile_x, tile_y + 4, tiles[4]);
2015 std::span<const gfx::TileInfo> tiles,
2022 int size = obj.
size_ & 0x0F;
2023 int count = size + 6;
2025 if (tiles.size() < 5)
2028 for (
int s = 0; s < count; s++) {
2029 int tile_x = obj.
x_ + s;
2030 int tile_y = obj.
y_ + s;
2032 WriteTile8(bg, tile_x, tile_y + 0, tiles[0]);
2033 WriteTile8(bg, tile_x, tile_y + 1, tiles[1]);
2034 WriteTile8(bg, tile_x, tile_y + 2, tiles[2]);
2035 WriteTile8(bg, tile_x, tile_y + 3, tiles[3]);
2036 WriteTile8(bg, tile_x, tile_y + 4, tiles[4]);
2043 std::span<const gfx::TileInfo> tiles,
2052 if (tiles.size() >= 16) {
2055 for (
int xx = 0; xx < 4; xx++) {
2056 for (
int yy = 0; yy < 4; yy++) {
2060 }
else if (tiles.size() >= 8) {
2064 for (
int xx = 0; xx < 2; xx++) {
2065 for (
int yy = 0; yy < 4; yy++) {
2069 }
else if (tiles.size() >= 4) {
2072 for (
int xx = 0; xx < 2; xx++) {
2073 for (
int yy = 0; yy < 2; yy++) {
2082 std::span<const gfx::TileInfo> tiles,
2086 int size = obj.
size_ & 0x0F;
2088 if (tiles.size() >= 9) {
2089 auto draw_column = [&](
int x,
int base) {
2096 draw_column(obj.
x_, 0);
2099 int mid_cols = (size + 1) * 2;
2100 for (
int s = 0; s < mid_cols; s++) {
2101 draw_column(obj.
x_ + 1 + s, 3);
2105 draw_column(obj.
x_ + 1 + mid_cols, 6);
2110 int count = (size * 2) + 1;
2111 for (
int s = 0; s < count; s++) {
2112 if (tiles.size() >= 2) {
2121 std::span<const gfx::TileInfo> tiles,
2127 int size = obj.
size_ & 0x0F;
2128 int count = size + 2;
2130 if (tiles.size() < 3)
2140 for (
int s = 0; s < count; s++) {
2151 std::span<const gfx::TileInfo> tiles,
2155 int size = obj.
size_ & 0x0F;
2156 int count = size + 21;
2158 if (tiles.size() < 3)
2165 for (
int s = 0; s < count; s++) {
2175 std::span<const gfx::TileInfo> tiles,
2181 int size = obj.
size_ & 0x0F;
2182 int count = size + 1;
2184 if (tiles.size() < 3)
2194 for (
int s = 0; s < count; s++) {
2205 std::span<const gfx::TileInfo> tiles,
2208 int size = obj.
size_ & 0x0F;
2211 int count = size + 10;
2213 for (
int s = 0; s < count; s++) {
2214 if (tiles.size() >= 2) {
2224 const std::span<const gfx::TileInfo> tiles,
2227 int size = obj.
size_ & 0x0F;
2230 int count = size + 10;
2232 for (
int s = 0; s < count; s++) {
2233 if (tiles.size() >= 2) {
2242 std::span<const gfx::TileInfo> tiles,
2246 if (tiles.size() >= 1) {
2254 std::span<const gfx::TileInfo> tiles,
2257 int size = obj.
size_ & 0x0F;
2260 int count = size + 1;
2263 if (obj.
id_ == 0xBA && tiles.size() >= 16) {
2265 "Large Ceiling Draw: obj=0x%02X pos=(%d,%d) size=%d tiles=%zu",
2266 obj.
id_, obj.
x_, obj.
y_, size, tiles.size());
2267 LOG_DEBUG(
"ObjectDrawer",
" First 4 Tile IDs: [%d, %d, %d, %d]",
2268 tiles[0].id_, tiles[1].id_, tiles[2].id_, tiles[3].id_);
2269 LOG_DEBUG(
"ObjectDrawer",
" First 4 Palettes: [%d, %d, %d, %d]",
2270 tiles[0].palette_, tiles[1].palette_, tiles[2].palette_,
2274 for (
int s = 0; s < count; s++) {
2275 if (tiles.size() >= 16) {
2278 for (
int x = 0; x < 4; ++x) {
2279 for (
int y = 0; y < 4; ++y) {
2280 WriteTile8(bg, obj.
x_ + (s * 4) + x, obj.
y_ + y, tiles[x * 4 + y]);
2289 std::span<const gfx::TileInfo> tiles,
2292 int size = obj.
size_ & 0x0F;
2295 int count = size + 4;
2297 for (
int s = 0; s < count; s++) {
2298 if (tiles.size() >= 1) {
2307 std::span<const gfx::TileInfo> tiles,
2313 if (state && state->IsDoorSwitchActive(
room_id_)) {
2315 if (tiles.size() >= 2) {
2320 if (tiles.size() > tile_index) {
2327 std::span<const gfx::TileInfo> tiles,
2330 int size = obj.
size_ & 0x0F;
2333 int count = size + 1;
2335 for (
int s = 0; s < count; s++) {
2336 if (tiles.size() >= 16) {
2338 for (
int x = 0; x < 4; ++x) {
2339 for (
int y = 0; y < 4; ++y) {
2340 WriteTile8(bg, obj.
x_ + (s * 6) + x, obj.
y_ + y, tiles[x * 4 + y]);
2349 std::span<const gfx::TileInfo> tiles,
2353 int size = obj.
size_ & 0x0F;
2356 int count = size + 1;
2358 for (
int s = 0; s < count; s++) {
2359 if (tiles.size() >= 6) {
2361 for (
int x = 0; x < 2; ++x) {
2362 for (
int y = 0; y < 3; ++y) {
2363 WriteTile8(bg, obj.
x_ + (s * 4) + x, obj.
y_ + y, tiles[x * 3 + y]);
2372 std::span<const gfx::TileInfo> tiles,
2379 int size = obj.
size_ & 0x0F;
2382 int count = size + 1;
2384 for (
int s = 0; s < count; s++) {
2385 if (tiles.size() >= 8) {
2387 for (
int x = 0; x < 2; ++x) {
2388 for (
int y = 0; y < 4; ++y) {
2389 WriteTile8(bg, obj.
x_ + (s * 6) + x, obj.
y_ + y, tiles[x * 4 + y]);
2398 std::span<const gfx::TileInfo> tiles,
2404 int size = obj.
size_ & 0x0F;
2407 int count = size + 1;
2409 for (
int s = 0; s < count; s++) {
2410 if (tiles.size() >= 12) {
2413 for (
int x = 0; x < 4; ++x) {
2414 for (
int y = 0; y < 3; ++y) {
2415 WriteTile8(bg, obj.
x_ + (s * 8) + x, obj.
y_ + y, tiles[x * 3 + y]);
2424 std::span<const gfx::TileInfo> tiles,
2427 int size = obj.
size_ & 0x0F;
2428 int count = size + 1;
2430 if (tiles.size() < 4)
2433 for (
int s = 0; s < count; s++) {
2434 int base_x = obj.
x_ + (s * 4);
2452 std::span<const gfx::TileInfo> tiles,
2455 int size = obj.
size_ & 0x0F;
2458 int count = size + 1;
2460 for (
int s = 0; s < count; s++) {
2461 if (tiles.size() >= 4) {
2478 std::span<const gfx::TileInfo> tiles,
2484 int size = obj.
size_ & 0x0F;
2485 int count = size + 1;
2487 for (
int s = 0; s < count; s++) {
2488 if (tiles.size() >= 8) {
2506 std::span<const gfx::TileInfo> tiles,
2512 int size = obj.
size_ & 0x0F;
2513 int count = size + 1;
2515 if (tiles.size() < 8)
2518 for (
int s = 0; s < count; s++) {
2520 int base_x = obj.
x_ + (s * 12);
2521 for (
int row = 0; row < 8; row++) {
2529 std::span<const gfx::TileInfo> tiles,
2542 int size = obj.
size_ & 0x0F;
2543 int count = size + 1;
2545 if (tiles.size() < 12) {
2549 auto draw_column = [&](
int x,
int y,
const gfx::TileInfo& t0,
2559 for (
int s = 0; s < count; ++s) {
2560 int base_x = obj.
x_ + (s * 2);
2561 draw_column(base_x + 0, obj.
y_, tiles[0], tiles[1], tiles[2]);
2562 draw_column(base_x + 1, obj.
y_, tiles[3], tiles[4], tiles[5]);
2566 int right_base_x = obj.
x_ + (count * 2);
2567 draw_column(right_base_x + 0, obj.
y_, tiles[6], tiles[7], tiles[8]);
2568 draw_column(right_base_x + 1, obj.
y_, tiles[9], tiles[10], tiles[11]);
2575 std::span<const gfx::TileInfo> tiles,
2579 int size = obj.
size_ & 0x0F;
2580 int count = size + 2;
2582 for (
int s = 0; s < count; s++) {
2583 if (tiles.size() >= 1) {
2591 std::span<const gfx::TileInfo> tiles,
2594 int size = obj.
size_ & 0x0F;
2595 int count = size + 1;
2597 if (tiles.size() < 9)
2601 for (
int s = 0; s < count; s++) {
2602 int base_x = obj.
x_ + (s * 2);
2617 int right_x = obj.
x_ + (size * 2) + 3;
2625 std::span<const gfx::TileInfo> tiles,
2630 int size = obj.
size_ & 0x0F;
2631 int count = size + 1;
2633 for (
int s = 0; s < count; s++) {
2634 if (tiles.size() >= 16) {
2635 int base_x = obj.
x_ + (s * 6);
2637 for (
int col = 0; col < 4; col++) {
2638 for (
int row = 0; row < 4; row++) {
2639 int tile_idx = col * 4 + row;
2640 WriteTile8(bg, base_x + col, obj.
y_ + row, tiles[tile_idx]);
2649 std::span<const gfx::TileInfo> tiles,
2658 int size = obj.
size_ & 0x0F;
2662 if (tiles.size() < 15)
2679 for (
int s = 0; s < middle_count; s++) {
2699 std::span<const gfx::TileInfo> tiles,
2704 int size = obj.
size_ & 0x0F;
2705 int count = size + 1;
2707 for (
int s = 0; s < count; s++) {
2708 if (tiles.size() >= 4) {
2709 int base_x = obj.
x_ + (s * 4);
2725 std::span<const gfx::TileInfo> tiles,
2729 int size = obj.
size_;
2733 for (
int s = 0; s < size; s++) {
2734 if (tiles.size() >= 4) {
2752 std::span<const gfx::TileInfo> tiles,
2756 int size = obj.
size_;
2761 "Wall Draw 4x2 Vertical: obj=0x%03X pos=(%d,%d) size=%d tiles=%zu",
2762 obj.
id_, obj.
x_, obj.
y_, size, tiles.size());
2764 for (
int s = 0; s < size; s++) {
2765 if (tiles.size() >= 8) {
2777 }
else if (tiles.size() >= 4) {
2789 std::span<const gfx::TileInfo> tiles,
2798 std::span<const gfx::TileInfo> tiles,
2803 int size = obj.
size_ & 0x0F;
2806 int count = size + 1;
2808 for (
int s = 0; s < count; s++) {
2809 if (tiles.size() >= 8) {
2812 const int base_y = obj.
y_ + (s * 6);
2827 std::span<const gfx::TileInfo> tiles,
2830 int size = obj.
size_ & 0x0F;
2833 int count = size + 1;
2835 for (
int s = 0; s < count; s++) {
2836 if (tiles.size() >= 4) {
2853 std::span<const gfx::TileInfo> tiles,
2856 int size = obj.
size_ & 0x0F;
2857 int count = size + 1;
2859 if (tiles.size() < 3)
2866 for (
int s = 0; s < count; s++) {
2876 std::span<const gfx::TileInfo> tiles,
2882 int size = obj.
size_ & 0x0F;
2883 int count = size + 21;
2885 if (tiles.size() < 3)
2888 int tile_y = obj.
y_;
2895 for (
int s = 0; s < count; s++) {
2906 std::span<const gfx::TileInfo> tiles,
2909 int size = obj.
size_ & 0x0F;
2912 int count = size + 1;
2914 for (
int s = 0; s < count; s++) {
2915 if (tiles.size() >= 1) {
2924 std::span<const gfx::TileInfo> tiles,
2927 int size = obj.
size_ & 0x0F;
2930 int count = size + 10;
2932 for (
int s = 0; s < count; s++) {
2933 if (tiles.size() >= 2) {
2943 std::span<const gfx::TileInfo> tiles,
2946 int size = obj.
size_ & 0x0F;
2949 int count = size + 10;
2951 for (
int s = 0; s < count; s++) {
2952 if (tiles.size() >= 2) {
2964 std::span<const gfx::TileInfo> tiles,
2969 int size = obj.
size_ & 0x0F;
2970 int count = size + 1;
2972 for (
int s = 0; s < count; s++) {
2973 if (tiles.size() >= 16) {
2975 for (
int col = 0; col < 4; col++) {
2976 for (
int row = 0; row < 4; row++) {
2977 int tile_idx = col * 4 + row;
2978 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 4) + row, tiles[tile_idx]);
2987 std::span<const gfx::TileInfo> tiles,
2991 int size = obj.
size_ & 0x0F;
2992 int count = size + 4;
2994 for (
int s = 0; s < count; s++) {
2995 if (tiles.size() >= 1) {
3003 std::span<const gfx::TileInfo> tiles,
3008 int size = obj.
size_ & 0x0F;
3009 int count = size + 1;
3011 for (
int s = 0; s < count; s++) {
3012 if (tiles.size() >= 16) {
3014 for (
int col = 0; col < 4; col++) {
3015 for (
int row = 0; row < 4; row++) {
3016 int tile_idx = col * 4 + row;
3017 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 6) + row, tiles[tile_idx]);
3026 std::span<const gfx::TileInfo> tiles,
3031 int size = obj.
size_ & 0x0F;
3032 int count = size + 1;
3034 for (
int s = 0; s < count; s++) {
3035 if (tiles.size() >= 8) {
3037 for (
int col = 0; col < 2; col++) {
3038 for (
int row = 0; row < 4; row++) {
3039 int tile_idx = col * 4 + row;
3040 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 6) + row, tiles[tile_idx]);
3049 std::span<const gfx::TileInfo> tiles,
3054 int size = obj.
size_ & 0x0F;
3055 int count = size + 1;
3057 for (
int s = 0; s < count; s++) {
3058 if (tiles.size() >= 12) {
3060 for (
int col = 0; col < 3; col++) {
3061 for (
int row = 0; row < 4; row++) {
3062 int tile_idx = col * 4 + row;
3063 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 8) + row, tiles[tile_idx]);
3072 std::span<const gfx::TileInfo> tiles,
3077 int size = obj.
size_ & 0x0F;
3078 int count = size + 1;
3080 for (
int s = 0; s < count; s++) {
3081 if (tiles.size() >= 4) {
3083 for (
int col = 0; col < 2; col++) {
3084 for (
int row = 0; row < 2; row++) {
3085 int tile_idx = col * 2 + row;
3096 std::span<const gfx::TileInfo> tiles,
3100 int size = obj.
size_ & 0x0F;
3101 int count = size + 2;
3103 for (
int s = 0; s < count; s++) {
3104 if (tiles.size() >= 1) {
3112 std::span<const gfx::TileInfo> tiles,
3117 int size = obj.
size_ & 0x0F;
3118 int count = size + 1;
3121 if (obj.
id_ == 0x80 && tiles.size() >= 8) {
3123 "Vertical Ceiling Draw: obj=0x%02X pos=(%d,%d) size=%d tiles=%zu",
3124 obj.
id_, obj.
x_, obj.
y_, size, tiles.size());
3125 LOG_DEBUG(
"ObjectDrawer",
" Tile IDs: [%d, %d, %d, %d, %d, %d, %d, %d]",
3126 tiles[0].id_, tiles[1].id_, tiles[2].id_, tiles[3].id_,
3127 tiles[4].id_, tiles[5].id_, tiles[6].id_, tiles[7].id_);
3128 LOG_DEBUG(
"ObjectDrawer",
" Palettes: [%d, %d, %d, %d, %d, %d, %d, %d]",
3129 tiles[0].palette_, tiles[1].palette_, tiles[2].palette_,
3130 tiles[3].palette_, tiles[4].palette_, tiles[5].palette_,
3131 tiles[6].palette_, tiles[7].palette_);
3134 for (
int s = 0; s < count; s++) {
3135 if (tiles.size() >= 8) {
3137 for (
int col = 0; col < 2; col++) {
3138 for (
int row = 0; row < 4; row++) {
3139 int tile_idx = col * 4 + row;
3154 std::span<const gfx::TileInfo> tiles,
3158 int size = obj.
size_ & 0x0F;
3159 int count = size + 1;
3161 for (
int s = 0; s < count; s++) {
3162 if (tiles.size() >= 12) {
3164 for (
int col = 0; col < 3; col++) {
3165 for (
int row = 0; row < 4; row++) {
3166 int tile_idx = col * 4 + row;
3167 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 6) + row, tiles[tile_idx]);
3176 std::span<const gfx::TileInfo> tiles,
3185 int size = obj.
size_ & 0x0F;
3186 int middle_count = size + 1;
3188 if (tiles.size() < 12)
3203 for (
int s = 0; s < middle_count; s++) {
3222 std::span<const gfx::TileInfo> tiles,
3226 int size = obj.
size_ & 0x0F;
3227 int count = size + 1;
3229 for (
int s = 0; s < count; s++) {
3230 if (tiles.size() >= 4) {
3231 int base_y = obj.
y_ + (s * 4);
3243 std::span<const gfx::TileInfo> tiles,
3248 int size = obj.
size_ & 0x0F;
3249 int count = size + 1;
3251 for (
int s = 0; s < count; s++) {
3252 if (tiles.size() >= 18) {
3254 for (
int col = 0; col < 3; col++) {
3255 for (
int row = 0; row < 6; row++) {
3256 int tile_idx = col * 6 + row;
3257 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 6) + row, tiles[tile_idx]);
3266 std::span<const gfx::TileInfo> tiles,
3270 int size = obj.
size_ & 0x0F;
3271 int count = size + 1;
3273 for (
int s = 0; s < count; s++) {
3274 if (tiles.size() >= 6) {
3276 for (
int col = 0; col < 2; col++) {
3277 for (
int row = 0; row < 3; row++) {
3278 int tile_idx = col * 3 + row;
3279 WriteTile8(bg, obj.
x_ + col, obj.
y_ + (s * 3) + row, tiles[tile_idx]);
3288 std::span<const gfx::TileInfo> tiles,
3292 int size = obj.
size_ & 0x0F;
3293 int count = size + 1;
3295 for (
int s = 0; s < count; s++) {
3296 if (tiles.size() >= 4) {
3297 int base_y = obj.
y_ + (s * 2);
3309 std::span<const gfx::TileInfo> tiles,
3313 int size = obj.
size_ & 0x0F;
3314 int count = size + 1;
3316 for (
int s = 0; s < count; s++) {
3317 if (tiles.size() >= 4) {
3318 int base_y = obj.
y_ + (s * 2);
3330 std::span<const gfx::TileInfo> tiles,
3334 int size = obj.
size_ & 0x0F;
3335 int count = size + 8;
3337 for (
int s = 0; s < count; s++) {
3338 if (tiles.size() >= 1) {
3346 std::span<const gfx::TileInfo> tiles,
3350 int size = obj.
size_ & 0x0F;
3351 int count = size + 1;
3353 for (
int s = 0; s < count; s++) {
3354 if (tiles.size() >= 4) {
3355 int base_x = obj.
x_ + (s * 2);
3367 std::span<const gfx::TileInfo> tiles,
3371 int size = obj.
size_ & 0x0F;
3372 int count = size + 1;
3374 for (
int s = 0; s < count; s++) {
3375 if (tiles.size() >= 4) {
3376 int base_x = obj.
x_ + (s * 2);
3392 std::span<const gfx::TileInfo> tiles,
3397 int count = (obj.
size_ & 0x0F) + 4;
3399 if (tiles.empty()) {
3400 LOG_DEBUG(
"ObjectDrawer",
"DiagonalCeilingTopLeft: No tiles for obj 0x%02X",
3406 "DiagonalCeilingTopLeft: obj=0x%02X pos=(%d,%d) size=%d count=%d",
3410 int tiles_in_row = count;
3411 for (
int row = 0; row < count && tiles_in_row > 0; row++) {
3412 for (
int col = 0; col < tiles_in_row; col++) {
3421 std::span<const gfx::TileInfo> tiles,
3426 int count = (obj.
size_ & 0x0F) + 4;
3432 for (
int row = 0; row < count; row++) {
3433 int tiles_in_row = row + 1;
3434 for (
int col = 0; col < tiles_in_row; col++) {
3442 std::span<const gfx::TileInfo> tiles,
3447 int count = (obj.
size_ & 0x0F) + 4;
3453 int tiles_in_row = count;
3454 for (
int row = 0; row < count && tiles_in_row > 0; row++) {
3455 for (
int col = 0; col < tiles_in_row; col++) {
3464 std::span<const gfx::TileInfo> tiles,
3469 int count = (obj.
size_ & 0x0F) + 4;
3475 int tiles_in_row = count;
3476 for (
int row = 0; row < count && tiles_in_row > 0; row++) {
3477 for (
int col = 0; col < tiles_in_row; col++) {
3490 std::span<const gfx::TileInfo> tiles) {
3506 if (tiles.size() < 16)
3510 int width = (obj.
size_ & 0x0F) + 4;
3511 int height = ((obj.
size_ >> 4) & 0x0F) + 1;
3514 "DrawClosedChestPlatform: obj=0x%03X pos=(%d,%d) size=0x%02X "
3515 "width=%d height=%d",
3520 size_t tile_idx = 0;
3521 for (
int row = 0; row < height * 3; ++row) {
3522 for (
int col = 0; col < width * 2; ++col) {
3524 tiles[tile_idx % tiles.size()]);
3532 std::span<const gfx::TileInfo> tiles) {
3535 if (tiles.size() < 6)
3538 int count = ((obj.
size_ >> 4) & 0x0F) + 4;
3541 for (
int row = 0; row < count; ++row) {
3542 for (
int col = 0; col < 3; ++col) {
3543 size_t tile_idx = (row * 3 + col) % tiles.size();
3551 std::span<const gfx::TileInfo> tiles) {
3554 if (tiles.size() < 6)
3557 int count = ((obj.
size_ >> 4) & 0x0F) + 4;
3559 for (
int row = 0; row < count; ++row) {
3560 for (
int col = 0; col < 3; ++col) {
3561 size_t tile_idx = (row * 3 + col) % tiles.size();
3569 std::span<const gfx::TileInfo> tiles) {
3585 if (tiles.size() < 8)
3589 int width = (obj.
size_ & 0x0F) + 1;
3592 int segments = ((obj.
size_ >> 4) & 0x0F) * 2 + 5;
3595 "DrawOpenChestPlatform: obj=0x%03X pos=(%d,%d) size=0x%02X "
3596 "width=%d segments=%d",
3604 int tile_offset = 0;
3605 for (
int seg = 0; seg < segments; ++seg) {
3606 for (
int col = 0; col < width; ++col) {
3607 size_t tile_idx = tile_offset % tiles.size();
3616 for (
int col = 0; col < width; ++col) {
3617 size_t tile_idx = tile_offset % tiles.size();
3618 WriteTile8(bg, obj.
x_ + col, obj.
y_ + segments, tiles[tile_idx]);
3622 for (
int col = 0; col < width; ++col) {
3623 size_t tile_idx = tile_offset % tiles.size();
3624 WriteTile8(bg, obj.
x_ + col, obj.
y_ + segments + 1, tiles[tile_idx]);
3634 int tile_y,
int pixel_width,
3636 auto& bitmap = bg1.
bitmap();
3637 if (!bitmap.is_active() || bitmap.width() == 0) {
3638 LOG_DEBUG(
"ObjectDrawer",
"MarkBG1Transparent: Bitmap not ready, skipping");
3642 int start_px = tile_x * 8;
3643 int start_py = tile_y * 8;
3644 int canvas_width = bitmap.width();
3645 int canvas_height = bitmap.height();
3646 auto& data = bitmap.mutable_data();
3648 int pixels_marked = 0;
3652 for (
int py = start_py; py < start_py + pixel_height && py < canvas_height;
3656 for (
int px = start_px; px < start_px + pixel_width && px < canvas_width;
3660 int idx = py * canvas_width + px;
3661 if (idx >= 0 && idx <
static_cast<int>(data.size())) {
3667 bitmap.set_modified(
true);
3670 "MarkBG1Transparent: Marked %d pixels at tile(%d,%d) pixel(%d,%d) "
3672 pixels_marked, tile_x, tile_y, start_px, start_py, pixel_width,
3686 auto& bitmap = bg.
bitmap();
3687 if (!bitmap.is_active() || bitmap.width() == 0) {
3697 LOG_DEBUG(
"ObjectDrawer",
"ERROR: No graphics data available");
3713 uint8_t priority = tile_info.
over_ ? 1 : 0;
3714 int pixel_x = tile_x * 8;
3715 int pixel_y = tile_y * 8;
3717 int width = bitmap.width();
3720 const auto& bitmap_data = bitmap.vector();
3721 for (
int py = 0; py < 8; py++) {
3722 int dest_y = pixel_y + py;
3723 if (dest_y < 0 || dest_y >= bitmap.height())
3726 for (
int px = 0; px < 8; px++) {
3727 int dest_x = pixel_x + px;
3728 if (dest_x < 0 || dest_x >= width)
3731 int dest_index = dest_y * width + dest_x;
3734 if (dest_index >= 0 &&
3735 dest_index <
static_cast<int>(coverage_buffer.size())) {
3736 coverage_buffer[dest_index] = 1;
3741 if (dest_index <
static_cast<int>(bitmap_data.size()) &&
3742 bitmap_data[dest_index] != 255) {
3743 priority_buffer[dest_index] = priority;
3745 priority_buffer[dest_index] = 0xFF;
3752 return tile_x >= 0 && tile_x < kMaxTilesX && tile_y >= 0 &&
3758 int pixel_y,
const uint8_t* tiledata) {
3766 LOG_DEBUG(
"ObjectDrawer",
"ERROR: Invalid bitmap - active=%d, size=%dx%d",
3775 constexpr int kGfxBufferSize = 0x10000;
3776 constexpr int kMaxTileRow = 63;
3778 int tile_col = tile_info.
id_ % 16;
3779 int tile_row = tile_info.
id_ / 16;
3782 if (tile_row > kMaxTileRow) {
3783 LOG_DEBUG(
"ObjectDrawer",
"Tile ID 0x%03X out of bounds (row %d > %d)",
3784 tile_info.
id_, tile_row, kMaxTileRow);
3788 int tile_base_x = tile_col * 8;
3793 static int draw_debug_count = 0;
3794 if (draw_debug_count < 5) {
3795 int sample_index = tile_base_y + tile_base_x;
3797 "DrawTile: id=%d (col=%d,row=%d) gfx_offset=%d (0x%04X)",
3798 tile_info.
id_, tile_col, tile_row, sample_index, sample_index);
3811 uint8_t pal = tile_info.
palette_ & 0x07;
3812 uint8_t palette_offset;
3813 if (pal >= 2 && pal <= 7) {
3815 palette_offset = (pal - 2) * 16;
3827 bool any_pixels_changed =
false;
3829 for (
int py = 0; py < 8; py++) {
3833 for (
int px = 0; px < 8; px++) {
3839 int src_index = (src_row * 128) + src_col + tile_base_x + tile_base_y;
3840 uint8_t pixel = tiledata[src_index];
3841 uint8_t out_pixel = 255;
3844 out_pixel =
static_cast<uint8_t
>(pixel + palette_offset);
3847 int dest_x = pixel_x + px;
3848 int dest_y = pixel_y + py;
3849 if (dest_x < 0 || dest_x >= bitmap.
width() || dest_y < 0 ||
3850 dest_y >= bitmap.
height()) {
3854 int dest_index = dest_y * bitmap.
width() + dest_x;
3855 if (dest_index < 0 ||
3856 dest_index >=
static_cast<int>(bitmap.
mutable_data().size())) {
3861 if (dst != out_pixel) {
3863 any_pixels_changed =
true;
3868 if (any_pixels_changed) {
3878 return absl::FailedPreconditionError(
"ROM not loaded");
3884 if (base < 0 || base + 7 >=
static_cast<int>(rom_data.size())) {
3885 return absl::OutOfRangeError(
3886 absl::StrFormat(
"RoomDrawObjectData 2x2 out of range: base=0x%X",
3890 auto read_word = [&](
int off) -> uint16_t {
3891 return static_cast<uint16_t
>(rom_data[off]) |
3892 (
static_cast<uint16_t
>(rom_data[off + 1]) << 8);
3895 const uint16_t w0 = read_word(base + 0);
3896 const uint16_t w1 = read_word(base + 2);
3897 const uint16_t w2 = read_word(base + 4);
3898 const uint16_t w3 = read_word(base + 6);
3906 RoomObject trace_obj(
static_cast<int16_t
>(object_id),
3907 static_cast<uint8_t
>(tile_x),
3908 static_cast<uint8_t
>(tile_y), 0,
3909 static_cast<uint8_t
>(layer));
3916 WriteTile8(target_bg, tile_x + 0, tile_y + 0, t0);
3917 WriteTile8(target_bg, tile_x + 0, tile_y + 1, t1);
3918 WriteTile8(target_bg, tile_x + 1, tile_y + 0, t2);
3919 WriteTile8(target_bg, tile_x + 1, tile_y + 1, t3);
3921 return absl::OkStatus();
3930 std::span<const gfx::TileInfo> tiles,
3947 int length = (obj.
size_ & 0x0F) + 1;
3948 int obj_subid = obj.
id_ & 0x0F;
3952 switch (obj_subid) {
4008 for (
int i = 0; i < length; ++i) {
4009 int tile_idx = i % tiles.size();
4010 WriteTile8(bg, obj.
x_ + (i * dx), obj.
y_ + (i * dy), tiles[tile_idx]);
4016 std::span<const gfx::TileInfo> tiles,
4022 if (tiles.size() >= 4) {
4032 std::span<const gfx::TileInfo> tiles,
4036 if (tiles.size() >= 16) {
4038 }
else if (tiles.size() >= 8) {
4041 for (
int xx = 0; xx < 2; xx++) {
4042 for (
int yy = 0; yy < 4; yy++) {
4046 }
else if (tiles.size() >= 4) {
4054 std::span<const gfx::TileInfo> tiles,
4059 if (tiles.size() >= 16) {
4061 }
else if (tiles.size() >= 12) {
4064 for (
int xx = 0; xx < 3; xx++) {
4065 for (
int yy = 0; yy < 4; yy++) {
4069 }
else if (tiles.size() >= 8) {
4072 for (
int xx = 0; xx < 2; xx++) {
4073 for (
int yy = 0; yy < 4; yy++) {
4077 }
else if (tiles.size() >= 4) {
4084 std::span<const gfx::TileInfo> tiles,
4089 if (tiles.size() >= 16) {
4091 }
else if (tiles.size() >= 12) {
4094 for (
int xx = 0; xx < 4; xx++) {
4095 for (
int yy = 0; yy < 3; yy++) {
4099 }
else if (tiles.size() >= 8) {
4102 for (
int xx = 0; xx < 2; xx++) {
4103 for (
int yy = 0; yy < 4; yy++) {
4107 }
else if (tiles.size() >= 4) {
4114 std::span<const gfx::TileInfo> tiles,
4115 int width,
int height) {
4117 if (tiles.size() >=
static_cast<size_t>(width * height)) {
4118 for (
int y = 0; y < height; ++y) {
4119 for (
int x = 0; x < width; ++x) {
4131 std::span<const gfx::TileInfo> tiles,
4135 constexpr int kWidth = 4;
4136 constexpr int kHeight = 5;
4138 if (tiles.size() >= kWidth * kHeight) {
4141 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4142 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size());
4155 std::span<const gfx::TileInfo> tiles,
4159 constexpr int kWidth = 3;
4160 constexpr int kHeight = 6;
4162 if (tiles.size() >= kWidth * kHeight) {
4164 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4165 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size());
4178 std::span<const gfx::TileInfo> tiles,
4182 constexpr int kWidth = 6;
4183 constexpr int kHeight = 3;
4185 if (tiles.size() >= kWidth * kHeight) {
4187 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size()); ++y) {
4188 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4200 std::span<const gfx::TileInfo> tiles,
4204 constexpr int kWidth = 3;
4205 constexpr int kHeight = 5;
4207 if (tiles.size() >= kWidth * kHeight) {
4209 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4210 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size());
4227 std::span<const gfx::TileInfo> tiles,
4231 constexpr int kWidth = 2;
4232 constexpr int kHeight = 6;
4238 int num_tiles =
static_cast<int>(tiles.size());
4239 for (
int x = 0; x < kWidth; ++x) {
4240 for (
int y = 0; y < kHeight; ++y) {
4249 std::span<const gfx::TileInfo> tiles,
4253 constexpr int kWidth = 6;
4254 constexpr int kHeight = 2;
4260 int num_tiles =
static_cast<int>(tiles.size());
4261 for (
int y = 0; y < kHeight; ++y) {
4262 for (
int x = 0; x < kWidth; ++x) {
4271 std::span<const gfx::TileInfo> tiles,
4276 constexpr int kWidth = 4;
4277 constexpr int kHeight = 4;
4279 if (tiles.size() >= kWidth * kHeight) {
4281 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size()); ++y) {
4282 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4294 std::span<const gfx::TileInfo> tiles,
4298 constexpr int kWidth = 6;
4299 constexpr int kHeight = 6;
4301 if (tiles.size() >= kWidth * kHeight) {
4303 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size()); ++y) {
4304 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4316 std::span<const gfx::TileInfo> tiles,
4326 std::span<const gfx::TileInfo> tiles,
4330 constexpr int kWidth = 3;
4331 constexpr int kHeight = 4;
4333 if (tiles.size() >= kWidth * kHeight) {
4335 for (
int x = 0; x < kWidth && tid < static_cast<int>(tiles.size()); ++x) {
4336 for (
int y = 0; y < kHeight && tid < static_cast<int>(tiles.size());
4349 std::span<const gfx::TileInfo> tiles,
4361 constexpr int kWidth = 3;
4362 constexpr int kSectionHeight = 3;
4364 if (tiles.size() >= 18) {
4367 for (
int x = 0; x < kWidth; ++x) {
4368 for (
int y = 0; y < kSectionHeight; ++y) {
4374 for (
int x = 0; x < kWidth; ++x) {
4375 for (
int y = 0; y < kSectionHeight; ++y) {
4376 WriteTile8(bg, obj.
x_ + x, obj.
y_ + kSectionHeight + y, tiles[tid++]);
4379 }
else if (tiles.size() >= 9) {
4382 for (
int x = 0; x < kWidth; ++x) {
4383 for (
int y = 0; y < kSectionHeight; ++y) {
4395 std::span<const gfx::TileInfo> tiles,
4401 if (tiles.size() >= 4) {
4411 std::span<const gfx::TileInfo> tiles,
4416 if (tiles.size() < 16)
4420 for (
int x = 0; x < 4; ++x) {
4421 for (
int y = 0; y < 4; ++y) {
4429 std::span<const gfx::TileInfo> tiles,
4435 if (tiles.size() >= 12) {
4437 for (
int x = 0; x < 4; ++x) {
4438 for (
int y = 0; y < 3; ++y) {
4447 std::span<const gfx::TileInfo> tiles,
4460 if (tiles.size() < 2)
4464 for (
int col = 0; col < 3; ++col) {
4465 int x = obj.
x_ + (col * 2);
4490 std::span<const gfx::TileInfo> tiles,
4497 if (tiles.size() < 16)
4501 for (
int x = 0; x < 4; ++x) {
4502 for (
int y = 0; y < 4; ++y) {
4510 std::span<const gfx::TileInfo> tiles,
4527 constexpr int kBlockSize = 4;
4529 if (tiles.size() >= 32) {
4532 for (
int y = 0; y < kBlockSize; ++y) {
4533 for (
int x = 0; x < kBlockSize; ++x) {
4540 for (
int y = 0; y < kBlockSize; ++y) {
4541 for (
int x = 0; x < kBlockSize; ++x) {
4542 WriteTile8(bg, obj.
x_ + x, obj.
y_ + kBlockSize + y, tiles[tid++]);
4548 for (
int y = 0; y < kBlockSize; ++y) {
4549 for (
int x = 0; x < kBlockSize; ++x) {
4550 WriteTile8(bg, obj.
x_ + x + 4, obj.
y_ + kBlockSize + y, tiles[tid++]);
4553 }
else if (tiles.size() >= 16) {
4556 for (
int y = 0; y < kBlockSize; ++y) {
4557 for (
int x = 0; x < kBlockSize; ++x) {
4581 int size =
object.size_;
4585 switch (routine_id) {
4591 if (routine_id == 0 || routine_id == 7) {
4600 if (routine_id == 0 || routine_id == 4) {
4614 int effective_size = (size == 0) ? 26 : (size & 0x0F);
4616 width = effective_size * 16;
4625 int count = size + 1;
4640 int count = size + 7;
4642 height = (count + 4) * 8;
4652 int count = size + 6;
4654 height = (count + 4) * 8;
4661 int effective_size = (size == 0) ? 26 : (size & 0x0F);
4664 height = effective_size * 16;
4672 int count = size + 1;
4674 height = count * 16;
4681 height = (size + 3) * 8;
4686 height = (size + 1) * 8;
4692 height = (size + 10) * 8;
4699 int count = (size & 0x0F) + 1;
4715 int tile_count =
object.tiles().size();
4716 if (tile_count >= 16) {
4717 width = height = 32;
4719 width = height = 16;
4728 width = (size * 2 + 4) * 8;
4737 width = (size + 4) * 8;
4746 int count = size + 1;
4747 width = (count + 2) * 8;
4754 width = (size + 23) * 8;
4762 width = (size + 4) * 8;
4770 width = 8 + size * 8;
4784 int count = size + 1;
4786 width = ((count - 1) * 6 + 4) * 8;
4796 int count = size + 1;
4798 width = ((count - 1) * 4 + 2) * 8;
4808 int count = size + 1;
4810 width = ((count - 1) * 4 + 2) * 8;
4820 int count = size + 1;
4822 width = ((count - 1) * 8 + 4) * 8;
4832 int count = size + 1;
4834 width = ((count - 1) * 6 + 4) * 8;
4843 int count = size + 1;
4845 width = ((count - 1) * 14 + 2) * 8;
4853 width = 8 + size * 8;
4866 int count = size + 1;
4867 width = count * 4 * 8;
4876 int count = size + 1;
4877 width = ((count - 1) * 12 + 1) * 8;
4886 int count = size + 1;
4887 width = count * 4 * 8;
4896 int count = size + 1;
4898 height = count * 4 * 8;
4906 height = (size + 4) * 8;
4913 int count = size + 1;
4915 height = ((count - 1) * 6 + 4) * 8;
4922 int count = size + 1;
4924 height = ((count - 1) * 6 + 4) * 8;
4931 int count = size + 1;
4933 height = ((count - 1) * 6 + 4) * 8;
4940 int count = size + 1;
4942 height = ((count - 1) * 14 + 2) * 8;
4950 height = (size + 2) * 8;
4957 int count = size + 1;
4959 height = ((count - 1) * 12 + 4) * 8;
4966 width = (size + 2) * 8;
4974 int count = size + 1;
4975 width = ((count - 1) * 6 + 4) * 8;
4983 int count = size + 1;
4984 width = ((count - 1) * 6 + 4) * 8;
4992 width = (size + 6) * 8;
5000 int count = size + 1;
5001 width = ((count - 1) * 4 + 2) * 8;
5017 int size_x = ((size >> 2) & 0x03) + 1;
5018 int size_y = (size & 0x03) + 1;
5019 width = size_x * 32;
5020 height = size_y * 32;
5034 int count = size + 1;
5036 height = ((count - 1) * 5 + 4) * 8;
5046 height = (size + 6) * 8;
5053 int count = size + 1;
5055 height = ((count - 1) * 4 + 2) * 8;
5062 int count = size + 1;
5064 height = count * 6 * 8;
5071 int count = size + 1;
5073 height = ((count - 1) * 3 + 3) * 8;
5081 int count = size + 1;
5083 height = count * 2 * 8;
5090 width = (size + 8) * 8;
5099 int count = size + 1;
5100 width = count * 2 * 8;
5112 int count = (size & 0x0F) + 2;
5121 int count = (size & 0x0F) + 2;
5260 int count = (size + 1) * 2;
5261 width = (2 + count) * 8;
5270 int count = (size + 1) * 2;
5271 width = (2 + count) * 8;
5304 int size_h = (
object.size_ & 0x0F);
5305 int size_v = (
object.size_ >> 4) & 0x0F;
5306 width = (size_h + 1) * 8;
5307 height = (size_v + 1) * 8;
5312 return {width, height};
5317 [[maybe_unused]] std::span<const gfx::TileInfo> tiles,
5323 int subtype = obj.
size_ & 0x1F;
5324 auto result = manager.GetObjectInternal(obj.
id_, subtype);
5326 LOG_DEBUG(
"ObjectDrawer",
"Custom object 0x%03X subtype %d not found: %s",
5327 obj.
id_, subtype, result.status().message().data());
5331 auto custom_obj = result.value();
5332 if (!custom_obj || custom_obj->IsEmpty())
5335 int tile_x = obj.
x_;
5336 int tile_y = obj.
y_;
5338 for (
const auto& entry : custom_obj->tiles) {
5343 WriteTile8(bg, tile_x + entry.rel_x, tile_y + entry.rel_y, tile_info);
5356 auto& bitmap = bg.
bitmap();
5358 if (!bitmap.is_active() || bitmap.width() == 0)
5363 int pixel_x = (x * 8) + 2;
5364 int pixel_y = (y * 8) + 2;
5443 if (color_idx != 255 && (color_idx % 16) == 0) {
5448 int bitmap_width = bitmap.width();
5449 int bitmap_height = bitmap.height();
5451 for (
int py = 0; py < 4; py++) {
5452 for (
int px = 0; px < 4; px++) {
5453 int dest_x = pixel_x + px;
5454 int dest_y = pixel_y + py;
5457 if (dest_x >= 0 && dest_x < bitmap_width && dest_y >= 0 &&
5458 dest_y < bitmap_height) {
5459 int offset = (dest_y * bitmap_width) + dest_x;
5460 bitmap.WriteToPixel(offset, color_idx);
5462 offset <
static_cast<int>(coverage_buffer.size())) {
5463 coverage_buffer[offset] = 1;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
const auto & vector() const
std::vector< uint8_t > & mutable_priority_data()
std::vector< uint8_t > & mutable_coverage_data()
Represents a bitmap image optimized for SNES ROM hacking.
void set_modified(bool modified)
std::vector< uint8_t > & mutable_data()
SNES 16-bit tile metadata container.
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< CustomObject > > GetObjectInternal(int object_id, int subtype)
const DrawRoutineInfo * GetRoutineInfo(int routine_id) const
bool RoutineDrawsToBothBGs(int routine_id) const
int GetRoutineIdForObject(int16_t object_id) const
static DrawRoutineRegistry & Get()
Interface for accessing dungeon game state.
Draws dungeon objects to background buffers using game patterns.
void DrawDiagonalAcute_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
int GetDrawRoutineId(int16_t object_id) const
Get draw routine ID for an object.
void WriteTile8(gfx::BackgroundBuffer &bg, int tile_x, int tile_y, const gfx::TileInfo &tile_info)
void DrawDiagonalGrave_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawBed4x5(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawClosedChestPlatform(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles)
void DrawRightwards2x4_1to15or26(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards2x4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawTileToBitmap(gfx::Bitmap &bitmap, const gfx::TileInfo &tile_info, int pixel_x, int pixel_y, const uint8_t *tiledata)
Draw a single tile directly to bitmap.
void InitializeDrawRoutines()
Initialize draw routine registry Must be called before drawing objects.
void DrawRightwardsPillar2x4spaced4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsBottomCorners1x2_1to16_plus13(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
TraceContext trace_context_
void DrawRightwards4x4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawUtility6x3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
std::vector< TileTrace > * trace_collector_
void DrawRightwardsEdge1x1_1to16plus7(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
std::vector< DrawRoutine > draw_routines_
void DrawUsingRegistryRoutine(int routine_id, const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state)
void DrawRightwardsDecor4x2spaced8_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsHammerPegs2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsPillar2x4spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawHorizontalTurtleRockPipe(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwards2x2_1to15or32(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDiagonalCeilingBottomRight(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsBlock2x2spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawSingle2x2(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
std::pair< int, int > CalculateObjectDimensions(const RoomObject &object)
Calculate the dimensions (width, height) of an object in pixels.
void SetTraceContext(const RoomObject &object, RoomObject::LayerType layer)
void DrawDoorSwitcherer(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards2x2_1to15or32(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
const uint8_t * room_gfx_buffer_
void DrawUtility3x5(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawSingle4x3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRupeeFloor(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsEdge1x1_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsBar2x3_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
static bool RoutineDrawsToBothBGs(int routine_id)
void DrawDownwardsDecor2x4spaced8_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawNothing(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
absl::Status DrawObjectList(const std::vector< RoomObject > &objects, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw all objects in a room.
void DrawDownwardsDecor4x2spaced4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsLine1x1_1to16plus1(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDoor(const DoorDef &door, int door_index, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const DungeonState *state=nullptr)
Draw a door to background buffers.
void DrawDiagonalCeilingTopLeft(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsTopCorners1x2_1to16_plus13(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsHasEdge1x1_1to16_plus3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDiagonalAcute_1to16_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawPotItem(uint8_t item_id, int x, int y, gfx::BackgroundBuffer &bg)
Draw a pot item visualization.
void DrawDoorIndicator(gfx::BackgroundBuffer &bg, int tile_x, int tile_y, int width, int height, DoorType type, DoorDirection direction)
void DrawLargeCanvasObject(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, int width, int height)
void Draw4x4Corner_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void PushTrace(int tile_x, int tile_y, const gfx::TileInfo &tile_info)
void DrawBigLightBeam(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
static void TraceHookThunk(int tile_x, int tile_y, const gfx::TileInfo &tile_info, void *user_data)
void DrawDownwardsCannonHole3x6_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwards2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsLine1x1_1to16plus1(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawVerticalTurtleRockPipe(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards4x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawCorner4x4(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsHasEdge1x1_1to16_plus3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsHammerPegs2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards2x4_1to16_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawCustomObject(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsStatue2x3spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDiagonalCeilingTopRight(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawWaterFace(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsDecor4x3spaced4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsShelf4x4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDiagonalGrave_1to16_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawMovingWallEast(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles)
void DrawDownwardsPots2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsFloor4x4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsDecor3x4spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsPots2x2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawActual4x4(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawOpenChestPlatform(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles)
void DrawRightwards1x1Solid_1to16_plus3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void MarkBG1Transparent(gfx::BackgroundBuffer &bg1, int tile_x, int tile_y, int pixel_width, int pixel_height)
Mark BG1 pixels as transparent where BG2 overlay objects are drawn.
void DrawChest(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsDecor4x4spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void CustomDraw(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawSolidWallDecor3x4(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDiagonalCeilingBottomLeft(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsCannonHole4x3_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawWeirdCornerBottom_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
bool routines_initialized_
void DrawRightwards3x6(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsHasEdge1x1_1to16_plus2(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwards4x2_1to15or26(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
ObjectDrawer(Rom *rom, int room_id, const uint8_t *room_gfx_buffer=nullptr)
void ClearTraceCollector()
void DrawRightwardsDecor4x4spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawSingle4x4(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawLightBeam(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw a room object to background buffers.
void DrawWeirdCornerTop_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwards4x2_1to16_BothBG(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsDecor3x4spaced4_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsRightCorners2x1_1to16_plus12(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
static constexpr int kMaxTilesY
void DrawRightwardsDoubled2x2spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawBossShell4x4(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
absl::Status DrawRoomDrawObjectData2x2(uint16_t object_id, int tile_x, int tile_y, RoomObject::LayerType layer, uint16_t room_draw_object_data_offset, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2)
Draw a fixed 2x2 (16x16) tile pattern from RoomDrawObjectData.
void DrawRightwardsHasEdge1x1_1to16_plus23(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwards1x2_1to16_plus2(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void SetTraceCollector(std::vector< TileTrace > *collector, bool trace_only=false)
void DrawGanonTriforceFloorDecor(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsBar4x3_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
bool IsValidTilePosition(int tile_x, int tile_y) const
void DrawRightwardsDecor2x2spaced12_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsBlock2x2spaced2_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsHasEdge1x1_1to16_plus23(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwards1x1Solid_1to16_plus3(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawSomariaLine(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawArcheryGameTargetDoor(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawRightwardsBigRail1x3_1to16plus5(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawMovingWallWest(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles)
void DrawDownwardsLeftCorners2x1_1to16_plus12(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsDecor2x2spaced12_1to16(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
void DrawDownwardsBigRail3x1_1to16plus5(const RoomObject &obj, gfx::BackgroundBuffer &bg, std::span< const gfx::TileInfo > tiles, const DungeonState *state=nullptr)
const std::vector< gfx::TileInfo > & tiles() const
#define LOG_DEBUG(category, format,...)
#define LOG_WARN(category, format,...)
TileInfo WordToTileInfo(uint16_t word)
void SetTraceHook(TraceHookFn hook, void *user_data, bool trace_only)
DoorType
Door types from ALTTP.
@ TopShutterLower
Top-sided shutter door (lower layer)
@ FancyDungeonExitLower
Fancy dungeon exit (lower layer)
@ FancyDungeonExit
Fancy dungeon exit.
@ SmallKeyDoor
Small key door.
@ SmallKeyStairsDown
Small key stairs (downwards)
@ BombableCaveExit
Bombable cave exit.
@ SmallKeyStairsUp
Small key stairs (upwards)
@ DungeonSwapMarker
Dungeon swap marker.
@ NormalDoor
Normal door (upper layer)
@ BombableDoor
Bombable door.
@ LayerSwapMarker
Layer swap marker.
@ BottomShutterLower
Bottom-sided shutter door (lower layer)
@ ExplodingWall
Exploding wall.
@ TopSidedShutter
Top-sided shutter door.
@ LitCaveExitLower
Lit cave exit (lower layer)
@ DoubleSidedShutterLower
Double-sided shutter (lower layer)
@ UnopenableBigKeyDoor
Unopenable, double-sided big key door.
@ NormalDoorLower
Normal door (lower layer)
@ BottomSidedShutter
Bottom-sided shutter door.
@ SmallKeyStairsDownLower
Small key stairs (lower layer; downwards)
@ CurtainDoor
Curtain door.
@ WaterfallDoor
Waterfall door.
@ BigKeyDoor
Big key door.
@ EyeWatchDoor
Eye watch door.
@ SmallKeyStairsUpLower
Small key stairs (lower layer; upwards)
@ DoubleSidedShutter
Double sided shutter door.
constexpr int kDoorGfxDown
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
constexpr int kDoorGfxLeft
constexpr int kRoomObjectTileAddress
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
DoorDirection
Door direction on room walls.
@ South
Bottom wall (horizontal door, 4x3 tiles)
@ North
Top wall (horizontal door, 4x3 tiles)
@ East
Right wall (vertical door, 3x4 tiles)
@ West
Left wall (vertical door, 3x4 tiles)
constexpr int kDoorGfxRight
bool kEnableCustomObjects
Represents a group of palettes.
int width_tiles
Width in 8x8 tiles.
Context passed to draw routines containing all necessary state.
gfx::BackgroundBuffer & target_bg
Metadata about a draw routine.
std::pair< int, int > GetTileCoords() const
DoorDimensions GetDimensions() const