refactor: Update Dungeon Rendering Logic for Enhanced Tile Management (WIP)

- Refactored DungeonCanvasViewer to utilize LoadLayoutTilesToBuffer for rendering layout tiles, improving the separation of concerns in the rendering process.
- Updated ObjectDrawer to draw using 8x8 tiles instead of 16x16, enhancing tile rendering accuracy and efficiency.
- Modified Room and RoomLayoutObject classes to support room-specific graphics buffers, ensuring correct tile usage during rendering.
- Removed legacy methods and classes related to tile handling, streamlining the codebase and improving maintainability.
This commit is contained in:
scawful
2025-10-09 23:50:12 -04:00
parent 3f2ef7f523
commit 8481cd9366
9 changed files with 348 additions and 362 deletions

View File

@@ -393,8 +393,8 @@ std::pair<int, int> DungeonCanvasViewer::CanvasToRoomCoordinates(int canvas_x,
bool DungeonCanvasViewer::IsWithinCanvasBounds(int canvas_x, int canvas_y,
int margin) const {
// Check if coordinates are within canvas bounds with optional margin
auto canvas_width = canvas_.width();
auto canvas_height = canvas_.height();
auto canvas_width = canvas_.width() * canvas_.global_scale();
auto canvas_height = canvas_.height() * canvas_.global_scale();
return (canvas_x >= -margin && canvas_y >= -margin &&
canvas_x <= canvas_width + margin &&
canvas_y <= canvas_height + margin);
@@ -606,7 +606,7 @@ absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
}
void DungeonCanvasViewer::DrawRoomBackgroundLayers(int room_id) {
if (room_id < 0 || room_id >= 128 || !rooms_) return;
if (room_id < 0 || room_id >= zelda3::NumberOfRooms || !rooms_) return;
auto& room = (*rooms_)[room_id];
auto& layer_settings = GetRoomLayerSettings(room_id);
@@ -628,8 +628,10 @@ void DungeonCanvasViewer::DrawRoomBackgroundLayers(int room_id) {
// Only draw if texture was successfully created
if (bg1_bitmap.texture()) {
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG1 bitmap to canvas with texture %p", bg1_bitmap.texture());
canvas_.DrawBitmap(bg1_bitmap, 0, 0, 1.0f, 255);
// Use canvas global scale so bitmap scales with zoom
float scale = canvas_.global_scale();
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG1 bitmap to canvas with texture %p, scale=%.2f", bg1_bitmap.texture(), scale);
canvas_.DrawBitmap(bg1_bitmap, 0, 0, scale, 255);
} else {
LOG_DEBUG("DungeonCanvasViewer", "ERROR: BG1 bitmap has no texture!");
}
@@ -651,8 +653,10 @@ void DungeonCanvasViewer::DrawRoomBackgroundLayers(int room_id) {
// Use the selected BG2 layer type alpha value
const int bg2_alpha_values[] = {255, 191, 127, 64, 0};
int alpha_value = bg2_alpha_values[std::min(layer_settings.bg2_layer_type, 4)];
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG2 bitmap to canvas with texture %p, alpha=%d", bg2_bitmap.texture(), alpha_value);
canvas_.DrawBitmap(bg2_bitmap, 0, 0, 1.0f, alpha_value);
// Use canvas global scale so bitmap scales with zoom
float scale = canvas_.global_scale();
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG2 bitmap to canvas with texture %p, alpha=%d, scale=%.2f", bg2_bitmap.texture(), alpha_value, scale);
canvas_.DrawBitmap(bg2_bitmap, 0, 0, scale, alpha_value);
} else {
LOG_DEBUG("DungeonCanvasViewer", "ERROR: BG2 bitmap has no texture!");
}
@@ -686,17 +690,9 @@ void DungeonCanvasViewer::DrawRoomBackgroundLayers(int room_id) {
bg2_data.size(), non_zero_pixels);
}
// TEST: Draw a bright red rectangle to verify canvas drawing works
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
draw_list->AddRectFilled(
ImVec2(canvas_pos.x + 50, canvas_pos.y + 50),
ImVec2(canvas_pos.x + 150, canvas_pos.y + 150),
IM_COL32(255, 0, 0, 255)); // Bright red
// DEBUG: Show canvas and bitmap info
LOG_DEBUG("DungeonCanvasViewer", "Canvas pos: (%.1f, %.1f), Canvas size: (%.1f, %.1f)",
canvas_pos.x, canvas_pos.y, canvas_.canvas_size().x, canvas_.canvas_size().y);
canvas_.zero_point().x, canvas_.zero_point().y, canvas_.width(), canvas_.height());
LOG_DEBUG("DungeonCanvasViewer", "BG1 bitmap size: %dx%d, BG2 bitmap size: %dx%d",
bg1_bitmap.width(), bg1_bitmap.height(), bg2_bitmap.width(), bg2_bitmap.height());
}

View File

