From e006702df1ad4e1a9a1996622a399723e5553e1a Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 28 Jan 2024 12:04:52 -0500 Subject: [PATCH] OverworldMap sprite entities, canvas drawing updates --- src/app/editor/graphics_editor.cc | 7 +- src/app/editor/overworld_editor.cc | 482 ++++++++++++++++++++++++----- src/app/editor/overworld_editor.h | 28 +- src/app/gui/canvas.cc | 160 ++++++---- src/app/gui/canvas.h | 14 +- src/app/zelda3/common.h | 38 +++ src/app/zelda3/overworld.cc | 143 +++++---- src/app/zelda3/overworld.h | 64 +--- src/app/zelda3/overworld_map.cc | 294 +++++++++--------- src/app/zelda3/overworld_map.h | 8 +- src/app/zelda3/sprite/sprite.cc | 33 +- src/app/zelda3/sprite/sprite.h | 15 +- 12 files changed, 872 insertions(+), 414 deletions(-) create mode 100644 src/app/zelda3/common.h diff --git a/src/app/editor/graphics_editor.cc b/src/app/editor/graphics_editor.cc index 39c80e2d..fa42a7da 100644 --- a/src/app/editor/graphics_editor.cc +++ b/src/app/editor/graphics_editor.cc @@ -253,9 +253,10 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + gfx::Bitmap& current_bitmap = + *rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id); + auto draw_tile_event = [&]() { - gfx::Bitmap& current_bitmap = - *rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id); current_sheet_canvas_.DrawTileOnBitmap(tile_size_, current_bitmap, current_color_); rom()->UpdateBitmap(¤t_bitmap); @@ -264,6 +265,8 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { current_sheet_canvas_.UpdateColorPainter( *rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event, tile_size_, current_scale_); + + ImGui::EndChild(); ImGui::EndTabItem(); } diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index 063dc075..f2efe1ce 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -44,7 +44,8 @@ using ImGui::Text; absl::Status OverworldEditor::Update() { if (rom()->is_loaded() && !all_gfx_loaded_) { RETURN_IF_ERROR(tile16_editor_.InitBlockset( - tile16_blockset_bmp_, current_gfx_bmp_, tile16_individual_)); + tile16_blockset_bmp_, current_gfx_bmp_, tile16_individual_, + *overworld_.mutable_all_tiles_types())); gfx_group_editor_.InitBlockset(tile16_blockset_bmp_); all_gfx_loaded_ = true; } else if (!rom()->is_loaded() && all_gfx_loaded_) { @@ -286,15 +287,16 @@ void OverworldEditor::RefreshChildMap(int map_index) { } else { blockset = overworld_.map_tiles().special_world; } + status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(blockset); maps_bmp_[map_index].set_data( overworld_.mutable_overworld_map(map_index)->BitmapData()); - status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(blockset); + maps_bmp_[map_index].set_modified(true); PRINT_IF_ERROR(status_); - rom()->UpdateBitmap(&maps_bmp_[map_index]); } void OverworldEditor::RefreshOverworldMap() { std::vector> futures; + int indices[4]; auto refresh_map_async = [this](int map_index) { RefreshChildMap(map_index); @@ -302,50 +304,49 @@ void OverworldEditor::RefreshOverworldMap() { if (overworld_.overworld_map(current_map_)->IsLargeMap()) { // We need to update the map and its siblings if it's a large map - for (int i = 0; i < 4; i++) { + for (int i = 1; i < 4; i++) { int sibling_index = overworld_.overworld_map(current_map_)->Parent() + i; - if (i >= 2) { - sibling_index += 6; - } + if (i >= 2) sibling_index += 6; futures.push_back( std::async(std::launch::async, refresh_map_async, sibling_index)); + indices[i] = sibling_index; } - } else { - futures.push_back( - std::async(std::launch::async, refresh_map_async, current_map_)); } + indices[0] = current_map_; + futures.push_back( + std::async(std::launch::async, refresh_map_async, current_map_)); for (auto &each : futures) { each.get(); } + + // We do texture updating on the main thread + for (int i = 0; i < 4; ++i) { + rom()->UpdateBitmap(&maps_bmp_[indices[i]]); + } } // TODO: Palette throws out of bounds error unexpectedly. void OverworldEditor::RefreshMapPalette() { std::vector> futures; - auto refresh_palette_async = [this](int map_index) { overworld_.mutable_overworld_map(map_index)->LoadPalette(); maps_bmp_[map_index].ApplyPalette( *overworld_.mutable_overworld_map(map_index) ->mutable_current_palette()); - rom()->UpdateBitmap(&maps_bmp_[map_index]); }; if (overworld_.overworld_map(current_map_)->IsLargeMap()) { // We need to update the map and its siblings if it's a large map - for (int i = 0; i < 4; i++) { + for (int i = 1; i < 4; i++) { int sibling_index = overworld_.overworld_map(current_map_)->Parent() + i; - if (i >= 2) { - sibling_index += 6; - } + if (i >= 2) sibling_index += 6; futures.push_back( std::async(std::launch::async, refresh_palette_async, sibling_index)); } - } else { - futures.push_back( - std::async(std::launch::async, refresh_palette_async, current_map_)); } + futures.push_back( + std::async(std::launch::async, refresh_palette_async, current_map_)); for (auto &each : futures) { each.get(); @@ -495,12 +496,16 @@ void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0, } else if (entity->type_ == zelda3::OverworldEntity::EntityType::kItem) { entity_type = "Item"; } - if (IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling) && - ImGui::IsMouseDragging(ImGuiMouseButton_Left) && !is_dragging_entity) { + const auto is_hovering = + IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling); + + const auto drag_or_clicked = ImGui::IsMouseDragging(ImGuiMouseButton_Left) || + ImGui::IsMouseClicked(ImGuiMouseButton_Left); + + if (is_hovering && drag_or_clicked && !is_dragging_entity) { dragged_entity = entity; is_dragging_entity = true; - } else if (IsMouseHoveringOverEntity(*entity, canvas_p0, scrolling) && - ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + } else if (is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { current_entity = entity; ImGui::OpenPopup(absl::StrFormat("%s editor", entity_type.c_str()).c_str()); } else if (is_dragging_entity && dragged_entity == entity && @@ -528,6 +533,24 @@ void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0, namespace entrance_internal { +void DrawEntranceInserterPopup() { + if (ImGui::BeginPopup("Entrance Inserter")) { + static int entrance_id = 0; + gui::InputHex("Entrance ID", &entrance_id); + + if (ImGui::Button(ICON_MD_DONE)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_CANCEL)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance) { static bool set_done = false; if (set_done) { @@ -598,14 +621,42 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling, i++; } - if (entrance_internal::DrawOverworldEntrancePopup( - overworld_.Entrances()[current_entrance_id_])) { - overworld_.Entrances()[current_entrance_id_] = current_entrance_; + entrance_internal::DrawEntranceInserterPopup(); + if (current_mode == EditingMode::ENTRANCES) { + const auto is_hovering = entity_internal::IsMouseHoveringOverEntity( + current_entrance_, canvas_p0, scrolling); + + if (!is_hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Entrance Inserter"); + } else { + if (entrance_internal::DrawOverworldEntrancePopup( + overworld_.Entrances()[current_entrance_id_])) { + overworld_.Entrances()[current_entrance_id_] = current_entrance_; + } + } } } namespace exit_internal { +void DrawExitInserterPopup() { + if (ImGui::BeginPopup("Exit Inserter")) { + static int exit_id = 0; + gui::InputHex("Exit ID", &exit_id); + + if (ImGui::Button(ICON_MD_DONE)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_CANCEL)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + bool DrawExitEditorPopup(zelda3::OverworldExit &exit) { static bool set_done = false; if (set_done) { @@ -661,7 +712,7 @@ bool DrawExitEditorPopup(zelda3::OverworldExit &exit) { static bool show_properties = false; ImGui::Checkbox("Show properties", &show_properties); if (show_properties) { - ImGui::Text("Deleted? %s", exit.deleted ? "true" : "false"); + ImGui::Text("Deleted? %s", exit.deleted_ ? "true" : "false"); ImGui::Text("Hole? %s", exit.is_hole_ ? "true" : "false"); ImGui::Text("Large Map? %s", exit.large_map_ ? "true" : "false"); } @@ -728,7 +779,7 @@ bool DrawExitEditorPopup(zelda3::OverworldExit &exit) { ImGui::SameLine(); if (ImGui::Button(ICON_MD_DELETE)) { - exit.deleted = true; + exit.deleted_ = true; ImGui::CloseCurrentPopup(); } @@ -744,7 +795,7 @@ void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) { int i = 0; for (auto &each : *overworld_.mutable_exits()) { if (each.map_id_ < 0x40 + (current_world_ * 0x40) && - each.map_id_ >= (current_world_ * 0x40) && !each.deleted) { + each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) { ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, ImVec4(255, 255, 255, 150)); if (current_mode == EditingMode::EXITS) { @@ -776,14 +827,55 @@ void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) { i++; } - if (exit_internal::DrawExitEditorPopup( - overworld_.mutable_exits()->at(current_exit_id_))) { - overworld_.mutable_exits()->at(current_exit_id_) = current_exit_; + exit_internal::DrawExitInserterPopup(); + if (current_mode == EditingMode::EXITS) { + const auto hovering = entity_internal::IsMouseHoveringOverEntity( + overworld_.mutable_exits()->at(current_exit_id_), + ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Exit Inserter"); + } else { + if (exit_internal::DrawExitEditorPopup( + overworld_.mutable_exits()->at(current_exit_id_))) { + overworld_.mutable_exits()->at(current_exit_id_) = current_exit_; + } + } } } namespace item_internal { +void DrawItemInsertPopup() { + // Contents of the Context Menu + if (ImGui::BeginPopup("Item Inserter")) { + static int new_item_id = 0; + ImGui::Text("Add Item"); + ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar); + for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) { + if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(), + i == new_item_id)) { + new_item_id = i; + } + } + ImGui::EndChild(); + + if (ImGui::Button(ICON_MD_DONE)) { + // Add the new item to the overworld + new_item_id = 0; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + + if (ImGui::Button(ICON_MD_CANCEL)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + bool DrawItemEditorPopup(zelda3::OverworldItem &item) { static bool set_done = false; if (set_done) { @@ -791,7 +883,7 @@ bool DrawItemEditorPopup(zelda3::OverworldItem &item) { } if (ImGui::BeginPopupModal("Item editor", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::BeginChild("ScrollRegion", ImVec2(0, 150), true, + ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); ImGui::BeginGroup(); for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) { @@ -831,34 +923,203 @@ void OverworldEditor::DrawOverworldItems() { std::string item_name = zelda3::kSecretItemNames[item.id]; ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150)); - if (current_mode == EditingMode::ITEMS) { - if (entity_internal::IsMouseHoveringOverEntity( - item, ow_map_canvas_.zero_point(), - ow_map_canvas_.scrolling()) && - ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - current_item_id_ = i; - current_item_ = item; - } + if (current_mode == EditingMode::ITEMS) { // Check if this item is being clicked and dragged entity_internal::HandleEntityDragging( &item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(), is_dragging_entity_, dragged_entity_, current_entity_); + + const auto hovering = entity_internal::IsMouseHoveringOverEntity( + item, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); + if (hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_item_id_ = i; + current_item_ = item; + current_entity_ = &item; + } } ow_map_canvas_.DrawText(item_name, item.x_, item.y_); } i++; } - if (item_internal::DrawItemEditorPopup( - overworld_.mutable_all_items()->at(current_item_id_))) { - overworld_.mutable_all_items()->at(current_item_id_) = current_item_; + item_internal::DrawItemInsertPopup(); + if (current_mode == EditingMode::ITEMS) { + const auto hovering = entity_internal::IsMouseHoveringOverEntity( + overworld_.mutable_all_items()->at(current_item_id_), + ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Item Inserter"); + } else { + if (item_internal::DrawItemEditorPopup( + overworld_.mutable_all_items()->at(current_item_id_))) { + overworld_.mutable_all_items()->at(current_item_id_) = current_item_; + } + } } } -// ---------------------------------------------------------------------------- +namespace sprite_internal { + +enum MyItemColumnID { + MyItemColumnID_ID, + MyItemColumnID_Name, + MyItemColumnID_Action, + MyItemColumnID_Quantity, + MyItemColumnID_Description +}; + +struct SpriteItem { + int id; + const char *name; + static const ImGuiTableSortSpecs *s_current_sort_specs; + + static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs, + std::vector &items) { + s_current_sort_specs = + sort_specs; // Store for access by the compare function. + if (items.size() > 1) + std::sort(items.begin(), items.end(), SpriteItem::CompareWithSortSpecs); + s_current_sort_specs = nullptr; + } + + static bool CompareWithSortSpecs(const SpriteItem &a, const SpriteItem &b) { + for (int n = 0; n < s_current_sort_specs->SpecsCount; n++) { + const ImGuiTableColumnSortSpecs *sort_spec = + &s_current_sort_specs->Specs[n]; + int delta = 0; + switch (sort_spec->ColumnUserID) { + case MyItemColumnID_ID: + delta = (a.id - b.id); + break; + case MyItemColumnID_Name: + delta = strcmp(a.name + 2, b.name + 2); + break; + } + if (delta != 0) + return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) + ? delta < 0 + : delta > 0; + } + return a.id < b.id; // Fallback + } +}; +const ImGuiTableSortSpecs *SpriteItem::s_current_sort_specs = nullptr; + +void DrawSpriteTable(std::function onSpriteSelect) { + static ImGuiTextFilter filter; + static int selected_id = 0; + static std::vector items; + + // Initialize items if empty + if (items.empty()) { + for (int i = 0; i < 256; ++i) { + items.push_back(SpriteItem{i, core::kSpriteDefaultNames[i].data()}); + } + } + + filter.Draw("Filter", 180); + + if (ImGui::BeginTable("##sprites", 2, + ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) { + ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort, 0.0f, + MyItemColumnID_ID); + ImGui::TableSetupColumn("Name", 0, 0.0f, MyItemColumnID_Name); + ImGui::TableHeadersRow(); + + // Handle sorting + if (ImGuiTableSortSpecs *sort_specs = ImGui::TableGetSortSpecs()) { + if (sort_specs->SpecsDirty) { + SpriteItem::SortWithSortSpecs(sort_specs, items); + sort_specs->SpecsDirty = false; + } + } + + // Display filtered and sorted items + for (const auto &item : items) { + if (filter.PassFilter(item.name)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("%d", item.id); + ImGui::TableSetColumnIndex(1); + + if (ImGui::Selectable(item.name, selected_id == item.id, + ImGuiSelectableFlags_SpanAllColumns)) { + selected_id = item.id; + onSpriteSelect(item.id); + } + } + } + ImGui::EndTable(); + } +} + +void DrawSpriteInserterPopup() { + if (ImGui::BeginPopup("Sprite Inserter")) { + static int new_sprite_id = 0; + ImGui::Text("Add Sprite"); + ImGui::BeginChild("ScrollRegion", ImVec2(250, 250), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar); + sprite_internal::DrawSpriteTable( + [](int selected_id) { new_sprite_id = selected_id; }); + ImGui::EndChild(); + + if (ImGui::Button(ICON_MD_DONE)) { + // Add the new item to the overworld + new_sprite_id = 0; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + + if (ImGui::Button(ICON_MD_CANCEL)) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +bool DrawSpriteEditorPopup(zelda3::Sprite &sprite) { + static bool set_done = false; + if (set_done) { + set_done = false; + } + if (ImGui::BeginPopupModal("Sprite editor", NULL, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::BeginChild("ScrollRegion", ImVec2(350, 350), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::BeginGroup(); + ImGui::Text("%s", sprite.Name().c_str()); + + sprite_internal::DrawSpriteTable([&sprite](int selected_id) { + sprite.set_id(selected_id); + sprite.UpdateMapProperties(sprite.map_id()); + }); + ImGui::EndGroup(); + ImGui::EndChild(); + + if (ImGui::Button(ICON_MD_DONE)) ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_CLOSE)) { + set_done = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_DELETE)) { + sprite.set_deleted(true); + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + return set_done; +} + +} // namespace sprite_internal void OverworldEditor::DrawOverworldSprites() { + int i = 0; for (auto &sprite : *overworld_.mutable_sprites(game_state_)) { int map_id = sprite.map_id(); int map_x = sprite.map_x(); @@ -866,14 +1127,43 @@ void OverworldEditor::DrawOverworldSprites() { if (map_id < 0x40 + (current_world_ * 0x40) && map_id >= (current_world_ * 0x40) && !sprite.deleted()) { - // auto globalx = (((map_id & 0x7) * 512) + (map_x * 16)); - // auto globaly = (((map_id & 0x3F) / 8 * 512) + (map_y * 16)); - ow_map_canvas_.DrawRect(map_x, map_y, 16, 16, /*magenta*/ ImVec4(255, 0, 255, 150)); + if (current_mode == EditingMode::SPRITES) { + entity_internal::HandleEntityDragging( + &sprite, ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling(), + is_dragging_entity_, dragged_entity_, current_entity_); + if (entity_internal::IsMouseHoveringOverEntity( + sprite, ow_map_canvas_.zero_point(), + ow_map_canvas_.scrolling()) && + ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + current_sprite_id_ = i; + current_sprite_ = sprite; + } + } + ow_map_canvas_.DrawText(absl::StrFormat("%s", sprite.Name()), map_x, map_y); } + i++; + } + + sprite_internal::DrawSpriteInserterPopup(); + if (current_mode == EditingMode::SPRITES) { + const auto hovering = entity_internal::IsMouseHoveringOverEntity( + overworld_.mutable_sprites(game_state_)->at(current_sprite_id_), + ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); + + if (!hovering && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("Sprite Inserter"); + } else { + if (sprite_internal::DrawSpriteEditorPopup( + overworld_.mutable_sprites(game_state_) + ->at(current_sprite_id_))) { + overworld_.mutable_sprites(game_state_)->at(current_sprite_id_) = + current_sprite_; + } + } } } @@ -904,11 +1194,11 @@ void OverworldEditor::DrawOverworldEdits() { int map_x = mouse_position.x / small_map_size; int map_y = mouse_position.y / small_map_size; current_map_ = map_x + map_y * 8; - - // If the map has a parent, set the current_map_ to that parent map - // if (overworld_.overworld_map(current_map_)->IsLargeMap()) { - // current_map_ = overworld_.overworld_map(current_map_)->Parent(); - // } + if (current_world_ == 1) { + current_map_ += 0x40; + } else if (current_world_ == 2) { + current_map_ += 0x80; + } // Render the updated map bitmap. RenderUpdatedMapBitmap(mouse_position, @@ -930,17 +1220,17 @@ void OverworldEditor::DrawOverworldEdits() { uint8_t high_byte = (tile_value >> 8) & 0xFF; if (current_world_ == 0) { overworld_.mutable_map_tiles()->light_world[tile16_x][tile16_y] = low_byte; - // overworld_.mutable_map_tiles()->light_world[tile16_x][tile16_y + 1] = - // high_byte; + overworld_.mutable_map_tiles()->light_world[tile16_x][tile16_y + 1] = + high_byte; } else if (current_world_ == 1) { overworld_.mutable_map_tiles()->dark_world[tile16_x][tile16_y] = low_byte; - // overworld_.mutable_map_tiles()->dark_world[tile16_x][tile16_y + 1] = - // high_byte; + overworld_.mutable_map_tiles()->dark_world[tile16_x][tile16_y + 1] = + high_byte; } else { overworld_.mutable_map_tiles()->special_world[tile16_x][tile16_y] = low_byte; - // overworld_.mutable_map_tiles()->special_world[tile16_x][tile16_y + 1] = - // high_byte; + overworld_.mutable_map_tiles()->special_world[tile16_x][tile16_y + 1] = + high_byte; } if (flags()->kLogToConsole) { @@ -978,8 +1268,10 @@ void OverworldEditor::RenderUpdatedMapBitmap(const ImVec2 &click_position, } } - // Render the updated bitmap to the canvas - rom()->UpdateBitmap(¤t_bitmap); + current_bitmap.set_modified(true); + + // // Render the updated bitmap to the canvas + // rom()->UpdateBitmap(¤t_bitmap); } void OverworldEditor::CheckForOverworldEdits() { @@ -1001,8 +1293,7 @@ void OverworldEditor::CheckForCurrentMap() { // 4096x4096, 512x512 maps and some are larges maps 1024x1024 auto mouse_position = ImGui::GetIO().MousePos; constexpr int small_map_size = 512; - auto large_map_size = 1024; - + const auto large_map_size = 1024; const auto canvas_zero_point = ow_map_canvas_.zero_point(); // Calculate which small map the mouse is currently over @@ -1011,6 +1302,16 @@ void OverworldEditor::CheckForCurrentMap() { // Calculate the index of the map in the `maps_bmp_` vector current_map_ = map_x + map_y * 8; + if (current_world_ == 1) { + current_map_ += 0x40; + } else if (current_world_ == 2) { + current_map_ += 0x80; + } + + // If the map has a parent, set the current_map_ to that parent map + if (overworld_.overworld_map(current_map_)->IsLargeMap()) { + current_parent_ = overworld_.overworld_map(current_map_)->Parent(); + } auto current_map_x = current_map_ % 8; auto current_map_y = current_map_ / 8; @@ -1027,6 +1328,31 @@ void OverworldEditor::CheckForCurrentMap() { current_map_y * small_map_size, small_map_size, small_map_size); } + + if (maps_bmp_[current_map_].modified()) { + rom()->UpdateBitmap(&maps_bmp_[current_map_]); + maps_bmp_[current_map_].set_modified(false); + } +} + +void OverworldEditor::CheckForSelectRectangle() { + if (current_mode == EditingMode::DRAW_TILE) { + ow_map_canvas_.DrawSelectRect(0x10); + static std::vector tile16_ids; + if (ow_map_canvas_.selected_tiles().size() != 0) { + // Get the tile16 IDs from the selected tile ID positions + if (tile16_ids.size() != 0) { + tile16_ids.clear(); + } + for (auto &each : ow_map_canvas_.selected_tiles()) { + int tile16_id = overworld_.GetTile16Id(each); + tile16_ids.push_back(tile16_id); + } + ow_map_canvas_.mutable_selected_tiles()->clear(); + } + // Create a composite image of all the tile16s selected + ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10); + } } // Overworld Editor canvas @@ -1047,9 +1373,9 @@ void OverworldEditor::DrawOverworldCanvas() { } if (overworld_.is_loaded()) { DrawOverworldMaps(); + DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); DrawOverworldEntrances(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); - DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling()); DrawOverworldItems(); DrawOverworldSprites(); if (ImGui::IsItemHovered()) CheckForCurrentMap(); @@ -1082,8 +1408,6 @@ void OverworldEditor::DrawTile16Selector() { ImGui::EndChild(); } -// Tile 8 Selector -// Displays all the individual tiles that make up a tile16. void OverworldEditor::DrawTile8Selector() { graphics_bin_canvas_.DrawBackground(); graphics_bin_canvas_.DrawContextMenu(); @@ -1112,8 +1436,21 @@ void OverworldEditor::DrawAreaGraphics() { current_gfx_canvas_.DrawBackground(); gui::EndPadding(); current_gfx_canvas_.DrawContextMenu(); - current_gfx_canvas_.DrawBitmap(current_gfx_bmp_, /*border_offset=*/2, - overworld_.is_loaded()); + if (current_graphics_set_.count(current_map_) == 0) { + overworld_.SetCurrentMap(current_map_); + palette_ = overworld_.AreaPalette(); + gfx::Bitmap bmp; + gui::BuildAndRenderBitmapPipeline( + 0x80, 0x200, 0x08, overworld_.AreaGraphics(), *rom(), bmp, palette_); + // int area_palette = + // overworld_.overworld_map(current_map_)->area_palette(); + // gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40, + // overworld_.AreaGraphics(), *rom(), bmp, + // palettesets_[area_palette].main); + current_graphics_set_[current_map_] = bmp; + } + current_gfx_canvas_.DrawBitmap(current_graphics_set_[current_map_], + /*border_offset=*/2, overworld_.is_loaded()); current_gfx_canvas_.DrawTileSelector(32.0f); current_gfx_canvas_.DrawGrid(); current_gfx_canvas_.DrawOverlay(); @@ -1168,8 +1505,6 @@ absl::Status OverworldEditor::LoadGraphics() { // Copy the tile16 data into individual tiles. auto tile16_data = overworld_.Tile16Blockset(); - std::cout << tile16_data.size() << std::endl; - // Loop through the tiles and copy their pixel data into separate vectors for (int i = 0; i < 4096; i++) { // Create a new vector for the pixel data of the current tile @@ -1234,7 +1569,6 @@ void OverworldEditor::DrawOverworldProperties() { if (!init_properties) { for (int i = 0; i < 0x40; i++) { std::string area_graphics_str = absl::StrFormat( - "0x%02hX", overworld_.overworld_map(i)->area_graphics()); properties_canvas_.mutable_labels(0)->push_back(area_graphics_str); } @@ -1437,11 +1771,11 @@ void OverworldEditor::DrawDebugWindow() { // Print the size of the overworld map_tiles per world ImGui::Text("Light World Map Tiles: %d", - overworld_.mutable_map_tiles()->light_world.size()); + (int)overworld_.mutable_map_tiles()->light_world.size()); ImGui::Text("Dark World Map Tiles: %d", - overworld_.mutable_map_tiles()->dark_world.size()); + (int)overworld_.mutable_map_tiles()->dark_world.size()); ImGui::Text("Special World Map Tiles: %d", - overworld_.mutable_map_tiles()->special_world.size()); + (int)overworld_.mutable_map_tiles()->special_world.size()); static bool view_lw_map_tiles = false; static MemoryEditor mem_edit; diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index 1a869b24..564bd348 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -2,6 +2,7 @@ #define YAZE_APP_EDITOR_OVERWORLDEDITOR_H #include +#include #include #include @@ -12,6 +13,7 @@ #include "absl/strings/str_format.h" #include "app/core/common.h" #include "app/core/editor.h" +#include "app/editor/context/gfx_context.h" #include "app/editor/modules/gfx_group_editor.h" #include "app/editor/modules/palette_editor.h" #include "app/editor/modules/tile16_editor.h" @@ -42,7 +44,8 @@ static constexpr absl::string_view kToolsetColumnNames[] = { "#transportTool", "#musicTool", "#separator3", "#tilemapTool", "propertiesTool"}; -constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders; +constexpr ImGuiTableFlags kOWMapFlags = + ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable; constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit; constexpr ImGuiTableFlags kOWEditFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | @@ -60,6 +63,7 @@ constexpr absl::string_view kOWMapTable = "#MapSettingsTable"; class OverworldEditor : public Editor, public SharedROM, + public GfxContext, public core::ExperimentFlags { public: absl::Status Update() final; @@ -75,16 +79,16 @@ class OverworldEditor : public Editor, int jump_to_tab_ = -1; void Shutdown() { - for (auto &bmp : tile16_individual_) { + for (auto& bmp : tile16_individual_) { bmp.Cleanup(); } - for (auto &[i, bmp] : maps_bmp_) { + for (auto& [i, bmp] : maps_bmp_) { bmp.Cleanup(); } - for (auto &[i, bmp] : graphics_bin_) { + for (auto& [i, bmp] : graphics_bin_) { bmp.Cleanup(); } - for (auto &[i, bmp] : current_graphics_set_) { + for (auto& [i, bmp] : current_graphics_set_) { bmp.Cleanup(); } } @@ -110,10 +114,11 @@ class OverworldEditor : public Editor, void DrawOverworldMaps(); void DrawOverworldEdits(); - void RenderUpdatedMapBitmap(const ImVec2 &click_position, - const Bytes &tile_data); + void RenderUpdatedMapBitmap(const ImVec2& click_position, + const Bytes& tile_data); void CheckForOverworldEdits(); void CheckForCurrentMap(); + void CheckForSelectRectangle(); void DrawOverworldCanvas(); void DrawTile16Selector(); @@ -152,9 +157,10 @@ class OverworldEditor : public Editor, int current_world_ = 0; int current_map_ = 0; + int current_parent_ = 0; + int game_state_ = 1; int current_tile16_ = 0; int selected_tile_ = 0; - int game_state_ = 0; int selected_entrance_ = 0; int selected_usage_map_ = 0xFFFF; @@ -184,8 +190,8 @@ class OverworldEditor : public Editor, bool middle_mouse_dragging_ = false; bool is_dragging_entity_ = false; - zelda3::OverworldEntity *dragged_entity_; - zelda3::OverworldEntity *current_entity_; + zelda3::OverworldEntity* dragged_entity_; + zelda3::OverworldEntity* current_entity_; int current_entrance_id_ = 0; zelda3::OverworldEntrance current_entrance_; @@ -193,6 +199,8 @@ class OverworldEditor : public Editor, zelda3::OverworldExit current_exit_; int current_item_id_ = 0; zelda3::OverworldItem current_item_; + int current_sprite_id_ = 0; + zelda3::Sprite current_sprite_; bool show_experimental = false; std::string ow_tilemap_filename_ = ""; diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index e6326d66..999cc94b 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -32,7 +32,7 @@ void Canvas::Update(const gfx::Bitmap &bitmap, ImVec2 bg_size, int tile_size, DrawOverlay(); } -void Canvas::UpdateColorPainter(const gfx::Bitmap &bitmap, const ImVec4 &color, +void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color, const std::function &event, int tile_size, float scale) { global_scale_ = scale; @@ -143,17 +143,7 @@ void Canvas::DrawContextMenu() { } ImGui::EndMenu(); } - // TODO: Add a menu item for selecting the palette - // ImGui::Separator(); - // if (ImGui::BeginMenu("Palette")) { - // for (const auto each : editor::kPaletteGroupAddressesKeys) { - // if (ImGui::BeginMenu(each)) { - // } - // ImGui::EndMenu(); - // } - // ImGui::EndMenu(); - // } ImGui::EndPopup(); } @@ -175,10 +165,13 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) { // Calculate the coordinates of the mouse ImVec2 painter_pos; - painter_pos.x = std::floor((double)mouse_pos.x / size) * size; - painter_pos.y = std::floor((double)mouse_pos.y / size) * size; + painter_pos.x = + std::floor((double)mouse_pos.x / (size * scale)) * (size * scale); + painter_pos.y = + std::floor((double)mouse_pos.y / (size * scale)) * (size * scale); - auto painter_pos_end = ImVec2(painter_pos.x + size, painter_pos.y + size); + auto painter_pos_end = + ImVec2(painter_pos.x + (size * scale), painter_pos.y + (size * scale)); points_.push_back(painter_pos); points_.push_back(painter_pos_end); @@ -186,8 +179,8 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) { draw_list_->AddImage( (void *)bitmap.texture(), ImVec2(origin.x + painter_pos.x, origin.y + painter_pos.y), - ImVec2(origin.x + painter_pos.x + bitmap.width() * scale, - origin.y + painter_pos.y + bitmap.height() * scale)); + ImVec2(origin.x + painter_pos.x + (size)*scale, + origin.y + painter_pos.y + size * scale)); } if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { @@ -195,8 +188,12 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) { // Save the coordinates of the selected tile. drawn_tile_pos_ = painter_pos; return true; + } else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + // Draw the currently selected tile on the overworld here + // Save the coordinates of the selected tile. + drawn_tile_pos_ = painter_pos; + return true; } - } else { // Erase the hover when the mouse is not in the canvas window. points_.clear(); @@ -387,6 +384,26 @@ void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) { IM_COL32(color.x, color.y, color.z, color.w)); } +namespace { +std::vector GetTileIDsInGrid(int start_x, int start_y, int width, + int height, int tile_size) { + std::vector tile_ids; + + int num_tiles_x = width / tile_size; + int num_tiles_y = height / tile_size; + + for (int y = 0; y < num_tiles_y; y++) { + for (int x = 0; x < num_tiles_x; x++) { + int tile_id = (start_y / tile_size + y) * (width / tile_size) + + (start_x / tile_size + x); + tile_ids.push_back(tile_id); + } + } + + return tile_ids; +} +} // namespace + void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) { ImVec2 origin(canvas_p0_.x + scrolling_.x + x, canvas_p0_.y + scrolling_.y + y); @@ -400,12 +417,8 @@ void Canvas::DrawSelectRect(int tile_size, float scale) { static ImVec2 drag_start_pos; static bool dragging = false; - if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { - if (!points_.empty()) { - points_.clear(); - } - // Snap the start position to the nearest grid point with scaling - // consideration + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + // Start dragging and snap the start position to the nearest grid point drag_start_pos.x = std::floor(io.MousePos.x / (tile_size * scale)) * tile_size * scale; drag_start_pos.y = @@ -413,40 +426,81 @@ void Canvas::DrawSelectRect(int tile_size, float scale) { dragging = true; } - if (dragging) { - ImVec2 current_pos = io.MousePos; - ImVec2 grid_pos; - grid_pos.x = - std::floor(current_pos.x / (tile_size * scale)) * tile_size * scale; - grid_pos.y = - std::floor(current_pos.y / (tile_size * scale)) * tile_size * scale; - - // Calculate rect_min and rect_max considering the drag direction - ImVec2 rect_min, rect_max; - rect_min.x = - (grid_pos.x < drag_start_pos.x) ? grid_pos.x : drag_start_pos.x; - rect_min.y = - (grid_pos.y < drag_start_pos.y) ? grid_pos.y : drag_start_pos.y; - rect_max.x = (grid_pos.x >= drag_start_pos.x) - ? grid_pos.x + tile_size * scale - : drag_start_pos.x + tile_size * scale; - rect_max.y = (grid_pos.y >= drag_start_pos.y) - ? grid_pos.y + tile_size * scale - : drag_start_pos.y + tile_size * scale; + ImVec2 current_pos = io.MousePos; + // Snap current position to the nearest grid point + ImVec2 snapped_current_pos; + snapped_current_pos.x = + std::floor(current_pos.x / (tile_size * scale)) * tile_size * scale; + snapped_current_pos.y = + std::floor(current_pos.y / (tile_size * scale)) * tile_size * scale; + // Calculate rect_min and rect_max considering the drag direction + ImVec2 rect_min = ImVec2(std::min(drag_start_pos.x, snapped_current_pos.x), + std::min(drag_start_pos.y, snapped_current_pos.y)); + ImVec2 rect_max = ImVec2( + std::max(drag_start_pos.x, snapped_current_pos.x) + tile_size * scale, + std::max(drag_start_pos.y, snapped_current_pos.y) + tile_size * scale); + if (dragging && ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { draw_list_->AddRect(rect_min, rect_max, kRectangleBorder); + } - if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) { - dragging = false; - // Convert the coordinates to scale-independent form - ImVec2 scaled_rect_min, scaled_rect_max; - scaled_rect_min.x = rect_min.x * scale; - scaled_rect_min.y = rect_min.y * scale; - scaled_rect_max.x = rect_max.x * scale; - scaled_rect_max.y = rect_max.y * scale; + if (dragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) { + dragging = false; + ImVec2 scaled_rect_min = + ImVec2(drag_start_pos.x / scale, drag_start_pos.y / scale); + ImVec2 scaled_rect_max = ImVec2(rect_max.x / scale, rect_max.y / scale); - points_.push_back(scaled_rect_min); - points_.push_back(scaled_rect_max); + // Here, calculate and store the tile16 IDs within the rectangle + selected_tiles_ = + GetTileIDsInGrid(scaled_rect_min.x, scaled_rect_min.y, + scaled_rect_max.x - scaled_rect_min.x, + scaled_rect_max.y - scaled_rect_min.y, tile_size); + + // Clear and add the calculated rectangle points + points_.clear(); + points_.push_back(scaled_rect_min); + points_.push_back(scaled_rect_max); + } +} + +void Canvas::DrawBitmapGroup(std::vector &group, + std::vector &tile16_individual_, + int tile_size, float scale) { + if (points_.size() != 2) { + // Handle error: points_ should contain exactly two points + return; + } + + // Top-left and bottom-right corners of the rectangle + ImVec2 rect_top_left = points_[0]; + ImVec2 rect_bottom_right = points_[1]; + + // Calculate the start and end tiles in the grid + int start_tile_x = static_cast(rect_top_left.x / (tile_size * scale)); + int start_tile_y = static_cast(rect_top_left.y / (tile_size * scale)); + int end_tile_x = static_cast(rect_bottom_right.x / (tile_size * scale)); + int end_tile_y = static_cast(rect_bottom_right.y / (tile_size * scale)); + + // Calculate the size of the rectangle in 16x16 grid form + int rect_width = (end_tile_x - start_tile_x + 1) * tile_size; + int rect_height = (end_tile_y - start_tile_y + 1) * tile_size; + + const int tiles_per_row = rect_width / tile_size; + + for (int i = 0; i < group.size(); ++i) { + int tile_id = group[i]; + + // Check if tile_id is within the range of tile16_individual_ + if (tile_id >= 0 && tile_id < tile16_individual_.size()) { + int x = i % tiles_per_row; + int y = i / tiles_per_row; + + // Calculate the position of the tile within the rectangle + int tile_pos_x = x * tile_size * scale + start_tile_x * tile_size * scale; + int tile_pos_y = y * tile_size * scale + start_tile_y * tile_size * scale; + + // Draw the tile bitmap at the calculated position + DrawBitmap(tile16_individual_[tile_id], tile_pos_x, tile_pos_y, scale); } } } diff --git a/src/app/gui/canvas.h b/src/app/gui/canvas.h index b8081786..a583f261 100644 --- a/src/app/gui/canvas.h +++ b/src/app/gui/canvas.h @@ -46,7 +46,7 @@ class Canvas { void Update(const gfx::Bitmap& bitmap, ImVec2 bg_size, int tile_size, float scale = 1.0f, float grid_size = 64.0f); - void UpdateColorPainter(const gfx::Bitmap& bitmap, const ImVec4& color, + void UpdateColorPainter(gfx::Bitmap& bitmap, const ImVec4& color, const std::function& event, int tile_size, float scale = 1.0f); @@ -90,6 +90,11 @@ class Canvas { float scale = 1.0f); void DrawBitmapTable(const BitmapTable& gfx_bin); + + void DrawBitmapGroup(std::vector &group, + std::vector& tile16_individual_, + int tile_size, float scale = 1.0f); + void DrawOutline(int x, int y, int w, int h); void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color); void DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color); @@ -117,7 +122,7 @@ class Canvas { auto canvas_size() const { return canvas_sz_; } void set_global_scale(float scale) { global_scale_ = scale; } auto global_scale() const { return global_scale_; } - auto custom_labels_enabled() const { return enable_custom_labels_; } + auto custom_labels_enabled() { return &enable_custom_labels_; } auto custom_step() const { return custom_step_; } auto width() const { return canvas_sz_.x; } auto height() const { return canvas_sz_.y; } @@ -151,6 +156,9 @@ class Canvas { auto set_highlight_tile_id(int i) { highlight_tile_id = i; } auto set_draggable(bool value) { draggable_ = value; } + auto selected_tiles() const { return selected_tiles_; } + auto mutable_selected_tiles() { return &selected_tiles_; } + private: bool draggable_ = false; bool enable_grid_ = true; @@ -175,6 +183,8 @@ class Canvas { ImVec2 canvas_p1_; ImVec2 mouse_pos_in_canvas_; ImVec2 drawn_tile_pos_; + + std::vector selected_tiles_; }; } // namespace gui diff --git a/src/app/zelda3/common.h b/src/app/zelda3/common.h new file mode 100644 index 00000000..412f0a08 --- /dev/null +++ b/src/app/zelda3/common.h @@ -0,0 +1,38 @@ +#ifndef YAZE_APP_ZELDA3_COMMON_H +#define YAZE_APP_ZELDA3_COMMON_H + +namespace yaze { +namespace app { +namespace zelda3 { + +class OverworldEntity { + public: + enum EntityType { + kEntrance = 0, + kExit = 1, + kItem = 2, + kSprite = 3, + kTransport = 4, + kMusic = 5, + kTilemap = 6, + kProperties = 7 + } type_; + int x_; + int y_; + int game_x_; + int game_y_; + int entity_id_; + int map_id_; + + auto set_x(int x) { x_ = x; } + auto set_y(int y) { y_ = y; } + + OverworldEntity() = default; + + virtual void UpdateMapProperties(short map_id) = 0; +}; +} // namespace zelda3 +} // namespace app +} // namespace yaze + +#endif // YAZE_APP_ZELDA3_COMMON_H \ No newline at end of file diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index 07955449..d74e9fd6 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -141,8 +141,8 @@ absl::Status Overworld::Load(ROM &rom) { FetchLargeMaps(); LoadEntrances(); LoadExits(); - LoadSprites(); RETURN_IF_ERROR(LoadItems()); + RETURN_IF_ERROR(LoadSprites()); RETURN_IF_ERROR(LoadOverworldMaps()) is_loaded_ = true; @@ -862,6 +862,12 @@ void Overworld::FetchLargeMaps() { } } +void Overworld::LoadTileTypes() { + for (int i = 0; i < 0x200; i++) { + all_tiles_types_[i] = rom()->data()[overworldTilesType + i]; + } +} + void Overworld::LoadEntrances() { for (int i = 0; i < 129; i++) { short map_id = rom()->toint16(OWEntranceMap + (i * 2)); @@ -939,6 +945,37 @@ absl::Status Overworld::SaveExits() { return absl::OkStatus(); } +namespace { + +bool compareItemsArrays(std::vector itemArray1, + std::vector itemArray2) { + if (itemArray1.size() != itemArray2.size()) { + return false; + } + + bool match; + for (int i = 0; i < itemArray1.size(); i++) { + match = false; + for (int j = 0; j < itemArray2.size(); j++) { + // Check all sprite in 2nd array if one match + if (itemArray1[i].x_ == itemArray2[j].x_ && + itemArray1[i].y_ == itemArray2[j].y_ && + itemArray1[i].id == itemArray2[j].id) { + match = true; + break; + } + } + + if (!match) { + return false; + } + } + + return true; +} + +} // namespace + absl::Status Overworld::SaveItems() { std::vector> roomItems(128); @@ -968,13 +1005,19 @@ absl::Status Overworld::SaveItems() { itemPointersReuse[i] = -2; break; } - // Unclear: this.compareItemsArrays(roomItems[i].ToArray(), - // roomItems[ci].ToArray()) Commenting out for now if - // (this.compareItemsArrays(roomItems[i].ToArray(), - // roomItems[ci].ToArray())) { - // itemPointersReuse[i] = ci; - // break; - // } + + // Unclear: + compareItemsArrays( + std::vector(roomItems[i].begin(), roomItems[i].end()), + std::vector(roomItems[ci].begin(), + roomItems[ci].end())); + if (compareItemsArrays(std::vector(roomItems[i].begin(), + roomItems[i].end()), + std::vector(roomItems[ci].begin(), + roomItems[ci].end()))) { + itemPointersReuse[i] = ci; + break; + } } } @@ -1051,10 +1094,6 @@ void Overworld::LoadExits() { rom_data[OWExitYPlayer + (i * 2)]); ushort px = (ushort)((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) + rom_data[OWExitXPlayer + (i * 2)]); - OverworldExit exit(exit_room_id, exit_map_id, exit_vram, exit_y_scroll, - exit_x_scroll, py, px, exit_y_camera, exit_x_camera, - exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1, - exit_door_type_2); if (rom()->flags()->kLogToConsole) { std::cout << "Exit: " << i << " RoomID: " << exit_room_id @@ -1069,22 +1108,24 @@ void Overworld::LoadExits() { << " DoorType2: " << exit_door_type_2 << std::endl; } - if ((px & py) == 0xFFFF) { - exit.deleted = true; - } - - exits.push_back(exit); + exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll, + exit_x_scroll, py, px, exit_y_camera, exit_x_camera, + exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1, + exit_door_type_2, (px & py) == 0xFFFF); } all_exits_ = exits; } absl::Status Overworld::LoadItems() { ASSIGN_OR_RETURN(int pointer, rom()->ReadLong(zelda3::overworldItemsAddress)); - int oointerPC = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9 + int pointer_pc = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9 for (int i = 0; i < 128; i++) { - int addr = (pointer & 0xFF0000) + // 1B - (rom()->data()[oointerPC + (i * 2) + 1] << 8) + // F9 - rom()->data()[oointerPC + (i * 2)]; // 3C + ASSIGN_OR_RETURN(uint16_t word_address, + rom()->ReadWord(pointer_pc + i * 2)); + int addr = (pointer & 0xFF0000) | word_address; // 1B + + // (rom()->data()[pointer_pc + (i * 2) + 1] << 8) + // F9 + // rom()->data()[pointer_pc + (i * 2)]; // 3C addr = core::SnesToPc(addr); @@ -1095,9 +1136,9 @@ absl::Status Overworld::LoadItems() { } while (true) { - uint8_t b1 = rom()->data()[addr]; - uint8_t b2 = rom()->data()[addr + 1]; - uint8_t b3 = rom()->data()[addr + 2]; + ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(addr)); + ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(addr + 1)); + ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(addr + 2)); if (b1 == 0xFF && b2 == 0xFF) { break; @@ -1116,51 +1157,41 @@ absl::Status Overworld::LoadItems() { int sy = fakeID / 8; int sx = fakeID - (sy * 8); - all_items_.emplace_back(zelda3::OverworldItem( - b3, (ushort)i, (x * 16) + (sx * 512), (y * 16) + (sy * 512), false)); + all_items_.emplace_back(b3, (ushort)i, (x * 16) + (sx * 512), + (y * 16) + (sy * 512), false); auto size = all_items_.size(); - all_items_.at(size - 1).game_x = (uint8_t)x; - all_items_.at(size - 1).game_y = (uint8_t)y; + + all_items_[size - 1].game_x = (uint8_t)x; + all_items_[size - 1].game_y = (uint8_t)y; addr += 3; } } return absl::OkStatus(); } -void Overworld::LoadSprites() { +absl::Status Overworld::LoadSprites() { for (int i = 0; i < 3; i++) { all_sprites_.emplace_back(); } - for (int i = 0; i < 64; i++) { - all_sprites_[0].emplace_back(); - } - - for (int i = 0; i < 144; i++) { - all_sprites_[1].emplace_back(); - } - - for (int i = 0; i < 144; i++) { - all_sprites_[2].emplace_back(); - } - - LoadSpritesFromMap(overworldSpritesBegining, 64, 0); - LoadSpritesFromMap(overworldSpritesZelda, 144, 1); - LoadSpritesFromMap(overworldSpritesAgahnim, 144, 2); + RETURN_IF_ERROR(LoadSpritesFromMap(overworldSpritesBegining, 64, 0)); + RETURN_IF_ERROR(LoadSpritesFromMap(overworldSpritesZelda, 144, 1)); + RETURN_IF_ERROR(LoadSpritesFromMap(overworldSpritesAgahnim, 144, 2)); + return absl::OkStatus(); } -void Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count, - int sprite_index) { +absl::Status Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count, + int sprite_index) { for (int i = 0; i < sprite_count; i++) { if (map_parent_[i] != i) continue; int ptrPos = sprite_start + (i * 2); - int sprite_address = - core::SnesToPc((0x09 << 0x10) | rom()->toint16(ptrPos)); + ASSIGN_OR_RETURN(auto word_addr, rom()->ReadWord(ptrPos)); + int sprite_address = core::SnesToPc((0x09 << 0x10) | word_addr); while (true) { - uchar b1 = rom_[sprite_address]; - uchar b2 = rom_[sprite_address + 1]; - uchar b3 = rom_[sprite_address + 2]; + ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(sprite_address)); + ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(sprite_address + 1)); + ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(sprite_address + 2)); if (b1 == 0xFF) break; int editor_map_index = i; @@ -1175,14 +1206,16 @@ void Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count, int realX = ((b2 & 0x3F) * 16) + mapX * 512; int realY = ((b1 & 0x3F) * 16) + mapY * 512; - all_sprites_[sprite_index][i].InitSprite( - overworld_maps_[i].AreaGraphics(), (uchar)i, b3, (uchar)(b2 & 0x3F), - (uchar)(b1 & 0x3F), realX, realY); - all_sprites_[sprite_index][i].Draw(); + all_sprites_[sprite_index].emplace_back(overworld_maps_[i].AreaGraphics(), + (uchar)i, b3, (uchar)(b2 & 0x3F), + (uchar)(b1 & 0x3F), realX, realY); + // all_sprites_[sprite_index][i].Draw(); sprite_address += 3; } } + + return absl::OkStatus(); } absl::Status Overworld::SaveMapProperties() { diff --git a/src/app/zelda3/overworld.h b/src/app/zelda3/overworld.h index b51cb309..398dea91 100644 --- a/src/app/zelda3/overworld.h +++ b/src/app/zelda3/overworld.h @@ -14,6 +14,7 @@ #include "app/gfx/bitmap.h" #include "app/gfx/snes_tile.h" #include "app/rom.h" +#include "app/zelda3/common.h" #include "app/zelda3/overworld_map.h" #include "app/zelda3/sprite/sprite.h" @@ -21,33 +22,6 @@ namespace yaze { namespace app { namespace zelda3 { -class OverworldEntity { - public: - enum EntityType { - kEntrance = 0, - kExit = 1, - kItem = 2, - kSprite = 3, - kTransport = 4, - kMusic = 5, - kTilemap = 6, - kProperties = 7 - } type_; - int x_; - int y_; - int game_x_; - int game_y_; - int entity_id_; - int map_id_; - - auto set_x(int x) { x_ = x; } - auto set_y(int y) { y_ = y; } - - OverworldEntity() = default; - - virtual void UpdateMapProperties(short map_id) = 0; -}; - // List of secret item names const std::vector kSecretItemNames = { "Nothing", // 0 @@ -96,15 +70,6 @@ class OverworldItem : public OverworldEntity { bool deleted = false; OverworldItem() = default; - /// - /// Initializes a new instance of the - /// class. - /// - /// The ID. - /// The dungeon room ID or overworld area ID. - /// The in editor X position. The in editor Y position. Whether - /// the Item is on BG2 or not. OverworldItem(uint8_t id, uint16_t room_map_id, int x, int y, bool bg2) { this->id = id; this->x_ = x; @@ -123,12 +88,6 @@ class OverworldItem : public OverworldEntity { // this->unique_id = ROM.unique_item_id++; } - /// - /// Updates the item info when needed. Generally when moving items around - /// in editor. - /// - /// The dungeon room ID or overworld area ID where - /// the item was moved to. void UpdateMapProperties(int16_t room_map_id) override { this->room_map_id = static_cast(room_map_id); @@ -205,7 +164,7 @@ class OverworldExit : public OverworldEntity { uchar area_x_; uchar area_y_; bool is_hole_ = false; - bool deleted = false; + bool deleted_ = false; bool is_automatic_ = false; bool large_map_ = false; @@ -214,7 +173,7 @@ class OverworldExit : public OverworldEntity { ushort y_scroll, ushort x_scroll, ushort player_y, ushort player_x, ushort camera_y, ushort camera_x, uchar scroll_mod_y, uchar scroll_mod_x, ushort door_type_1, - ushort door_type_2) + ushort door_type_2, bool deleted = false) : map_pos_(vram_location), entrance_id_(0), area_x_(0), @@ -230,7 +189,8 @@ class OverworldExit : public OverworldEntity { scroll_mod_y_(scroll_mod_y), scroll_mod_x_(scroll_mod_x), door_type_1_(door_type_1), - door_type_2_(door_type_2) { + door_type_2_(door_type_2), + deleted_(deleted) { // Initialize entity variables this->x_ = player_x; this->y_ = player_y; @@ -494,6 +454,10 @@ class Overworld : public SharedROM, public core::ExperimentFlags { absl::Status SaveMapProperties(); + int GetTile16Id(int grid_id) const { + return map_tiles_.light_world[game_state_][grid_id]; + } + auto overworld_maps() const { return overworld_maps_; } auto overworld_map(int i) const { return &overworld_maps_[i]; } auto mutable_overworld_map(int i) { return &overworld_maps_[i]; } @@ -528,6 +492,8 @@ class Overworld : public SharedROM, public core::ExperimentFlags { auto all_items() const { return all_items_; } auto mutable_all_items() { return &all_items_; } auto &ref_all_items() { return all_items_; } + auto all_tiles_types() const { return all_tiles_types_; } + auto mutable_all_tiles_types() { return &all_tiles_types_; } absl::Status LoadPrototype(ROM &rom_, const std::string &tilemap_filename); @@ -549,11 +515,13 @@ class Overworld : public SharedROM, public core::ExperimentFlags { absl::Status DecompressAllMapTiles(); absl::Status DecompressProtoMapTiles(const std::string &filename); void FetchLargeMaps(); + void LoadTileTypes(); void LoadEntrances(); void LoadExits(); absl::Status LoadItems(); - void LoadSprites(); - void LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex); + absl::Status LoadSprites(); + absl::Status LoadSpritesFromMap(int spriteStart, int spriteCount, + int spriteIndex); bool is_loaded_ = false; @@ -564,6 +532,8 @@ class Overworld : public SharedROM, public core::ExperimentFlags { ROM rom_; OWMapTiles map_tiles_; + uint8_t all_tiles_types_[0x200]; + std::vector tiles16_; std::vector tiles32; std::vector tiles32_unique_; diff --git a/src/app/zelda3/overworld_map.cc b/src/app/zelda3/overworld_map.cc index dc481b0b..dcb563d4 100644 --- a/src/app/zelda3/overworld_map.cc +++ b/src/app/zelda3/overworld_map.cc @@ -9,6 +9,7 @@ #include #include "app/core/common.h" +#include "app/editor/context/gfx_context.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_tile.h" #include "app/rom.h" @@ -18,138 +19,6 @@ namespace yaze { namespace app { namespace zelda3 { -namespace { - -void CopyTile8bpp16(int x, int y, int tile, Bytes& bitmap, Bytes& blockset) { - int src_pos = - ((tile - ((tile / 0x08) * 0x08)) * 0x10) + ((tile / 0x08) * 2048); - int dest_pos = (x + (y * 0x200)); - for (int yy = 0; yy < 0x10; yy++) { - for (int xx = 0; xx < 0x10; xx++) { - bitmap[dest_pos + xx + (yy * 0x200)] = - blockset[src_pos + xx + (yy * 0x80)]; - } - } -} - -void SetColorsPalette(ROM& rom, int index, gfx::SNESPalette& current, - gfx::SNESPalette main, gfx::SNESPalette animated, - gfx::SNESPalette aux1, gfx::SNESPalette aux2, - gfx::SNESPalette hud, gfx::SNESColor bgrcolor, - gfx::SNESPalette spr, gfx::SNESPalette spr2) { - // Palettes infos, color 0 of a palette is always transparent (the arrays - // contains 7 colors width wide) There is 16 color per line so 16*Y - - // Left side of the palette - Main, Animated - std::vector new_palette(256); - - // Main Palette, Location 0,2 : 35 colors [7x5] - int k = 0; - for (int y = 2; y < 7; y++) { - for (int x = 1; x < 8; x++) { - new_palette[x + (16 * y)] = main[k]; - k++; - } - } - - // Animated Palette, Location 0,7 : 7colors - for (int x = 1; x < 8; x++) { - new_palette[(16 * 7) + (x)] = animated[(x - 1)]; - } - - // Right side of the palette - Aux1, Aux2 - - // Aux1 Palette, Location 8,2 : 21 colors [7x3] - k = 0; - for (int y = 2; y < 5; y++) { - for (int x = 9; x < 16; x++) { - new_palette[x + (16 * y)] = aux1[k]; - k++; - } - } - - // Aux2 Palette, Location 8,5 : 21 colors [7x3] - k = 0; - for (int y = 5; y < 8; y++) { - for (int x = 9; x < 16; x++) { - new_palette[x + (16 * y)] = aux2[k]; - k++; - } - } - - // Hud Palette, Location 0,0 : 32 colors [16x2] - for (int i = 0; i < 32; i++) { - new_palette[i] = hud[i]; - } - - // Hardcoded grass color (that might change to become invisible instead) - for (int i = 0; i < 8; i++) { - new_palette[(i * 16)] = bgrcolor; - new_palette[(i * 16) + 8] = bgrcolor; - } - - // Sprite Palettes - k = 0; - for (int y = 8; y < 9; y++) { - for (int x = 1; x < 8; x++) { - new_palette[x + (16 * y)] = rom.palette_group("sprites_aux1")[1][k]; - k++; - } - } - - // Sprite Palettes - k = 0; - for (int y = 8; y < 9; y++) { - for (int x = 9; x < 16; x++) { - new_palette[x + (16 * y)] = rom.palette_group("sprites_aux3")[0][k]; - k++; - } - } - - // Sprite Palettes - k = 0; - for (int y = 9; y < 13; y++) { - for (int x = 1; x < 16; x++) { - new_palette[x + (16 * y)] = rom.palette_group("global_sprites")[0][k]; - k++; - } - } - - // Sprite Palettes - k = 0; - for (int y = 13; y < 14; y++) { - for (int x = 1; x < 8; x++) { - new_palette[x + (16 * y)] = spr[k]; - k++; - } - } - - // Sprite Palettes - k = 0; - for (int y = 14; y < 15; y++) { - for (int x = 1; x < 8; x++) { - new_palette[x + (16 * y)] = spr2[k]; - k++; - } - } - - // Sprite Palettes - k = 0; - for (int y = 15; y < 16; y++) { - for (int x = 1; x < 16; x++) { - new_palette[x + (16 * y)] = rom.palette_group("armors")[0][k]; - k++; - } - } - - current.Create(new_palette); - for (int i = 0; i < 256; i++) { - current[(i / 16) * 16].SetTransparent(true); - } -} - -} // namespace - OverworldMap::OverworldMap(int index, ROM& rom, std::vector& tiles16) : parent_(index), index_(index), rom_(rom), tiles16_(tiles16) { @@ -322,14 +191,6 @@ void OverworldMap::DrawAnimatedTiles() { } static_graphics_[7] = 0x5B; } - // if (static_graphics_[7] == 0x5A) { - // static_graphics_[7] = 0x5B; - // } else { - // if (static_graphics_[7] == 0x58) { - // static_graphics_[7] = 0x59; - // } - // static_graphics_[7] = 0x5A; - // } } void OverworldMap::LoadAreaGraphicsBlocksets() { @@ -359,6 +220,125 @@ void OverworldMap::LoadAreaGraphics() { LoadDeathMountainGFX(); } +namespace palette_internal { + +void SetColorsPalette(ROM& rom, int index, gfx::SNESPalette& current, + gfx::SNESPalette main, gfx::SNESPalette animated, + gfx::SNESPalette aux1, gfx::SNESPalette aux2, + gfx::SNESPalette hud, gfx::SNESColor bgrcolor, + gfx::SNESPalette spr, gfx::SNESPalette spr2) { + // Palettes infos, color 0 of a palette is always transparent (the arrays + // contains 7 colors width wide) There is 16 color per line so 16*Y + + // Left side of the palette - Main, Animated + std::vector new_palette(256); + + // Main Palette, Location 0,2 : 35 colors [7x5] + int k = 0; + for (int y = 2; y < 7; y++) { + for (int x = 1; x < 8; x++) { + new_palette[x + (16 * y)] = main[k]; + k++; + } + } + + // Animated Palette, Location 0,7 : 7colors + for (int x = 1; x < 8; x++) { + new_palette[(16 * 7) + (x)] = animated[(x - 1)]; + } + + // Right side of the palette - Aux1, Aux2 + + // Aux1 Palette, Location 8,2 : 21 colors [7x3] + k = 0; + for (int y = 2; y < 5; y++) { + for (int x = 9; x < 16; x++) { + new_palette[x + (16 * y)] = aux1[k]; + k++; + } + } + + // Aux2 Palette, Location 8,5 : 21 colors [7x3] + k = 0; + for (int y = 5; y < 8; y++) { + for (int x = 9; x < 16; x++) { + new_palette[x + (16 * y)] = aux2[k]; + k++; + } + } + + // Hud Palette, Location 0,0 : 32 colors [16x2] + for (int i = 0; i < 32; i++) { + new_palette[i] = hud[i]; + } + + // Hardcoded grass color (that might change to become invisible instead) + for (int i = 0; i < 8; i++) { + new_palette[(i * 16)] = bgrcolor; + new_palette[(i * 16) + 8] = bgrcolor; + } + + // Sprite Palettes + k = 0; + for (int y = 8; y < 9; y++) { + for (int x = 1; x < 8; x++) { + new_palette[x + (16 * y)] = rom.palette_group("sprites_aux1")[1][k]; + k++; + } + } + + // Sprite Palettes + k = 0; + for (int y = 8; y < 9; y++) { + for (int x = 9; x < 16; x++) { + new_palette[x + (16 * y)] = rom.palette_group("sprites_aux3")[0][k]; + k++; + } + } + + // Sprite Palettes + k = 0; + for (int y = 9; y < 13; y++) { + for (int x = 1; x < 16; x++) { + new_palette[x + (16 * y)] = rom.palette_group("global_sprites")[0][k]; + k++; + } + } + + // Sprite Palettes + k = 0; + for (int y = 13; y < 14; y++) { + for (int x = 1; x < 8; x++) { + new_palette[x + (16 * y)] = spr[k]; + k++; + } + } + + // Sprite Palettes + k = 0; + for (int y = 14; y < 15; y++) { + for (int x = 1; x < 8; x++) { + new_palette[x + (16 * y)] = spr2[k]; + k++; + } + } + + // Sprite Palettes + k = 0; + for (int y = 15; y < 16; y++) { + for (int x = 1; x < 16; x++) { + new_palette[x + (16 * y)] = rom.palette_group("armors")[0][k]; + k++; + } + } + + current.Create(new_palette); + for (int i = 0; i < 256; i++) { + current[(i / 16) * 16].SetTransparent(true); + } +} +} // namespace palette_internal + // New helper function to get a palette from the ROM. gfx::SNESPalette OverworldMap::GetPalette(const std::string& group, int index, int previousIndex, int limit) { @@ -427,8 +407,11 @@ void OverworldMap::LoadPalette() { gfx::SNESPalette spr2 = GetPalette("sprites_aux3", pal5, previousSprPalId, 24); - SetColorsPalette(rom_, parent_, current_palette_, main, animated, aux1, aux2, - hud, bgr, spr, spr2); + palette_internal::SetColorsPalette(rom_, parent_, current_palette_, main, + animated, aux1, aux2, hud, bgr, spr, spr2); + + gfx::Paletteset paletteset{main, animated, aux1, aux2, bgr, hud, spr, spr2}; + palettesets_[area_graphics_] = paletteset; } // New helper function to process graphics buffer. @@ -450,7 +433,7 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset, absl::Status OverworldMap::BuildTileset() { all_gfx_ = rom_.graphics_buffer(); - current_gfx_.resize(0x10000, 0x00); + if (current_gfx_.size() == 0) current_gfx_.resize(0x10000, 0x00); for (int i = 0; i < 0x10; i++) { ProcessGraphicsBuffer(i, static_graphics_[i], 0x1000); @@ -460,13 +443,8 @@ absl::Status OverworldMap::BuildTileset() { } absl::Status OverworldMap::BuildTiles16Gfx(int count) { - if (current_blockset_.size() != 0) { - current_blockset_.clear(); - } - current_blockset_.reserve(0x100000); - for (int i = 0; i < 0x100000; i++) { - current_blockset_.push_back(0x00); - } + if (current_blockset_.size() == 0) current_blockset_.resize(0x100000, 0x00); + const int offsets[] = {0x00, 0x08, 0x400, 0x408}; auto yy = 0; auto xx = 0; @@ -509,6 +487,22 @@ absl::Status OverworldMap::BuildTiles16Gfx(int count) { return absl::OkStatus(); } +namespace { + +void CopyTile8bpp16(int x, int y, int tile, Bytes& bitmap, Bytes& blockset) { + int src_pos = + ((tile - ((tile / 0x08) * 0x08)) * 0x10) + ((tile / 0x08) * 2048); + int dest_pos = (x + (y * 0x200)); + for (int yy = 0; yy < 0x10; yy++) { + for (int xx = 0; xx < 0x10; xx++) { + bitmap[dest_pos + xx + (yy * 0x200)] = + blockset[src_pos + xx + (yy * 0x80)]; + } + } +} + +} // namespace + absl::Status OverworldMap::BuildBitmap(OWBlockset& world_blockset) { if (bitmap_data_.size() != 0) { bitmap_data_.clear(); diff --git a/src/app/zelda3/overworld_map.h b/src/app/zelda3/overworld_map.h index dad32d22..df5dbace 100644 --- a/src/app/zelda3/overworld_map.h +++ b/src/app/zelda3/overworld_map.h @@ -11,6 +11,7 @@ #include "absl/status/status.h" #include "app/core/common.h" +#include "app/editor/context/gfx_context.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" #include "app/gfx/snes_tile.h" @@ -22,7 +23,9 @@ namespace zelda3 { static constexpr int kTileOffsets[] = {0, 8, 4096, 4104}; -class OverworldMap { +using editor::GfxContext; + +class OverworldMap : public GfxContext { public: OverworldMap() = default; OverworldMap(int index, ROM& rom, std::vector& tiles16); @@ -38,6 +41,7 @@ class OverworldMap { void DrawAnimatedTiles(); + auto Tile16Blockset() const { return current_blockset_; } auto AreaGraphics() const { return current_gfx_; } auto AreaPalette() const { return current_palette_; } @@ -46,7 +50,7 @@ class OverworldMap { auto IsLargeMap() const { return large_map_; } auto IsInitialized() const { return initialized_; } auto Parent() const { return parent_; } - + auto mutable_current_palette() { return ¤t_palette_; } auto area_graphics() const { return area_graphics_; } diff --git a/src/app/zelda3/sprite/sprite.cc b/src/app/zelda3/sprite/sprite.cc index ba0562aa..4e413318 100644 --- a/src/app/zelda3/sprite/sprite.cc +++ b/src/app/zelda3/sprite/sprite.cc @@ -4,39 +4,40 @@ namespace yaze { namespace app { namespace zelda3 { -Sprite::Sprite() { - preview_gfx_.reserve(64 * 64); - for (int i = 0; i < 64 * 64; i++) { - preview_gfx_.push_back(0xFF); - } -} - void Sprite::InitSprite(const Bytes& src, uchar mapid, uchar id, uchar x, uchar y, int map_x, int map_y) { current_gfx_ = src; overworld_ = true; - map_id_ = mapid; + map_id_ = static_cast(mapid); id_ = id; - x_ = x; - y_ = y; + this->type_ = zelda3::OverworldEntity::EntityType::kSprite; + this->entity_id_ = id; + this->x_ = map_x_; + this->y_ = map_y_; nx_ = x; ny_ = y; name_ = core::kSpriteDefaultNames[id]; map_x_ = map_x; map_y_ = map_y; + preview_gfx_.reserve(64 * 64); + for (int i = 0; i < 64 * 64; i++) { + preview_gfx_.push_back(0xFF); + } } Sprite::Sprite(Bytes src, uchar mapid, uchar id, uchar x, uchar y, int map_x, int map_y) : current_gfx_(src), - map_id_(mapid), + map_id_(static_cast(mapid)), id_(id), - x_(x), - y_(y), nx_(x), ny_(y), map_x_(map_x), map_y_(map_y) { + this->type_ = zelda3::OverworldEntity::EntityType::kSprite; + this->entity_id_ = id; + this->x_ = map_x_; + this->y_ = map_y_; current_gfx_ = src; overworld_ = true; @@ -47,6 +48,12 @@ Sprite::Sprite(Bytes src, uchar mapid, uchar id, uchar x, uchar y, int map_x, } } +void Sprite::UpdateMapProperties(short map_id) { + map_x_ = x_; + map_y_ = y_; + name_ = core::kSpriteDefaultNames[id_]; +} + void Sprite::updateCoordinates(int map_x, int map_y) { map_x_ = map_x; map_y_ = map_y; diff --git a/src/app/zelda3/sprite/sprite.h b/src/app/zelda3/sprite/sprite.h index 62dde690..761c2c17 100644 --- a/src/app/zelda3/sprite/sprite.h +++ b/src/app/zelda3/sprite/sprite.h @@ -13,14 +13,15 @@ #include "app/gfx/bitmap.h" #include "app/gfx/snes_tile.h" #include "app/rom.h" +#include "app/zelda3/common.h" namespace yaze { namespace app { namespace zelda3 { -class Sprite { +class Sprite : public OverworldEntity { public: - Sprite(); + Sprite() = default; Sprite(Bytes src, uchar mapid, uchar id, uchar x, uchar y, int map_x, int map_y); void InitSprite(const Bytes& src, uchar mapid, uchar id, uchar x, uchar y, @@ -32,13 +33,14 @@ class Sprite { bool mirror_x = false, bool mirror_y = false, int sizex = 2, int sizey = 2); + void UpdateMapProperties(short map_id) override; + // New methods void updateCoordinates(int map_x, int map_y); auto PreviewGraphics() const { return preview_gfx_; } - auto GetRealX() const { return bounding_box_.x; } - auto GetRealY() const { return bounding_box_.y; } auto id() const { return id_; } + auto set_id(uchar id) { id_ = id; } auto x() const { return x_; } auto y() const { return y_; } auto nx() const { return nx_; } @@ -55,6 +57,7 @@ class Sprite { auto Height() const { return bounding_box_.h; } std::string& Name() { return name_; } auto deleted() const { return deleted_; } + auto set_deleted(bool deleted) { deleted_ = deleted; } private: Bytes current_gfx_; @@ -62,8 +65,8 @@ class Sprite { uchar map_id_; uchar id_; - uchar x_; - uchar y_; + // uchar x_; + // uchar y_; uchar nx_; uchar ny_; uchar overlord_ = 0;