14namespace draw_routines {
29 if (ctx.
tiles.size() >= 32) {
32 for (
int x = 0; x < 4; ++x) {
33 for (
int y = 0; y < 4; ++y) {
36 ctx.
tiles[16 + x * 4 + y]);
41 if (ctx.
tiles.size() >= 8 && ctx.
tiles.size() < 16) {
58 if (ctx.
tiles.size() >= 16) {
60 for (
int x = 0; x < 4; ++x) {
61 for (
int y = 0; y < 4; ++y) {
66 }
else if (ctx.
tiles.size() >= 4) {
93 if (ctx.
tiles.size() >= 1) {
105 if (!result.ok() || !result.value() || result.value()->IsEmpty()) {
107 if (ctx.
tiles.size() >= 1) {
114 const auto& custom_obj = *result.value();
116 for (
const auto& entry : custom_obj.tiles) {
119 uint8_t lo =
static_cast<uint8_t
>(entry.tile_data & 0xFF);
120 uint8_t hi =
static_cast<uint8_t
>((entry.tile_data >> 8) & 0xFF);
125 int draw_x = ctx.
object.
x_ + entry.rel_x;
126 int draw_y = ctx.
object.
y_ + entry.rel_y;
139 if (ctx.
tiles.size() >= 2) {
144 if (ctx.
tiles.size() >
static_cast<size_t>(tile_index)) {
146 ctx.
tiles[tile_index]);
163 if (ctx.
tiles.empty())
return;
226 for (
int i = 0; i < length; ++i) {
227 size_t tile_idx = i % ctx.
tiles.size();
239 if (ctx.
tiles.size() >= 4) {
256 if (ctx.
tiles.size() >=
static_cast<size_t>(width * height)) {
257 for (
int y = 0; y < height; ++y) {
258 for (
int x = 0; x < width; ++x) {
261 ctx.
tiles[y * width + x]);
281 "Draw4x4BlocksIn4x4SuperSquare: obj=0x%03X pos=(%d,%d) size=0x%02X "
282 "tiles=%zu size_x=%d size_y=%d",
284 ctx.
tiles.size(), size_x, size_y);
286 if (ctx.
tiles.empty()) {
288 "Draw4x4BlocksIn4x4SuperSquare: SKIPPING - no tiles loaded!");
293 const auto& tile = ctx.
tiles[0];
295 "Draw4x4BlocksIn4x4SuperSquare: tile[0] id=%d palette=%d",
296 tile.id_, tile.palette_);
298 for (
int sy = 0; sy < size_y; ++sy) {
299 for (
int sx = 0; sx < size_x; ++sx) {
301 int base_x = ctx.
object.
x_ + (sx * 4);
302 int base_y = ctx.
object.
y_ + (sy * 4);
305 for (
int y = 0; y < 4; ++y) {
306 for (
int x = 0; x < 4; ++x) {
321 if (ctx.
tiles.empty())
return;
323 const auto& tile = ctx.
tiles[0];
324 for (
int sy = 0; sy < size_y; ++sy) {
325 for (
int sx = 0; sx < size_x; ++sx) {
326 int base_x = ctx.
object.
x_ + (sx * 3);
327 int base_y = ctx.
object.
y_ + (sy * 3);
329 for (
int y = 0; y < 3; ++y) {
330 for (
int x = 0; x < 3; ++x) {
346 if (ctx.
tiles.empty())
return;
347 if (ctx.
tiles.size() < 8) {
354 for (
int sy = 0; sy < size_y; ++sy) {
355 for (
int sx = 0; sx < size_x; ++sx) {
356 int base_x = ctx.
object.
x_ + (sx * 4);
357 int base_y = ctx.
object.
y_ + (sy * 4);
361 for (
int x = 0; x < 4; ++x) {
362 const auto& row0 = ctx.
tiles[(x * 2) + 0];
363 const auto& row1 = ctx.
tiles[(x * 2) + 1];
388 if (ctx.
tiles.size() < 8) {
393 for (
int sy = 0; sy < size_y; ++sy) {
394 for (
int sx = 0; sx < size_x; ++sx) {
395 int base_x = ctx.
object.
x_ + (sx * 4);
396 int base_y = ctx.
object.
y_ + (sy * 4);
398 for (
int x = 0; x < 4; ++x) {
399 const auto& row0 = ctx.
tiles[(x * 2) + 0];
400 const auto& row1 = ctx.
tiles[(x * 2) + 1];
421 if (ctx.
tiles.size() < 8) {
426 for (
int sy = 0; sy < size_y; ++sy) {
427 for (
int sx = 0; sx < size_x; ++sx) {
428 int base_x = ctx.
object.
x_ + (sx * 4);
429 int base_y = ctx.
object.
y_ + (sy * 4);
431 for (
int x = 0; x < 4; ++x) {
432 const auto& row0 = ctx.
tiles[(x * 2) + 0];
433 const auto& row1 = ctx.
tiles[(x * 2) + 1];
453 if (ctx.
tiles.size() < 24)
return;
469 for (
int xx = 1; xx < max; ++xx) {
470 for (
int yy = 1; yy < max; ++yy) {
480 for (
int yy = 1; yy < max; ++yy) {
494 if (ctx.
tiles.size() < 4)
return;
496 for (
int sy = 0; sy < size_y; ++sy) {
497 for (
int sx = 0; sx < size_x; ++sx) {
498 int base_x = ctx.
object.
x_ + (sx * 2);
499 int base_y = ctx.
object.
y_ + (sy * 2);
518 if (ctx.
tiles.size() < 16)
return;
520 int right_x = ctx.
object.
x_ + (3 + (size_x * 2));
521 int bottom_y = ctx.
object.
y_ + (3 + (size_y * 2));
524 for (
int xx = 0; xx < size_x + 1; ++xx) {
525 for (
int yy = 0; yy < size_y + 1; ++yy) {
526 int base_x = ctx.
object.
x_ + (xx * 2);
527 int base_y = ctx.
object.
y_ + (yy * 2);
540 for (
int yy = 0; yy < size_y + 1; ++yy) {
541 int base_y = ctx.
object.
y_ + (yy * 2);
554 for (
int xx = 0; xx < size_x + 1; ++xx) {
555 int base_x = ctx.
object.
x_ + (xx * 2);
587 int count_x = size_x + 2;
588 int count_y = size_y + 2;
590 if (ctx.
tiles.empty())
return;
591 if (ctx.
tiles.size() < 8) {
593 for (
int yy = 0; yy < count_y; ++yy) {
594 for (
int xx = 0; xx < count_x; ++xx) {
595 int base_x = ctx.
object.
x_ + (xx * 4);
596 int base_y = ctx.
object.
y_ + (yy * 4);
598 ctx.
tiles[
static_cast<size_t>((xx + yy) % ctx.
tiles.size())];
599 for (
int y = 0; y < 4; ++y) {
600 for (
int x = 0; x < 4; ++x) {
610 for (
int yy = 0; yy < count_y; ++yy) {
611 for (
int xx = 0; xx < count_x; ++xx) {
612 int base_x = ctx.
object.
x_ + (xx * 4);
613 int base_y = ctx.
object.
y_ + (yy * 4);
615 for (
int x = 0; x < 4; ++x) {
639 if (ctx.
tiles.size() < 16)
return;
642 for (
int y = 0; y < 4; ++y) {
643 for (
int x = 0; x < 4; ++x) {
644 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
670 if (ctx.
tiles.size() < 12)
return;
674 for (
int x = 0; x < 4; ++x) {
675 for (
int y = 0; y < 3; ++y) {
685 if (ctx.
tiles.size() < 16)
return;
687 for (
int y = 0; y < 4; ++y) {
688 for (
int x = 0; x < 4; ++x) {
689 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
699 if (ctx.
tiles.size() < 16)
return;
701 for (
int y = 0; y < 4; ++y) {
702 for (
int x = 0; x < 4; ++x) {
703 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
720 if (ctx.
tiles.size() < 6)
return;
731 for (
int col = 0; col < 5; ++col) {
735 for (
int row = 0; row < 4; ++row) {
736 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
740 base_y + row, ctx.
tiles[tile_idx]);
743 auto mirrored_tile = ctx.
tiles[tile_idx];
744 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
746 base_y + row, mirrored_tile);
753 for (
int col = 0; col < 5; ++col) {
756 for (
int row = 0; row < 4; ++row) {
757 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
761 base_y + row, ctx.
tiles[tile_idx]);
764 auto mirrored_tile = ctx.
tiles[tile_idx];
765 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
767 base_y + row, mirrored_tile);
778 bool is_opened =
false;
786 if (ctx.
tiles.size() >= 8) {
788 for (
int y = 0; y < 2; ++y) {
789 for (
int x = 0; x < 2; ++x) {
792 ctx.
tiles[4 + y * 2 + x]);
800 if (ctx.
tiles.size() >= 4) {
801 for (
int y = 0; y < 2; ++y) {
802 for (
int x = 0; x < 2; ++x) {
814 bool is_bombed =
false;
821 if (ctx.
tiles.size() >= 8) {
822 for (
int y = 0; y < 2; ++y) {
823 for (
int x = 0; x < 2; ++x) {
826 ctx.
tiles[4 + y * 2 + x]);
834 if (ctx.
tiles.size() >= 4) {
835 for (
int y = 0; y < 2; ++y) {
836 for (
int x = 0; x < 2; ++x) {
849 bool has_moved =
false;
858 if (ctx.
tiles.size() < 4)
return;
860 for (
int s = 0; s < size; ++s) {
861 int offset = has_moved ? 2 : 0;
866 for (
int dy = 0; dy < 2; ++dy) {
867 for (
int dx = 0; dx < 2; ++dx) {
869 ctx.
tiles[dy * 2 + dx]);
883 if (row_count <= 0 || tile_offset < 0 || ctx.
tiles.empty()) {
887 if (tile_offset >=
static_cast<int>(ctx.
tiles.size())) {
891 const int available_tiles =
static_cast<int>(ctx.
tiles.size()) - tile_offset;
893 const int rows_to_draw = std::min(row_count, available_rows);
895 for (
int row = 0; row < rows_to_draw; ++row) {
900 ctx.
tiles[row_base + col]);
915 const bool water_active =
918 const int row_count = water_active ? 5 : 3;
919 const int tile_offset = water_active ? 12 : 0;
920 DrawWaterFaceRows(ctx, row_count, tile_offset);
926 DrawWaterFaceRows(ctx, 5, 0);
932 DrawWaterFaceRows(ctx, 7, 0);
946 if (ctx.
tiles.size() < 16)
return;
949 for (
int x = 0; x < size_x; ++x) {
951 size_t tile_idx = (x == 0) ? 0 : ((x == size_x - 1) ? 2 : 1);
957 for (
int y = 1; y < size_y + 1; ++y) {
967 int bottom_y = ctx.
object.
y_ + size_y + 1;
968 for (
int x = 0; x < size_x; ++x) {
969 size_t tile_idx = (x == 0) ? 5 : ((x == size_x - 1) ? 7 : 6);
971 ctx.
tiles[tile_idx]);
979 if (ctx.
tiles.size() < 3)
return;
981 for (
int x = 0; x < width; ++x) {
982 size_t tile_idx = (x == 0) ? 0 : ((x == width - 1) ? 2 : 1);
992 if (ctx.
tiles.empty())
return;
994 for (
int y = 0; y < height; ++y) {
1013 .draws_to_both_bgs =
false,
1024 .draws_to_both_bgs =
false,
1032 .name =
"DoorSwitcherer",
1034 .draws_to_both_bgs =
false,
1043 .name =
"SomariaLine",
1045 .draws_to_both_bgs =
false,
1053 .name =
"WaterFace",
1055 .draws_to_both_bgs =
false,
1068 .name =
"4x4BlocksIn4x4SuperSquare",
1070 .draws_to_both_bgs =
false,
1078 .name =
"3x3FloorIn4x4SuperSquare",
1080 .draws_to_both_bgs =
false,
1088 .name =
"4x4FloorIn4x4SuperSquare",
1090 .draws_to_both_bgs =
false,
1098 .name =
"4x4FloorOneIn4x4SuperSquare",
1100 .draws_to_both_bgs =
false,
1108 .name =
"4x4FloorTwoIn4x4SuperSquare",
1110 .draws_to_both_bgs =
false,
1118 .name =
"BigHole4x4_1to16",
1120 .draws_to_both_bgs =
false,
1128 .name =
"Spike2x2In4x4SuperSquare",
1130 .draws_to_both_bgs =
false,
1138 .name =
"TableRock4x4_1to16",
1140 .draws_to_both_bgs =
false,
1148 .name =
"WaterOverlay8x8_1to16",
1150 .draws_to_both_bgs =
false,
1159 .name =
"InterRoomFatStairsUp",
1161 .draws_to_both_bgs =
false,
1170 .name =
"InterRoomFatStairsDownA",
1172 .draws_to_both_bgs =
false,
1181 .name =
"InterRoomFatStairsDownB",
1183 .draws_to_both_bgs =
false,
1192 .name =
"AutoStairs",
1194 .draws_to_both_bgs =
false,
1203 .name =
"StraightInterRoomStairs",
1205 .draws_to_both_bgs =
false,
1215 .name =
"SpiralStairsGoingUpUpper",
1218 .draws_to_both_bgs =
false,
1227 .name =
"SpiralStairsGoingDownUpper",
1230 .draws_to_both_bgs =
false,
1239 .name =
"SpiralStairsGoingUpLower",
1242 .draws_to_both_bgs =
false,
1251 .name =
"SpiralStairsGoingDownLower",
1254 .draws_to_both_bgs =
false,
1264 .name =
"BigKeyLock",
1266 .draws_to_both_bgs =
false,
1275 .name =
"BombableFloor",
1277 .draws_to_both_bgs =
false,
1287 .name =
"EmptyWaterFace",
1289 .draws_to_both_bgs =
false,
1298 .name =
"SpittingWaterFace",
1300 .draws_to_both_bgs =
false,
1309 .name =
"DrenchingWaterFace",
1311 .draws_to_both_bgs =
false,
1321 .name =
"PrisonCell",
1323 .draws_to_both_bgs =
true,
1332 .name =
"ClosedChestPlatform",
1334 .draws_to_both_bgs =
false,
1343 .name =
"MovingWallWest",
1347 .draws_to_both_bgs =
false,
1356 .name =
"MovingWallEast",
1360 .draws_to_both_bgs =
false,
1369 .name =
"OpenChestPlatform",
1373 int width = (ctx.object.size_ & 0x0F) + 1;
1374 int segments = ((ctx.object.size_ >> 4) & 0x0F) * 2 + 5;
1376 for (
int s = 0; s < segments && s < 8; ++s) {
1377 for (
int x = 0; x < width && x < 8; ++x) {
1378 if (ctx.tiles.size() > 0) {
1379 size_t idx = (s * width + x) % ctx.tiles.size();
1381 ctx.object.x_ + x, ctx.object.y_ + s, ctx.tiles[idx]);
1386 .draws_to_both_bgs =
false,
1396 .name =
"DownwardsHasEdge1x1_1to16_plus23",
1399 int size = ctx.object.size_ & 0x0F;
1400 int count = size + 21;
1401 if (ctx.tiles.size() < 3)
return;
1403 int tile_y = ctx.object.y_;
1408 for (
int s = 0; s < count; s++) {
1415 .draws_to_both_bgs =
false,
1427 .name =
"CustomObject",
1429 .draws_to_both_bgs =
false,
SNES 16-bit tile metadata container.
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< CustomObject > > GetObjectInternal(int object_id, int subtype)
virtual bool IsWaterFaceActive(int room_id) const
virtual bool IsFloorBombable(int room_id) const =0
virtual bool IsWallMoved(int room_id) const =0
virtual bool IsChestOpen(int room_id, int chest_index) const =0
virtual bool IsDoorSwitchActive(int room_id) const =0
virtual bool IsDoorOpen(int room_id, int door_index) const =0
#define LOG_DEBUG(category, format,...)
constexpr int kClosedChestPlatform
constexpr int kMovingWallWest
constexpr int kMovingWallEast
constexpr int kCustomObject
constexpr int kOpenChestPlatform
constexpr int kDownwardsHasEdge1x1_1to16_plus23
void WriteTile8(gfx::BackgroundBuffer &bg, int tile_x, int tile_y, const gfx::TileInfo &tile_info)
Write an 8x8 tile to the background buffer.
constexpr int kWaterFaceWidthTiles
void DrawWaterFaceRows(const DrawContext &ctx, int row_count, int tile_offset)
void DrawTableRock4x4_1to16(const DrawContext &ctx)
Draw 4x4 table rock pattern.
void DrawInterRoomFatStairsDownA(const DrawContext &ctx)
Draw inter-room fat stairs going down A (Type 2 object 0x12E)
void DrawAutoStairs(const DrawContext &ctx)
Draw auto stairs (Type 2/3 objects 0x130-0x133, 0x21B-0x21D, 0x233)
void DrawSpittingWaterFace(const DrawContext &ctx)
Draw spitting water face (Type 3 object 0x201)
void DrawLargeCanvasObject(const DrawContext &ctx, int width, int height)
Draw large canvas object with arbitrary dimensions.
void DrawChest(const DrawContext &ctx, int chest_index)
Draw chest object (big or small) with open/closed state support.
void DrawDoorSwitcherer(const DrawContext &ctx)
Draw door switcher object with state-based graphics.
void Draw4x4BlocksIn4x4SuperSquare(const DrawContext &ctx)
Draw 4x4 solid blocks in a super square grid.
void DrawBigHole4x4_1to16(const DrawContext &ctx)
Draw 4x4 big hole pattern.
void Draw4x4FloorTwoIn4x4SuperSquare(const DrawContext &ctx)
Draw two 4x4 floor pattern variant.
void DrawWaterFace(const DrawContext &ctx)
Draw a generic 2x2 water-face helper pattern.
void DrawChestPlatformVerticalWall(const DrawContext &ctx)
Draw chest platform vertical wall section.
void DrawClosedChestPlatform(const DrawContext &ctx)
Draw closed chest platform (Type 1 object 0xC1)
void DrawSpike2x2In4x4SuperSquare(const DrawContext &ctx)
Draw 2x2 spike pattern in super square units.
void DrawSpiralStairs(const DrawContext &ctx, bool going_up, bool is_upper)
Draw spiral stairs (Type 2 objects 0x138-0x13B)
void DrawSomariaLine(const DrawContext &ctx)
Draw Somaria line in various directions.
void DrawBombableFloor(const DrawContext &ctx)
Draw bombable floor (Type 3 object 0x247)
void DrawDrenchingWaterFace(const DrawContext &ctx)
Draw drenching water face (Type 3 object 0x202)
void DrawEmptyWaterFace(const DrawContext &ctx)
Draw empty water face (Type 3 object 0x200)
void CustomDraw(const DrawContext &ctx)
Custom draw routine for special objects.
void Draw4x4FloorIn4x4SuperSquare(const DrawContext &ctx)
Draw 4x4 floor pattern in super square units.
void DrawInterRoomFatStairsUp(const DrawContext &ctx)
Draw inter-room fat stairs going up (Type 2 object 0x12D)
void Draw3x3FloorIn4x4SuperSquare(const DrawContext &ctx)
Draw 3x3 floor pattern in super square units.
void DrawStraightInterRoomStairs(const DrawContext &ctx)
Draw straight inter-room stairs (Type 3 objects 0x21E-0x229)
void RegisterSpecialRoutines(std::vector< DrawRoutineInfo > ®istry)
Register all special/miscellaneous draw routines to the registry.
void DrawInterRoomFatStairsDownB(const DrawContext &ctx)
Draw inter-room fat stairs going down B (Type 2 object 0x12F)
void DrawMovingWall(const DrawContext &ctx, bool is_west)
Draw moving wall (Type 1 objects 0xCD, 0xCE)
void DrawChestPlatformHorizontalWall(const DrawContext &ctx)
Draw chest platform horizontal wall section.
void DrawWaterOverlay8x8_1to16(const DrawContext &ctx)
Draw water overlay 8x8 pattern.
void Draw4x4FloorOneIn4x4SuperSquare(const DrawContext &ctx)
Draw single 4x4 floor pattern variant.
void DrawNothing(const DrawContext &ctx)
Draw nothing - represents invisible logic objects or placeholders.
void DrawBigKeyLock(const DrawContext &ctx)
Draw big key lock (Type 3 object 0x218)
void DrawPrisonCell(const DrawContext &ctx)
Draw prison cell with bars (Type 3 objects 0x20D, 0x217)
Context passed to draw routines containing all necessary state.
std::span< const gfx::TileInfo > tiles
gfx::BackgroundBuffer & target_bg
bool HasSecondaryBG() const
const RoomObject & object
gfx::BackgroundBuffer * secondary_bg
const DungeonState * state
Metadata about a draw routine.