@@ -9,7 +9,8 @@
namespace yaze {
namespace zelda3 {
ObjectDrawer::ObjectDrawer(Rom* rom) : rom_(rom) {
ObjectDrawer::ObjectDrawer(Rom* rom, const uint8_t* room_gfx_buffer)
: rom_(rom), room_gfx_buffer_(room_gfx_buffer) {
InitializeDrawRoutines();
}
@@ -54,8 +55,11 @@ absl::Status ObjectDrawer::DrawObject(const RoomObject& object,
int routine_id = GetDrawRoutineId(object.id_);
if (routine_id < 0 || routine_id >= static_cast<int>(draw_routines_.size())) {
// Fallback to simple 1x1 drawing
WriteTile16(target_bg, object.x_, object.y_, mutable_obj.tiles()[0]);
// Fallback to simple 1x1 drawing using first 8x8 tile
if (!mutable_obj.tiles().empty()) {
const auto& tile16 = mutable_obj.tiles()[0];
WriteTile8(target_bg, object.x_, object.y_, tile16.tile0_);
}
return absl::OkStatus();
}
@@ -245,11 +249,13 @@ void ObjectDrawer::DrawRightwards2x2_1to15or32(const RoomObject& obj, gfx::Backg
if (size == 0) size = 32; // Special case for object 0x00
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
WriteTile16(bg, obj.x_ + (s * 2), obj.y_, tiles[0]); // Top-left
WriteTile16(bg, obj.x_ + (s * 2) + 1, obj.y_, tiles[1]); // Top-right
WriteTile16(bg, obj.x_ + (s * 2), obj.y_ + 1, tiles[2]); // Bottom-left
WriteTile16(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tiles[3]); // Bottom-right
if (tiles.size() >= 1) {
// Draw 2x2 pattern using 8x8 tiles from the first Tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
}
}
}
@@ -261,14 +267,18 @@ void ObjectDrawer::DrawRightwards2x4_1to15or26(const RoomObject& obj, gfx::Backg
if (size == 0) size = 26; // Special case
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 2x4 pattern
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 2; x++) {
int tile_index = y * 2 + x;
WriteTile16(bg, obj.x_ + (s * 2) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Draw 2x4 pattern using 8x8 tiles from the first Tile16
const auto& tile16 = tiles[0];
// For 2x4, we'll use the same tile16 pattern repeated
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tile16.tile2_); // Mid-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tile16.tile3_); // Mid-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -279,14 +289,17 @@ void ObjectDrawer::DrawRightwards2x4spaced4_1to16(const RoomObject& obj, gfx::Ba
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 2x4 pattern with spacing
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 2; x++) {
int tile_index = y * 2 + x;
WriteTile16(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Draw 2x4 pattern with spacing using 8x8 tiles from first Tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tile16.tile2_); // Mid-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tile16.tile3_); // Mid-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -304,11 +317,13 @@ void ObjectDrawer::DrawRightwards2x2_1to16(const RoomObject& obj, gfx::Backgroun
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
WriteTile16(bg, obj.x_ + (s * 2), obj.y_, tiles[0]);
WriteTile16(bg, obj.x_ + (s * 2) + 1, obj.y_, tiles[1]);
WriteTile16(bg, obj.x_ + (s * 2), obj.y_ + 1, tiles[2]);
WriteTile16(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tiles[3]);
if (tiles.size() >= 1) {
// Draw 2x2 pattern using 8x8 tiles from first Tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
}
}
}
@@ -319,9 +334,15 @@ void ObjectDrawer::DrawDiagonalAcute_1to16(const RoomObject& obj, gfx::Backgroun
int size = obj.size_ & 0x0F;
for (int s = 0; s < size + 6; s++) {
if (tiles.size() >= 5) {
if (tiles.size() >= 1) {
// Use first tile16 for diagonal pattern
const auto& tile16 = tiles[0];
for (int i = 0; i < 5; i++) {
WriteTile16(bg, obj.x_ + s, obj.y_ + (i - s), tiles[i]);
// Cycle through the 4 tiles in the tile16
const gfx::TileInfo& tile_info = (i % 4 == 0) ? tile16.tile0_ :
(i % 4 == 1) ? tile16.tile1_ :
(i % 4 == 2) ? tile16.tile2_ : tile16.tile3_;
WriteTile8(bg, obj.x_ + s, obj.y_ + (i - s), tile_info);
}
}
}
@@ -333,9 +354,15 @@ void ObjectDrawer::DrawDiagonalGrave_1to16(const RoomObject& obj, gfx::Backgroun
int size = obj.size_ & 0x0F;
for (int s = 0; s < size + 6; s++) {
if (tiles.size() >= 5) {
if (tiles.size() >= 1) {
// Use first tile16 for diagonal pattern
const auto& tile16 = tiles[0];
for (int i = 0; i < 5; i++) {
WriteTile16(bg, obj.x_ + s, obj.y_ + (i + s), tiles[i]);
// Cycle through the 4 tiles in the tile16
const gfx::TileInfo& tile_info = (i % 4 == 0) ? tile16.tile0_ :
(i % 4 == 1) ? tile16.tile1_ :
(i % 4 == 2) ? tile16.tile2_ : tile16.tile3_;
WriteTile8(bg, obj.x_ + s, obj.y_ + (i + s), tile_info);
}
}
}
@@ -359,9 +386,11 @@ void ObjectDrawer::DrawRightwards1x2_1to16_plus2(const RoomObject& obj, gfx::Bac
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
WriteTile16(bg, obj.x_ + s + 2, obj.y_, tiles[0]);
WriteTile16(bg, obj.x_ + s + 2, obj.y_ + 1, tiles[1]);
if (tiles.size() >= 1) {
// Use first tile16 for 1x2 pattern
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 2, obj.y_, tile16.tile0_);
WriteTile8(bg, obj.x_ + s + 2, obj.y_ + 1, tile16.tile2_);
}
}
}
@@ -373,7 +402,9 @@ void ObjectDrawer::DrawRightwardsHasEdge1x1_1to16_plus3(const RoomObject& obj, g
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
WriteTile16(bg, obj.x_ + s + 3, obj.y_, tiles[0]);
// Use first 8x8 tile from first tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 3, obj.y_, tile16.tile0_);
}
}
}
@@ -385,7 +416,9 @@ void ObjectDrawer::DrawRightwardsHasEdge1x1_1to16_plus2(const RoomObject& obj, g
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
WriteTile16(bg, obj.x_ + s + 2, obj.y_, tiles[0]);
// Use first 8x8 tile from first tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 2, obj.y_, tile16.tile0_);
}
}
}
@@ -396,9 +429,11 @@ void ObjectDrawer::DrawRightwardsTopCorners1x2_1to16_plus13(const RoomObject& ob
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
WriteTile16(bg, obj.x_ + s + 13, obj.y_, tiles[0]);
WriteTile16(bg, obj.x_ + s + 13, obj.y_ + 1, tiles[1]);
if (tiles.size() >= 1) {
// Use first tile16 for 1x2 pattern
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 13, obj.y_, tile16.tile0_);
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 1, tile16.tile2_);
}
}
}
@@ -409,9 +444,11 @@ void ObjectDrawer::DrawRightwardsBottomCorners1x2_1to16_plus13(const RoomObject&
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
WriteTile16(bg, obj.x_ + s + 13, obj.y_ + 1, tiles[0]);
WriteTile16(bg, obj.x_ + s + 13, obj.y_ + 2, tiles[1]);
if (tiles.size() >= 1) {
// Use first tile16 for 1x2 pattern
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 1, tile16.tile0_);
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 2, tile16.tile2_);
}
}
}
@@ -421,7 +458,9 @@ void ObjectDrawer::CustomDraw(const RoomObject& obj, gfx::BackgroundBuffer& bg,
// Pattern: Custom draw routine (objects 0x31-0x32)
// For now, fall back to simple 1x1
if (tiles.size() >= 1) {
WriteTile16(bg, obj.x_, obj.y_, tiles[0]);
// Use first 8x8 tile from first tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_, obj.y_, tile16.tile0_);
}
}
@@ -431,14 +470,26 @@ void ObjectDrawer::DrawRightwards4x4_1to16(const RoomObject& obj, gfx::Backgroun
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 16) {
// Draw 4x4 block
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
int tile_index = y * 4 + x;
WriteTile16(bg, obj.x_ + (s * 4) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for 4x4 pattern (repeat the 2x2 pattern)
const auto& tile16 = tiles[0];
// Draw 2x2 pattern repeated to make 4x4
WriteTile8(bg, obj.x_ + (s * 4), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 4), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
WriteTile8(bg, obj.x_ + (s * 4) + 2, obj.y_, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 3, obj.y_, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 2, obj.y_ + 1, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 3, obj.y_ + 1, tile16.tile3_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 4), obj.y_ + 2, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_ + 2, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 4), obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 2, obj.y_ + 2, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 3, obj.y_ + 2, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 2, obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 3, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -450,7 +501,9 @@ void ObjectDrawer::DrawRightwards1x1Solid_1to16_plus3(const RoomObject& obj, gfx
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
WriteTile16(bg, obj.x_ + s + 3, obj.y_, tiles[0]);
// Use first 8x8 tile from first tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_ + s + 3, obj.y_, tile16.tile0_);
}
}
}
@@ -460,7 +513,9 @@ void ObjectDrawer::DrawDoorSwitcherer(const RoomObject& obj, gfx::BackgroundBuff
// Pattern: Door switcher (object 0x35)
// Special door logic - simplified for now
if (tiles.size() >= 1) {
WriteTile16(bg, obj.x_, obj.y_, tiles[0]);
// Use first 8x8 tile from first tile16
const auto& tile16 = tiles[0];
WriteTile8(bg, obj.x_, obj.y_, tile16.tile0_);
}
}
@@ -470,14 +525,26 @@ void ObjectDrawer::DrawRightwardsDecor4x4spaced2_1to16(const RoomObject& obj, gf
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 16) {
// Draw 4x4 block with spacing
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
int tile_index = y * 4 + x;
WriteTile16(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for 4x4 pattern with spacing
const auto& tile16 = tiles[0];
// Draw 2x2 pattern repeated to make 4x4 with spacing
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 1, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 1, tile16.tile3_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 2, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 2, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 2, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 2, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -488,14 +555,16 @@ void ObjectDrawer::DrawRightwardsStatue2x3spaced2_1to16(const RoomObject& obj, g
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 6) {
// Draw 2x3 statue
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 2; x++) {
int tile_index = y * 2 + x;
WriteTile16(bg, obj.x_ + (s * 4) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for 2x3 statue pattern
const auto& tile16 = tiles[0];
// Draw 2x3 pattern using 8x8 tiles
WriteTile8(bg, obj.x_ + (s * 4), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 4), obj.y_ + 1, tile16.tile2_); // Mid-left
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_ + 1, tile16.tile3_); // Mid-right
WriteTile8(bg, obj.x_ + (s * 4), obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 4) + 1, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
}
}
}
@@ -506,14 +575,18 @@ void ObjectDrawer::DrawRightwardsPillar2x4spaced4_1to16(const RoomObject& obj, g
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 2x4 pillar
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 2; x++) {
int tile_index = y * 2 + x;
WriteTile16(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for 2x4 pillar pattern
const auto& tile16 = tiles[0];
// Draw 2x4 pattern using 8x8 tiles
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tile16.tile2_); // Mid-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tile16.tile3_); // Mid-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 3, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 3, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -524,14 +597,22 @@ void ObjectDrawer::DrawRightwardsDecor4x3spaced4_1to16(const RoomObject& obj, gf
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 12) {
// Draw 4x3 decoration
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 4; x++) {
int tile_index = y * 4 + x;
WriteTile16(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for 4x3 decoration pattern
const auto& tile16 = tiles[0];
// Draw 4x3 pattern using 8x8 tiles
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tile16.tile2_); // Mid-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tile16.tile3_); // Mid-right
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 1, tile16.tile2_); // Mid-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 1, tile16.tile3_); // Mid-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 2, tile16.tile0_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 2, tile16.tile1_); // Bottom-right (repeat)
}
}
}
@@ -542,14 +623,18 @@ void ObjectDrawer::DrawRightwardsDoubled2x2spaced2_1to16(const RoomObject& obj,
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw doubled 2x2 pattern
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 4; x++) {
int tile_index = y * 4 + x;
WriteTile16(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[tile_index]);
}
}
if (tiles.size() >= 1) {
// Use first tile16 for doubled 2x2 pattern
const auto& tile16 = tiles[0];
// Draw doubled 2x2 pattern using 8x8 tiles
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_, tile16.tile0_); // Top-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_, tile16.tile1_); // Top-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
WriteTile8(bg, obj.x_ + (s * 6) + 2, obj.y_ + 1, tile16.tile2_); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 3, obj.y_ + 1, tile16.tile3_); // Bottom-right (repeat)
}
}
}
@@ -560,12 +645,14 @@ void ObjectDrawer::DrawRightwardsDecor2x2spaced12_1to16(const RoomObject& obj, g
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 decoration with 12-tile spacing
WriteTile16(bg, obj.x_ + (s * 14), obj.y_, tiles[0]);
WriteTile16(bg, obj.x_ + (s * 14) + 1, obj.y_, tiles[1]);
WriteTile16(bg, obj.x_ + (s * 14), obj.y_ + 1, tiles[2]);
WriteTile16(bg, obj.x_ + (s * 14) + 1, obj.y_ + 1, tiles[3]);
if (tiles.size() >= 1) {
// Use first tile16 for 2x2 decoration with large spacing
const auto& tile16 = tiles[0];
// Draw 2x2 decoration with 12-tile spacing using 8x8 tiles
WriteTile8(bg, obj.x_ + (s * 14), obj.y_, tile16.tile0_); // Top-left
WriteTile8(bg, obj.x_ + (s * 14) + 1, obj.y_, tile16.tile1_); // Top-right
WriteTile8(bg, obj.x_ + (s * 14), obj.y_ + 1, tile16.tile2_); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 14) + 1, obj.y_ + 1, tile16.tile3_); // Bottom-right
}
}
}
@@ -574,9 +661,9 @@ void ObjectDrawer::DrawRightwardsDecor2x2spaced12_1to16(const RoomObject& obj, g
// Utility Methods
// ============================================================================
void ObjectDrawer::WriteTile16(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::Tile16& tile) {
LOG_DEBUG("ObjectDrawer", "Writing Tile16 at tile pos (%d,%d) to bitmap", tile_x, tile_y);
void ObjectDrawer::WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::TileInfo& tile_info) {
LOG_DEBUG("ObjectDrawer", "Writing 8x8 tile at tile pos (%d,%d) to bitmap", tile_x, tile_y);
// Draw directly to bitmap instead of tile buffer to avoid being overwritten
auto& bitmap = bg.bitmap();
@@ -587,21 +674,27 @@ void ObjectDrawer::WriteTile16(gfx::BackgroundBuffer& bg, int tile_x, int tile_y
return; // Bitmap not ready
}
// Get graphics data from ROM
if (!rom_ || !rom_->is_loaded()) {
return;
// Get graphics data - prefer room-specific buffer if available
const uint8_t* gfx_data = nullptr;
if (room_gfx_buffer_) {
// Use room-specific graphics buffer (current_gfx16_)
gfx_data = room_gfx_buffer_;
} else if (rom_ && rom_->is_loaded()) {
// Fallback to ROM graphics buffer
auto rom_gfx = rom_->mutable_graphics_buffer();
if (rom_gfx && !rom_gfx->empty()) {
gfx_data = rom_gfx->data();
}
}
auto gfx_data = rom_->mutable_graphics_buffer();
if (!gfx_data || gfx_data->empty()) {
if (!gfx_data) {
LOG_DEBUG("ObjectDrawer", "ERROR: No graphics data available");
return;
}
// Draw each 8x8 tile directly to bitmap
DrawTileToBitmap(bitmap, tile.tile0_, tile_x * 8, tile_y * 8, gfx_data->data());
DrawTileToBitmap(bitmap, tile.tile1_, (tile_x + 1) * 8, tile_y * 8, gfx_data->data());
DrawTileToBitmap(bitmap, tile.tile2_, tile_x * 8, (tile_y + 1) * 8, gfx_data->data());
DrawTileToBitmap(bitmap, tile.tile3_, (tile_x + 1) * 8, (tile_y + 1) * 8, gfx_data->data());
// Draw single 8x8 tile directly to bitmap
DrawTileToBitmap(bitmap, tile_info, tile_x * 8, tile_y * 8, gfx_data);
}
bool ObjectDrawer::IsValidTilePosition(int tile_x, int tile_y) const {

View File

@@ -31,7 +31,7 @@ namespace zelda3 {
*/
class ObjectDrawer {
public:
explicit ObjectDrawer(Rom* rom);
explicit ObjectDrawer(Rom* rom, const uint8_t* room_gfx_buffer = nullptr);
/**
* @brief Draw a room object to background buffers
@@ -139,8 +139,8 @@ class ObjectDrawer {
const std::vector<gfx::Tile16>& tiles);
// Utility methods
void WriteTile16(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::Tile16& tile);
void WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::TileInfo& tile_info);
bool IsValidTilePosition(int tile_x, int tile_y) const;
// Draw routine registry
@@ -149,6 +149,7 @@ class ObjectDrawer {
bool routines_initialized_ = false;
Rom* rom_;
const uint8_t* room_gfx_buffer_; // Room-specific graphics buffer (current_gfx16_)
// Canvas dimensions in tiles (64x64 = 512x512 pixels)
static constexpr int kMaxTilesX = 64;

View File

@@ -292,12 +292,17 @@ void Room::RenderRoomGraphics() {
// LoadGraphicsSheetsIntoArena() removed - using per-room graphics instead
// Arena sheets are optional and not needed for room rendering
// STEP 1: Draw floor tiles to bitmaps (base layer)
bg1_buffer_.DrawFloor(rom()->vector(), tile_address,
tile_address_floor, floor1_graphics_);
bg2_buffer_.DrawFloor(rom()->vector(), tile_address,
tile_address_floor, floor2_graphics_);
// Draw background tiles (floor, walls, etc.) to buffers
// STEP 2: Load layout tiles into tile buffer before DrawBackground
// This populates the buffer with wall/structure tiles from the layout
LoadLayoutTilesToBuffer();
// STEP 3: Draw background tiles (walls from layout) to buffers
bg1_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
bg2_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
@@ -372,7 +377,7 @@ void Room::RenderObjectsToBackground() {
if (!palette_group_result.ok()) {
// Fallback to empty palette group
gfx::PaletteGroup empty_group;
ObjectDrawer drawer(rom_);
ObjectDrawer drawer(rom_, current_gfx16_.data());
drawer.DrawObjectList(tile_objects_, bg1_buffer_, bg2_buffer_, empty_group);
return;
}
@@ -380,7 +385,8 @@ void Room::RenderObjectsToBackground() {
// Use ObjectDrawer for pattern-based object rendering
// This provides proper wall/object drawing patterns
ObjectDrawer drawer(rom_);
// Pass the room-specific graphics buffer (current_gfx16_) so objects use correct tiles
ObjectDrawer drawer(rom_, current_gfx16_.data());
auto status = drawer.DrawObjectList(tile_objects_, bg1_buffer_, bg2_buffer_, palette_group);
// Log only failures, not successes
@@ -858,6 +864,73 @@ void Room::LoadRoomLayout() {
layout = static_cast<uint8_t>(room_id_ & 0xFF);
}
void Room::LoadLayoutTilesToBuffer() {
// Load layout tiles into the BackgroundBuffer tile buffers
// This populates the buffer with wall/structure tile IDs from the layout
LOG_DEBUG("RenderRoomGraphics", "LoadLayoutTilesToBuffer START for room %d", room_id_);
if (!rom_ || !rom_->is_loaded()) {
LOG_DEBUG("RenderRoomGraphics", "ROM not loaded, aborting");
return;
}
// Get layout data
auto& layout_objects = layout_.GetObjects();
LOG_DEBUG("RenderRoomGraphics", "Layout has %zu objects", layout_objects.size());
if (layout_objects.empty()) {
LOG_DEBUG("RenderRoomGraphics", "No layout objects for room %d", room_id_);
return;
}
int tiles_written_bg1 = 0;
int tiles_written_bg2 = 0;
int tiles_skipped = 0;
// Write each layout object's tile to the appropriate buffer
for (const auto& layout_obj : layout_objects) {
uint8_t x = layout_obj.x();
uint8_t y = layout_obj.y();
// Get the tile16 for this layout object, passing room graphics buffer
auto tile_result = layout_obj.GetTile(current_gfx16_.data());
if (!tile_result.ok()) {
tiles_skipped++;
continue; // Skip objects that don't have valid tiles
}
auto& tile16 = tile_result.value();
// Convert Tile16 to a 16-bit tile ID word (use first tile)
uint16_t tile_word = gfx::TileInfoToWord(tile16.tile0_);
// Debug first few tiles to verify data
if (tiles_written_bg1 + tiles_written_bg2 < 5) {
LOG_DEBUG("RenderRoomGraphics", "Layout tile[%d] at (%d,%d): ID=0x%04X, palette=%d, type=%d, layer=%d",
tiles_written_bg1 + tiles_written_bg2, x, y, tile16.tile0_.id_, tile16.tile0_.palette_,
static_cast<int>(layout_obj.type()), layout_obj.layer());
}
// Determine which buffer based on layer
auto* target_buffer = &bg1_buffer_;
if (layout_obj.layer() == 1) {
target_buffer = &bg2_buffer_;
tiles_written_bg2++;
} else {
tiles_written_bg1++;
}
// Write tile ID to buffer at position (x, y)
if (x < 64 && y < 64) { // 64x64 tiles = 512x512 pixels
target_buffer->SetTileAt(x, y, tile_word);
}
}
LOG_DEBUG("RenderRoomGraphics", "Wrote %d BG1 tiles, %d BG2 tiles, skipped %d",
tiles_written_bg1, tiles_written_bg2, tiles_skipped);
}
void Room::LoadDoors() {
auto rom_data = rom()->vector();

View File

@@ -194,6 +194,7 @@ class Room {
void LoadSprites();
void LoadChests();
void LoadRoomLayout();
void LoadLayoutTilesToBuffer(); // NEW: Write layout tiles to BG tile buffers
void LoadDoors();
void LoadTorches();
void LoadBlocks();

View File

@@ -7,18 +7,48 @@
namespace yaze {
namespace zelda3 {
absl::StatusOr<gfx::Tile16> RoomLayoutObject::GetTile() const {
// This would typically look up the actual tile data from the graphics sheets
// For now, we'll create a placeholder tile based on the object type
absl::StatusOr<gfx::Tile16> RoomLayoutObject::GetTile(const uint8_t* room_gfx_buffer) const {
// Map layout code to actual VRAM tile ID
// Layout codes (id_) are indices into a layout tilemap
// The actual tile graphics are in the room's graphics buffer
// For dungeon layouts, the tile ID from the layout data directly maps to
// a tile in the room's graphics sheets (current_gfx16_)
// Layout codes typically range from 0x00 to 0xFF
// Use the layout code directly as tile ID
// The palette will be determined by the tile's position and room palette
uint16_t tile_id = static_cast<uint16_t>(id_);
// Determine palette based on object type
uint8_t palette = 0;
switch (type_) {
case Type::kWall:
palette = 2; // Walls typically use palette 2
break;
case Type::kFloor:
palette = 0; // Floors use palette 0
break;
case Type::kWater:
palette = 4; // Water uses palette 4
break;
case Type::kDoor:
palette = 3; // Doors use palette 3
break;
default:
palette = 0;
break;
}
gfx::TileInfo tile_info;
tile_info.id_ = static_cast<uint16_t>(id_);
tile_info.palette_ = 0; // Default palette
tile_info.id_ = tile_id;
tile_info.palette_ = palette;
tile_info.vertical_mirror_ = false;
tile_info.horizontal_mirror_ = false;
tile_info.over_ = false;
// Create a 16x16 tile with the same tile info for all 4 sub-tiles
// Create a Tile16 with the same 8x8 tile in all 4 positions
// This makes the layout tile appear as a single repeated pattern
return gfx::Tile16(tile_info, tile_info, tile_info, tile_info);
}

View File

@@ -51,7 +51,9 @@ class RoomLayoutObject {
void set_layer(uint8_t layer) { layer_ = layer; }
// Get tile data for this layout object
absl::StatusOr<gfx::Tile16> GetTile() const;
// NOTE: Layout codes need to be mapped to actual VRAM tile IDs
// The room_gfx_buffer provides the assembled graphics for this specific room
absl::StatusOr<gfx::Tile16> GetTile(const uint8_t* room_gfx_buffer = nullptr) const;
// Get the name/description of this layout object type
std::string GetTypeName() const;

View File

@@ -46,112 +46,8 @@ ObjectOption operator~(ObjectOption option) {
return static_cast<ObjectOption>(~static_cast<int>(option));
}
SubtypeInfo FetchSubtypeInfo(uint16_t object_id) {
SubtypeInfo info;
// TODO: Determine the subtype based on object_id
uint8_t subtype = 1;
switch (subtype) {
case 1: // Subtype 1
info.subtype_ptr = kRoomObjectSubtype1 + (object_id & 0xFF) * 2;
info.routine_ptr = kRoomObjectSubtype1 + 0x200 + (object_id & 0xFF) * 2;
break;
case 2: // Subtype 2
info.subtype_ptr = kRoomObjectSubtype2 + (object_id & 0x7F) * 2;
info.routine_ptr = kRoomObjectSubtype2 + 0x80 + (object_id & 0x7F) * 2;
break;
case 3: // Subtype 3
info.subtype_ptr = kRoomObjectSubtype3 + (object_id & 0xFF) * 2;
info.routine_ptr = kRoomObjectSubtype3 + 0x100 + (object_id & 0xFF) * 2;
break;
default:
throw std::runtime_error("Invalid object subtype");
}
return info;
}
void RoomObject::DrawTile(gfx::Tile16 t, int xx, int yy,
std::vector<uint8_t>& current_gfx16,
std::vector<uint8_t>& tiles_bg1_buffer,
std::vector<uint8_t>& tiles_bg2_buffer,
uint16_t tileUnder) {
bool preview = false;
if (width_ < xx + 8) {
width_ = xx + 8;
}
if (height_ < yy + 8) {
height_ = yy + 8;
}
if (preview) {
if (xx < 0x39 && yy < 0x39 && xx >= 0 && yy >= 0) {
gfx::TileInfo ti = t.tile0_; // t.GetTileInfo();
for (auto yl = 0; yl < 8; yl++) {
for (auto xl = 0; xl < 4; xl++) {
int mx = xl;
int my = yl;
uint8_t r = 0;
if (ti.horizontal_mirror_) {
mx = 3 - xl;
r = 1;
}
if (ti.vertical_mirror_) {
my = 7 - yl;
}
// Formula information to get tile index position in the array.
//((ID / nbrofXtiles) * (imgwidth/2) + (ID - ((ID/16)*16) ))
int tx = ((ti.id_ / 0x10) * 0x200) +
((ti.id_ - ((ti.id_ / 0x10) * 0x10)) * 4);
auto pixel = current_gfx16[tx + (yl * 0x40) + xl];
// nx,ny = object position, xx,yy = tile position, xl,yl = pixel
// position
int index =
((xx / 8) * 8) + ((yy / 8) * 0x200) + ((mx * 2) + (my * 0x40));
preview_object_data_[index + r ^ 1] =
(uint8_t)((pixel & 0x0F) + ti.palette_ * 0x10);
preview_object_data_[index + r] =
(uint8_t)(((pixel >> 4) & 0x0F) + ti.palette_ * 0x10);
}
}
}
} else {
if (((xx / 8) + nx_ + offset_x_) + ((ny_ + offset_y_ + (yy / 8)) * 0x40) <
0x1000 &&
((xx / 8) + nx_ + offset_x_) + ((ny_ + offset_y_ + (yy / 8)) * 0x40) >=
0) {
uint16_t td = 0; // gfx::GetTilesInfo();
// collisionPoint.Add(
// new Point(xx + ((nx + offsetX) * 8), yy + ((ny + +offsetY) * 8)));
if (layer_ == 0 || (uint8_t)layer_ == 2 || all_bgs_) {
if (tileUnder ==
tiles_bg1_buffer[((xx / 8) + offset_x_ + nx_) +
((ny_ + offset_y_ + (yy / 8)) * 0x40)]) {
return;
}
tiles_bg1_buffer[((xx / 8) + offset_x_ + nx_) +
((ny_ + offset_y_ + (yy / 8)) * 0x40)] = td;
}
if ((uint8_t)layer_ == 1 || all_bgs_) {
if (tileUnder ==
tiles_bg2_buffer[((xx / 8) + nx_ + offset_x_) +
((ny_ + offset_y_ + (yy / 8)) * 0x40)]) {
return;
}
tiles_bg2_buffer[((xx / 8) + nx_ + offset_x_) +
((ny_ + offset_y_ + (yy / 8)) * 0x40)] = td;
}
}
}
}
// NOTE: DrawTile was legacy ZScream code that is no longer used.
// Modern rendering uses ObjectDrawer which draws directly to BackgroundBuffer bitmaps.
void RoomObject::EnsureTilesLoaded() {
LOG_DEBUG("RoomObject", "Object ID=0x%02X, tiles_loaded=%d", id_, tiles_loaded_);

View File

@@ -12,13 +12,6 @@
namespace yaze {
namespace zelda3 {
struct SubtypeInfo {
uint32_t subtype_ptr;
uint32_t routine_ptr;
};
SubtypeInfo FetchSubtypeInfo(uint16_t object_id);
enum class SpecialObjectType { Chest, BigChest, InterroomStairs };
enum Sorting {
@@ -132,27 +125,9 @@ class RoomObject {
// ============================================================================
void AddTiles(int nbr, int pos) {
// Reads nbr Tile16 entries from ROM object data starting at pos (8 bytes per Tile16)
for (int i = 0; i < nbr; i++) {
int tpos = pos + (i * 8);
auto rom_data = rom()->data();
if (tpos + 7 >= (int)rom()->size()) break;
uint16_t w0 = (uint16_t)(rom_data[tpos] | (rom_data[tpos + 1] << 8));
uint16_t w1 = (uint16_t)(rom_data[tpos + 2] | (rom_data[tpos + 3] << 8));
uint16_t w2 = (uint16_t)(rom_data[tpos + 4] | (rom_data[tpos + 5] << 8));
uint16_t w3 = (uint16_t)(rom_data[tpos + 6] | (rom_data[tpos + 7] << 8));
gfx::Tile16 tile(gfx::WordToTileInfo(w0), gfx::WordToTileInfo(w1),
gfx::WordToTileInfo(w2), gfx::WordToTileInfo(w3));
tiles_.push_back(tile);
}
}
void DrawTile(gfx::Tile16 t, int xx, int yy,
std::vector<uint8_t>& current_gfx16,
std::vector<uint8_t>& tiles_bg1_buffer,
std::vector<uint8_t>& tiles_bg2_buffer,
uint16_t tile_under = 0xFFFF);
// NOTE: Legacy ZScream methods removed. Modern rendering uses:
// - ObjectParser for loading tiles from ROM
// - ObjectDrawer for rendering tiles to BackgroundBuffer
auto options() const { return options_; }
void set_options(ObjectOption options) { options_ = options; }
@@ -197,90 +172,9 @@ class RoomObject {
Rom* rom_;
};
class Subtype1 : public RoomObject {
public:
bool all_bgs;
int tile_count_;
std::string name;
Sorting sort;
Subtype1(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer,
int tile_count)
: RoomObject(id, x, y, size, layer), tile_count_(tile_count) {
auto rom_data = rom()->data();
int pos = kRoomObjectTileAddress +
static_cast<int16_t>(
(rom_data[kRoomObjectSubtype1 + ((id & 0xFF) * 2) + 1] << 8) +
rom_data[kRoomObjectSubtype1 + ((id & 0xFF) * 2)]);
AddTiles(tile_count_, pos);
sort = (Sorting)(Sorting::Horizontal | Sorting::Wall);
}
void Draw(std::vector<uint8_t>& current_gfx16,
std::vector<uint8_t>& tiles_bg1_buffer,
std::vector<uint8_t>& tiles_bg2_buffer) {
for (int s = 0; s < size_ + (tile_count_ == 8 ? 1 : 0); s++) {
for (int i = 0; i < tile_count_; i++) {
DrawTile(tiles_[i], ((s * 2)) * 8, (i / 2) * 8, current_gfx16,
tiles_bg1_buffer, tiles_bg2_buffer);
}
}
}
};
class Subtype2 : public RoomObject {
public:
std::string name;
bool all_bgs;
Sorting sort;
Subtype2(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer)
: RoomObject(id, x, y, size, layer) {
auto rom_data = rom()->data();
int pos = kRoomObjectTileAddress +
static_cast<int16_t>(
(rom_data[kRoomObjectSubtype2 + ((id & 0x7F) * 2) + 1] << 8) +
rom_data[kRoomObjectSubtype2 + ((id & 0x7F) * 2)]);
AddTiles(8, pos);
sort = (Sorting)(Sorting::Horizontal | Sorting::Wall);
}
void Draw(std::vector<uint8_t>& current_gfx16,
std::vector<uint8_t>& tiles_bg1_buffer,
std::vector<uint8_t>& tiles_bg2_buffer) {
for (int i = 0; i < 8; i++) {
DrawTile(tiles_[i], x_ * 8, (y_ + i) * 8, current_gfx16, tiles_bg1_buffer,
tiles_bg2_buffer);
}
}
};
class Subtype3 : public RoomObject {
public:
bool all_bgs;
std::string name;
Sorting sort;
Subtype3(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer)
: RoomObject(id, x, y, size, layer) {
auto rom_data = rom()->data();
int pos = kRoomObjectTileAddress +
static_cast<int16_t>(
(rom_data[kRoomObjectSubtype3 + ((id & 0xFF) * 2) + 1] << 8) +
rom_data[kRoomObjectSubtype3 + ((id & 0xFF) * 2)]);
AddTiles(8, pos);
sort = (Sorting)(Sorting::Horizontal | Sorting::Wall);
}
void Draw(std::vector<uint8_t>& current_gfx16,
std::vector<uint8_t>& tiles_bg1_buffer,
std::vector<uint8_t>& tiles_bg2_buffer) {
for (int i = 0; i < 8; i++) {
DrawTile(tiles_[i], x_ * 8, (y_ + i) * 8, current_gfx16, tiles_bg1_buffer,
tiles_bg2_buffer);
}
}
};
// NOTE: Legacy Subtype1, Subtype2, Subtype3 classes removed.
// These were ported from ZScream but are no longer used.
// Modern code uses: ObjectParser + ObjectDrawer + ObjectRenderer
constexpr static inline const char* Type1RoomObjectNames[] = {
"Ceiling ↔",