10namespace draw_routines {
25 if (ctx.
tiles.size() >= 32) {
28 for (
int x = 0; x < 4; ++x) {
29 for (
int y = 0; y < 4; ++y) {
32 ctx.
tiles[16 + x * 4 + y]);
37 if (ctx.
tiles.size() >= 8 && ctx.
tiles.size() < 16) {
54 if (ctx.
tiles.size() >= 16) {
56 for (
int x = 0; x < 4; ++x) {
57 for (
int y = 0; y < 4; ++y) {
62 }
else if (ctx.
tiles.size() >= 4) {
84 if (ctx.
tiles.size() >= 1) {
98 if (ctx.
tiles.size() >= 2) {
103 if (ctx.
tiles.size() >
static_cast<size_t>(tile_index)) {
105 ctx.
tiles[tile_index]);
122 if (ctx.
tiles.empty())
return;
185 for (
int i = 0; i < length; ++i) {
186 size_t tile_idx = i % ctx.
tiles.size();
198 if (ctx.
tiles.size() >= 4) {
215 if (ctx.
tiles.size() >=
static_cast<size_t>(width * height)) {
216 for (
int y = 0; y < height; ++y) {
217 for (
int x = 0; x < width; ++x) {
220 ctx.
tiles[y * width + x]);
240 "Draw4x4BlocksIn4x4SuperSquare: obj=0x%03X pos=(%d,%d) size=0x%02X "
241 "tiles=%zu size_x=%d size_y=%d",
243 ctx.
tiles.size(), size_x, size_y);
245 if (ctx.
tiles.empty()) {
247 "Draw4x4BlocksIn4x4SuperSquare: SKIPPING - no tiles loaded!");
252 const auto& tile = ctx.
tiles[0];
254 "Draw4x4BlocksIn4x4SuperSquare: tile[0] id=%d palette=%d",
255 tile.id_, tile.palette_);
257 for (
int sy = 0; sy < size_y; ++sy) {
258 for (
int sx = 0; sx < size_x; ++sx) {
260 int base_x = ctx.
object.
x_ + (sx * 4);
261 int base_y = ctx.
object.
y_ + (sy * 4);
264 for (
int y = 0; y < 4; ++y) {
265 for (
int x = 0; x < 4; ++x) {
280 if (ctx.
tiles.size() < 9)
return;
282 for (
int sy = 0; sy < size_y; ++sy) {
283 for (
int sx = 0; sx < size_x; ++sx) {
285 int base_x = ctx.
object.
x_ + (sx * 4);
286 int base_y = ctx.
object.
y_ + (sy * 4);
289 for (
int y = 0; y < 3; ++y) {
290 for (
int x = 0; x < 3; ++x) {
291 size_t tile_idx =
static_cast<size_t>(y * 3 + x);
292 if (tile_idx < ctx.
tiles.size()) {
294 ctx.
tiles[tile_idx]);
309 if (ctx.
tiles.size() < 16)
return;
311 for (
int sy = 0; sy < size_y; ++sy) {
312 for (
int sx = 0; sx < size_x; ++sx) {
313 int base_x = ctx.
object.
x_ + (sx * 4);
314 int base_y = ctx.
object.
y_ + (sy * 4);
317 for (
int y = 0; y < 4; ++y) {
318 for (
int x = 0; x < 4; ++x) {
319 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
320 if (tile_idx < ctx.
tiles.size()) {
322 ctx.
tiles[tile_idx]);
338 size_t tile_offset = 0;
339 if (ctx.
tiles.size() >= 32) tile_offset = 16;
341 if (ctx.
tiles.size() < tile_offset + 16) {
347 for (
int sy = 0; sy < size_y; ++sy) {
348 for (
int sx = 0; sx < size_x; ++sx) {
349 int base_x = ctx.
object.
x_ + (sx * 4);
350 int base_y = ctx.
object.
y_ + (sy * 4);
352 for (
int y = 0; y < 4; ++y) {
353 for (
int x = 0; x < 4; ++x) {
354 size_t tile_idx = tile_offset +
static_cast<size_t>(y * 4 + x);
355 if (tile_idx < ctx.
tiles.size()) {
357 ctx.
tiles[tile_idx]);
372 size_t tile_offset = 0;
373 if (ctx.
tiles.size() >= 48) tile_offset = 32;
375 if (ctx.
tiles.size() < tile_offset + 16) {
380 for (
int sy = 0; sy < size_y; ++sy) {
381 for (
int sx = 0; sx < size_x; ++sx) {
382 int base_x = ctx.
object.
x_ + (sx * 4);
383 int base_y = ctx.
object.
y_ + (sy * 4);
385 for (
int y = 0; y < 4; ++y) {
386 for (
int x = 0; x < 4; ++x) {
387 size_t tile_idx = tile_offset +
static_cast<size_t>(y * 4 + x);
388 if (tile_idx < ctx.
tiles.size()) {
390 ctx.
tiles[tile_idx]);
403 if (ctx.
tiles.size() < 16)
return;
405 for (
int s = 0; s < count; ++s) {
407 int base_y = ctx.
object.
y_ + (s * 4);
410 for (
int y = 0; y < 4; ++y) {
411 for (
int x = 0; x < 4; ++x) {
412 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
414 ctx.
tiles[tile_idx]);
426 if (ctx.
tiles.size() < 4)
return;
428 for (
int sy = 0; sy < size_y; ++sy) {
429 for (
int sx = 0; sx < size_x; ++sx) {
430 int base_x = ctx.
object.
x_ + (sx * 4);
431 int base_y = ctx.
object.
y_ + (sy * 4);
451 if (ctx.
tiles.size() < 16)
return;
453 for (
int s = 0; s < count; ++s) {
454 int base_x = ctx.
object.
x_ + (s * 4);
457 for (
int y = 0; y < 4; ++y) {
458 for (
int x = 0; x < 4; ++x) {
459 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
461 ctx.
tiles[tile_idx]);
478 if (ctx.
tiles.empty())
return;
480 size_t num_tiles = ctx.
tiles.size();
482 for (
int sy = 0; sy < size_y; ++sy) {
483 for (
int sx = 0; sx < size_x; ++sx) {
484 int base_x = ctx.
object.
x_ + (sx * 8);
485 int base_y = ctx.
object.
y_ + (sy * 8);
488 for (
int y = 0; y < 8; ++y) {
489 for (
int x = 0; x < 8; ++x) {
491 size_t tile_idx =
static_cast<size_t>((y * 8 + x) % num_tiles);
495 ctx.
tiles[tile_idx]);
512 if (ctx.
tiles.size() < 16)
return;
515 for (
int y = 0; y < 4; ++y) {
516 for (
int x = 0; x < 4; ++x) {
517 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
543 if (ctx.
tiles.size() < 12)
return;
547 for (
int x = 0; x < 4; ++x) {
548 for (
int y = 0; y < 3; ++y) {
558 if (ctx.
tiles.size() < 16)
return;
560 for (
int y = 0; y < 4; ++y) {
561 for (
int x = 0; x < 4; ++x) {
562 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
572 if (ctx.
tiles.size() < 16)
return;
574 for (
int y = 0; y < 4; ++y) {
575 for (
int x = 0; x < 4; ++x) {
576 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
593 if (ctx.
tiles.size() < 6)
return;
604 for (
int col = 0; col < 5; ++col) {
608 for (
int row = 0; row < 4; ++row) {
609 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
613 base_y + row, ctx.
tiles[tile_idx]);
616 auto mirrored_tile = ctx.
tiles[tile_idx];
617 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
619 base_y + row, mirrored_tile);
626 for (
int col = 0; col < 5; ++col) {
629 for (
int row = 0; row < 4; ++row) {
630 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
634 base_y + row, ctx.
tiles[tile_idx]);
637 auto mirrored_tile = ctx.
tiles[tile_idx];
638 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
640 base_y + row, mirrored_tile);
651 bool is_opened =
false;
659 if (ctx.
tiles.size() >= 8) {
661 for (
int y = 0; y < 2; ++y) {
662 for (
int x = 0; x < 2; ++x) {
665 ctx.
tiles[4 + y * 2 + x]);
673 if (ctx.
tiles.size() >= 4) {
674 for (
int y = 0; y < 2; ++y) {
675 for (
int x = 0; x < 2; ++x) {
687 bool is_bombed =
false;
694 if (ctx.
tiles.size() >= 8) {
695 for (
int y = 0; y < 2; ++y) {
696 for (
int x = 0; x < 2; ++x) {
699 ctx.
tiles[4 + y * 2 + x]);
707 if (ctx.
tiles.size() >= 4) {
708 for (
int y = 0; y < 2; ++y) {
709 for (
int x = 0; x < 2; ++x) {
722 bool has_moved =
false;
731 if (ctx.
tiles.size() < 4)
return;
733 for (
int s = 0; s < size; ++s) {
734 int offset = has_moved ? 2 : 0;
739 for (
int dy = 0; dy < 2; ++dy) {
740 for (
int dx = 0; dx < 2; ++dx) {
742 ctx.
tiles[dy * 2 + dx]);
781 if (ctx.
tiles.size() < 16)
return;
784 for (
int x = 0; x < size_x; ++x) {
786 size_t tile_idx = (x == 0) ? 0 : ((x == size_x - 1) ? 2 : 1);
792 for (
int y = 1; y < size_y + 1; ++y) {
802 int bottom_y = ctx.
object.
y_ + size_y + 1;
803 for (
int x = 0; x < size_x; ++x) {
804 size_t tile_idx = (x == 0) ? 5 : ((x == size_x - 1) ? 7 : 6);
806 ctx.
tiles[tile_idx]);
814 if (ctx.
tiles.size() < 3)
return;
816 for (
int x = 0; x < width; ++x) {
817 size_t tile_idx = (x == 0) ? 0 : ((x == width - 1) ? 2 : 1);
827 if (ctx.
tiles.empty())
return;
829 for (
int y = 0; y < height; ++y) {
848 .draws_to_both_bgs =
false,
858 .draws_to_both_bgs =
false,
866 .name =
"DoorSwitcherer",
868 .draws_to_both_bgs =
false,
876 .name =
"SomariaLine",
878 .draws_to_both_bgs =
false,
888 .draws_to_both_bgs =
false,
897 .name =
"InterRoomFatStairsUp",
899 .draws_to_both_bgs =
false,
907 .name =
"InterRoomFatStairsDownA",
909 .draws_to_both_bgs =
false,
917 .name =
"InterRoomFatStairsDownB",
919 .draws_to_both_bgs =
false,
927 .name =
"AutoStairs",
929 .draws_to_both_bgs =
false,
937 .name =
"StraightInterRoomStairs",
939 .draws_to_both_bgs =
false,
948 .name =
"SpiralStairsGoingUpUpper",
951 .draws_to_both_bgs =
false,
959 .name =
"SpiralStairsGoingDownUpper",
962 .draws_to_both_bgs =
false,
970 .name =
"SpiralStairsGoingUpLower",
973 .draws_to_both_bgs =
false,
981 .name =
"SpiralStairsGoingDownLower",
984 .draws_to_both_bgs =
false,
993 .name =
"BigKeyLock",
995 .draws_to_both_bgs =
false,
1003 .name =
"BombableFloor",
1005 .draws_to_both_bgs =
false,
1014 .name =
"EmptyWaterFace",
1016 .draws_to_both_bgs =
false,
1024 .name =
"SpittingWaterFace",
1026 .draws_to_both_bgs =
false,
1034 .name =
"DrenchingWaterFace",
1036 .draws_to_both_bgs =
false,
1045 .name =
"ClosedChestPlatform",
1047 .draws_to_both_bgs =
false,
1056 .name =
"MovingWallWest",
1061 .draws_to_both_bgs =
false,
1069 .name =
"MovingWallEast",
1074 .draws_to_both_bgs =
false,
1082 .name =
"OpenChestPlatform",
1086 int width = (ctx.object.size_ & 0x0F) + 1;
1087 int segments = ((ctx.object.size_ >> 4) & 0x0F) * 2 + 5;
1089 for (
int s = 0; s < segments && s < 8; ++s) {
1090 for (
int x = 0; x < width && x < 8; ++x) {
1091 if (ctx.tiles.size() > 0) {
1092 size_t idx = (s * width + x) % ctx.tiles.size();
1094 ctx.object.x_ + x, ctx.object.y_ + s, ctx.tiles[idx]);
1099 .draws_to_both_bgs =
false,
1109 .name =
"DownwardsHasEdge1x1_1to16_plus23",
1112 int size = ctx.object.size_ & 0x0F;
1113 int count = (size + 1) * 2;
1114 if (ctx.tiles.size() < 3)
return;
1116 int tile_y = ctx.object.y_;
1121 for (
int s = 0; s < count; s++) {
1128 .draws_to_both_bgs =
false,
1138 .name =
"CustomObject",
1143 for (
int row = 0; row < 4 && row < 4; ++row) {
1144 for (
int col = 0; col < 4 && col < 4; ++col) {
1145 if (
static_cast<size_t>(row * 4 + col) < ctx.tiles.size()) {
1147 ctx.object.x_ + col, ctx.object.y_ + row,
1148 ctx.tiles[row * 4 + col]);
1153 .draws_to_both_bgs =
false,
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.
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 water face pattern (2x2)
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.