9namespace draw_routines {
24 if (ctx.
tiles.size() >= 32) {
27 for (
int x = 0; x < 4; ++x) {
28 for (
int y = 0; y < 4; ++y) {
31 ctx.
tiles[16 + x * 4 + y]);
36 if (ctx.
tiles.size() >= 8 && ctx.
tiles.size() < 16) {
53 if (ctx.
tiles.size() >= 16) {
55 for (
int x = 0; x < 4; ++x) {
56 for (
int y = 0; y < 4; ++y) {
61 }
else if (ctx.
tiles.size() >= 4) {
83 if (ctx.
tiles.size() >= 1) {
97 if (ctx.
tiles.size() >= 2) {
102 if (ctx.
tiles.size() >
static_cast<size_t>(tile_index)) {
104 ctx.
tiles[tile_index]);
121 if (ctx.
tiles.empty())
return;
184 for (
int i = 0; i < length; ++i) {
185 size_t tile_idx = i % ctx.
tiles.size();
197 if (ctx.
tiles.size() >= 4) {
214 if (ctx.
tiles.size() >=
static_cast<size_t>(width * height)) {
215 for (
int y = 0; y < height; ++y) {
216 for (
int x = 0; x < width; ++x) {
219 ctx.
tiles[y * width + x]);
238 if (ctx.
tiles.empty())
return;
241 const auto& tile = ctx.
tiles[0];
243 for (
int sy = 0; sy < size_y; ++sy) {
244 for (
int sx = 0; sx < size_x; ++sx) {
246 int base_x = ctx.
object.
x_ + (sx * 4);
247 int base_y = ctx.
object.
y_ + (sy * 4);
250 for (
int y = 0; y < 4; ++y) {
251 for (
int x = 0; x < 4; ++x) {
266 if (ctx.
tiles.size() < 9)
return;
268 for (
int sy = 0; sy < size_y; ++sy) {
269 for (
int sx = 0; sx < size_x; ++sx) {
271 int base_x = ctx.
object.
x_ + (sx * 4);
272 int base_y = ctx.
object.
y_ + (sy * 4);
275 for (
int y = 0; y < 3; ++y) {
276 for (
int x = 0; x < 3; ++x) {
277 size_t tile_idx =
static_cast<size_t>(y * 3 + x);
278 if (tile_idx < ctx.
tiles.size()) {
280 ctx.
tiles[tile_idx]);
295 if (ctx.
tiles.size() < 16)
return;
297 for (
int sy = 0; sy < size_y; ++sy) {
298 for (
int sx = 0; sx < size_x; ++sx) {
299 int base_x = ctx.
object.
x_ + (sx * 4);
300 int base_y = ctx.
object.
y_ + (sy * 4);
303 for (
int y = 0; y < 4; ++y) {
304 for (
int x = 0; x < 4; ++x) {
305 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
306 if (tile_idx < ctx.
tiles.size()) {
308 ctx.
tiles[tile_idx]);
324 size_t tile_offset = 0;
325 if (ctx.
tiles.size() >= 32) tile_offset = 16;
327 if (ctx.
tiles.size() < tile_offset + 16) {
333 for (
int sy = 0; sy < size_y; ++sy) {
334 for (
int sx = 0; sx < size_x; ++sx) {
335 int base_x = ctx.
object.
x_ + (sx * 4);
336 int base_y = ctx.
object.
y_ + (sy * 4);
338 for (
int y = 0; y < 4; ++y) {
339 for (
int x = 0; x < 4; ++x) {
340 size_t tile_idx = tile_offset +
static_cast<size_t>(y * 4 + x);
341 if (tile_idx < ctx.
tiles.size()) {
343 ctx.
tiles[tile_idx]);
358 size_t tile_offset = 0;
359 if (ctx.
tiles.size() >= 48) tile_offset = 32;
361 if (ctx.
tiles.size() < tile_offset + 16) {
366 for (
int sy = 0; sy < size_y; ++sy) {
367 for (
int sx = 0; sx < size_x; ++sx) {
368 int base_x = ctx.
object.
x_ + (sx * 4);
369 int base_y = ctx.
object.
y_ + (sy * 4);
371 for (
int y = 0; y < 4; ++y) {
372 for (
int x = 0; x < 4; ++x) {
373 size_t tile_idx = tile_offset +
static_cast<size_t>(y * 4 + x);
374 if (tile_idx < ctx.
tiles.size()) {
376 ctx.
tiles[tile_idx]);
389 if (ctx.
tiles.size() < 16)
return;
391 for (
int s = 0; s < count; ++s) {
393 int base_y = ctx.
object.
y_ + (s * 4);
396 for (
int y = 0; y < 4; ++y) {
397 for (
int x = 0; x < 4; ++x) {
398 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
400 ctx.
tiles[tile_idx]);
412 if (ctx.
tiles.size() < 4)
return;
414 for (
int sy = 0; sy < size_y; ++sy) {
415 for (
int sx = 0; sx < size_x; ++sx) {
416 int base_x = ctx.
object.
x_ + (sx * 4);
417 int base_y = ctx.
object.
y_ + (sy * 4);
437 if (ctx.
tiles.size() < 16)
return;
439 for (
int s = 0; s < count; ++s) {
440 int base_x = ctx.
object.
x_ + (s * 4);
443 for (
int y = 0; y < 4; ++y) {
444 for (
int x = 0; x < 4; ++x) {
445 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
447 ctx.
tiles[tile_idx]);
464 if (ctx.
tiles.empty())
return;
466 size_t num_tiles = ctx.
tiles.size();
468 for (
int sy = 0; sy < size_y; ++sy) {
469 for (
int sx = 0; sx < size_x; ++sx) {
470 int base_x = ctx.
object.
x_ + (sx * 8);
471 int base_y = ctx.
object.
y_ + (sy * 8);
474 for (
int y = 0; y < 8; ++y) {
475 for (
int x = 0; x < 8; ++x) {
477 size_t tile_idx =
static_cast<size_t>((y * 8 + x) % num_tiles);
481 ctx.
tiles[tile_idx]);
498 if (ctx.
tiles.size() < 16)
return;
501 for (
int y = 0; y < 4; ++y) {
502 for (
int x = 0; x < 4; ++x) {
503 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
529 if (ctx.
tiles.size() < 12)
return;
533 for (
int x = 0; x < 4; ++x) {
534 for (
int y = 0; y < 3; ++y) {
544 if (ctx.
tiles.size() < 16)
return;
546 for (
int y = 0; y < 4; ++y) {
547 for (
int x = 0; x < 4; ++x) {
548 size_t tile_idx =
static_cast<size_t>(y * 4 + x);
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);
579 if (ctx.
tiles.size() < 6)
return;
590 for (
int col = 0; col < 5; ++col) {
594 for (
int row = 0; row < 4; ++row) {
595 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
599 base_y + row, ctx.
tiles[tile_idx]);
602 auto mirrored_tile = ctx.
tiles[tile_idx];
603 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
605 base_y + row, mirrored_tile);
612 for (
int col = 0; col < 5; ++col) {
615 for (
int row = 0; row < 4; ++row) {
616 size_t tile_idx = (row < static_cast<int>(ctx.
tiles.size())) ? row : 0;
620 base_y + row, ctx.
tiles[tile_idx]);
623 auto mirrored_tile = ctx.
tiles[tile_idx];
624 mirrored_tile.horizontal_mirror_ = !mirrored_tile.horizontal_mirror_;
626 base_y + row, mirrored_tile);
637 bool is_opened =
false;
645 if (ctx.
tiles.size() >= 8) {
647 for (
int y = 0; y < 2; ++y) {
648 for (
int x = 0; x < 2; ++x) {
651 ctx.
tiles[4 + y * 2 + x]);
659 if (ctx.
tiles.size() >= 4) {
660 for (
int y = 0; y < 2; ++y) {
661 for (
int x = 0; x < 2; ++x) {
673 bool is_bombed =
false;
680 if (ctx.
tiles.size() >= 8) {
681 for (
int y = 0; y < 2; ++y) {
682 for (
int x = 0; x < 2; ++x) {
685 ctx.
tiles[4 + y * 2 + x]);
693 if (ctx.
tiles.size() >= 4) {
694 for (
int y = 0; y < 2; ++y) {
695 for (
int x = 0; x < 2; ++x) {
708 bool has_moved =
false;
717 if (ctx.
tiles.size() < 4)
return;
719 for (
int s = 0; s < size; ++s) {
720 int offset = has_moved ? 2 : 0;
725 for (
int dy = 0; dy < 2; ++dy) {
726 for (
int dx = 0; dx < 2; ++dx) {
728 ctx.
tiles[dy * 2 + dx]);
767 if (ctx.
tiles.size() < 16)
return;
770 for (
int x = 0; x < size_x; ++x) {
772 size_t tile_idx = (x == 0) ? 0 : ((x == size_x - 1) ? 2 : 1);
778 for (
int y = 1; y < size_y + 1; ++y) {
788 int bottom_y = ctx.
object.
y_ + size_y + 1;
789 for (
int x = 0; x < size_x; ++x) {
790 size_t tile_idx = (x == 0) ? 5 : ((x == size_x - 1) ? 7 : 6);
792 ctx.
tiles[tile_idx]);
800 if (ctx.
tiles.size() < 3)
return;
802 for (
int x = 0; x < width; ++x) {
803 size_t tile_idx = (x == 0) ? 0 : ((x == width - 1) ? 2 : 1);
813 if (ctx.
tiles.empty())
return;
815 for (
int y = 0; y < height; ++y) {
834 .draws_to_both_bgs =
false,
844 .draws_to_both_bgs =
false,
852 .name =
"DoorSwitcherer",
854 .draws_to_both_bgs =
false,
862 .name =
"SomariaLine",
864 .draws_to_both_bgs =
false,
874 .draws_to_both_bgs =
false,
883 .name =
"InterRoomFatStairsUp",
885 .draws_to_both_bgs =
false,
893 .name =
"InterRoomFatStairsDownA",
895 .draws_to_both_bgs =
false,
903 .name =
"InterRoomFatStairsDownB",
905 .draws_to_both_bgs =
false,
913 .name =
"AutoStairs",
915 .draws_to_both_bgs =
false,
923 .name =
"StraightInterRoomStairs",
925 .draws_to_both_bgs =
false,
934 .name =
"SpiralStairsGoingUpUpper",
937 .draws_to_both_bgs =
false,
945 .name =
"SpiralStairsGoingDownUpper",
948 .draws_to_both_bgs =
false,
956 .name =
"SpiralStairsGoingUpLower",
959 .draws_to_both_bgs =
false,
967 .name =
"SpiralStairsGoingDownLower",
970 .draws_to_both_bgs =
false,
979 .name =
"BigKeyLock",
981 .draws_to_both_bgs =
false,
989 .name =
"BombableFloor",
991 .draws_to_both_bgs =
false,
1000 .name =
"EmptyWaterFace",
1002 .draws_to_both_bgs =
false,
1010 .name =
"SpittingWaterFace",
1012 .draws_to_both_bgs =
false,
1020 .name =
"DrenchingWaterFace",
1022 .draws_to_both_bgs =
false,
1031 .name =
"ClosedChestPlatform",
1033 .draws_to_both_bgs =
false,
1042 .name =
"MovingWallWest",
1047 .draws_to_both_bgs =
false,
1055 .name =
"MovingWallEast",
1060 .draws_to_both_bgs =
false,
1068 .name =
"OpenChestPlatform",
1072 int width = (ctx.object.size_ & 0x0F) + 1;
1073 int segments = ((ctx.object.size_ >> 4) & 0x0F) * 2 + 5;
1075 for (
int s = 0; s < segments && s < 8; ++s) {
1076 for (
int x = 0; x < width && x < 8; ++x) {
1077 if (ctx.tiles.size() > 0) {
1078 size_t idx = (s * width + x) % ctx.tiles.size();
1080 ctx.object.x_ + x, ctx.object.y_ + s, ctx.tiles[idx]);
1085 .draws_to_both_bgs =
false,
1095 .name =
"DownwardsHasEdge1x1_1to16_plus23",
1098 int size = ctx.object.size_ & 0x0F;
1099 int count = (size + 1) * 2;
1100 if (ctx.tiles.size() < 3)
return;
1102 int tile_y = ctx.object.y_;
1107 for (
int s = 0; s < count; s++) {
1114 .draws_to_both_bgs =
false,
1124 .name =
"CustomObject",
1129 for (
int row = 0; row < 4 && row < 4; ++row) {
1130 for (
int col = 0; col < 4 && col < 4; ++col) {
1131 if (
static_cast<size_t>(row * 4 + col) < ctx.tiles.size()) {
1133 ctx.object.x_ + col, ctx.object.y_ + row,
1134 ctx.tiles[row * 4 + col]);
1139 .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
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.