diff --git a/include/ui/character_screen.hpp b/include/ui/character_screen.hpp index f9f3b12e..b872b7bf 100644 --- a/include/ui/character_screen.hpp +++ b/include/ui/character_screen.hpp @@ -64,7 +64,7 @@ private: std::function onCreateCharacter; std::function onBack; std::function onDeleteCharacter; - bool confirmDelete = false; + int deleteConfirmStage = 0; // 0=none, 1=first warning, 2=final warning /** * Get faction color based on race diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index a7153444..e407d78d 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -1051,12 +1051,14 @@ void CameraController::reset() { if (terrainManager) { terrainH = terrainManager->getHeightAt(x, y); } - float floorProbeZ = terrainH.value_or(refZ); + // Probe from the highest of terrain, refZ (server position), and defaultPosition.z + // so we don't miss WMO floors above terrain (e.g. Stormwind city surface). + float floorProbeZ = std::max(terrainH.value_or(refZ), refZ); if (wmoRenderer) { - wmoH = wmoRenderer->getFloorHeight(x, y, floorProbeZ + 2.0f); + wmoH = wmoRenderer->getFloorHeight(x, y, floorProbeZ + 4.0f); } if (m2Renderer) { - m2H = m2Renderer->getFloorHeight(x, y, floorProbeZ + 2.0f); + m2H = m2Renderer->getFloorHeight(x, y, floorProbeZ + 4.0f); } auto h = selectReachableFloor(terrainH, wmoH, refZ, 16.0f); if (!h) { @@ -1177,6 +1179,10 @@ void CameraController::reset() { lastGroundZ = spawnPos.z - 0.05f; } + // Invalidate inter-frame floor cache so the first frame probes fresh. + cachedFloorHeight.reset(); + cachedFloorPos = glm::vec2(0.0f); + camera->setRotation(yaw, pitch); glm::vec3 forward3D = camera->getForward(); diff --git a/src/ui/character_screen.cpp b/src/ui/character_screen.cpp index 1f563c45..9e8bd67e 100644 --- a/src/ui/character_screen.cpp +++ b/src/ui/character_screen.cpp @@ -200,29 +200,6 @@ void CharacterScreen::render(game::GameHandler& gameHandler) { if (onCharacterSelected) onCharacterSelected(character.guid); } - ImGui::Spacing(); - - // Delete - if (!confirmDelete) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.1f, 0.1f, 1.0f)); - if (ImGui::Button("Delete Character", ImVec2(btnW, 36))) { - confirmDelete = true; - } - ImGui::PopStyleColor(); - } else { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.0f, 0.0f, 1.0f)); - if (ImGui::Button("Confirm Delete?", ImVec2(btnW, 36))) { - if (onDeleteCharacter) onDeleteCharacter(character.guid); - confirmDelete = false; - selectedCharacterIndex = -1; - selectedCharacterGuid = 0; - } - ImGui::PopStyleColor(); - if (ImGui::Button("Cancel", ImVec2(btnW, 30))) { - confirmDelete = false; - } - } - ImGui::EndChild(); } @@ -242,6 +219,78 @@ void CharacterScreen::render(game::GameHandler& gameHandler) { if (onCreateCharacter) onCreateCharacter(); } + // Delete button — small, red, far right, only when a character is selected + if (selectedCharacterIndex >= 0 && + selectedCharacterIndex < static_cast(characters.size())) { + float deleteW = 80.0f; + ImGui::SameLine(ImGui::GetContentRegionMax().x - deleteW); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.08f, 0.08f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.7f, 0.1f, 0.1f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.5f, 0.5f, 1.0f)); + if (ImGui::Button("Delete", ImVec2(deleteW, 28))) { + deleteConfirmStage = 1; + ImGui::OpenPopup("DeleteConfirm1"); + } + ImGui::PopStyleColor(3); + } + + // First confirmation popup + if (ImGui::BeginPopupModal("DeleteConfirm1", nullptr, + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { + const auto& ch = characters[selectedCharacterIndex]; + ImGui::Text("Are you sure you want to delete"); + ImGui::TextColored(getFactionColor(ch.race), "%s", ch.name.c_str()); + ImGui::Text("Level %d %s %s?", + ch.level, game::getRaceName(ch.race), game::getClassName(ch.characterClass)); + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); + if (ImGui::Button("Yes, delete this character", ImVec2(240, 32))) { + ImGui::CloseCurrentPopup(); + deleteConfirmStage = 2; + ImGui::OpenPopup("DeleteConfirm2"); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(100, 32))) { + deleteConfirmStage = 0; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + // Second (final) confirmation popup + if (deleteConfirmStage == 2) { + ImGui::OpenPopup("DeleteConfirm2"); + } + if (ImGui::BeginPopupModal("DeleteConfirm2", nullptr, + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { + const auto& ch = characters[selectedCharacterIndex]; + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); + ImGui::Text("THIS CANNOT BE UNDONE!"); + ImGui::PopStyleColor(); + ImGui::Spacing(); + ImGui::Text("Are you REALLY sure you want to permanently"); + ImGui::Text("delete %s? This character will be gone forever.", ch.name.c_str()); + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.0f, 0.0f, 1.0f)); + if (ImGui::Button("DELETE PERMANENTLY", ImVec2(240, 32))) { + if (onDeleteCharacter) onDeleteCharacter(ch.guid); + deleteConfirmStage = 0; + selectedCharacterIndex = -1; + selectedCharacterGuid = 0; + ImGui::CloseCurrentPopup(); + } + ImGui::PopStyleColor(); + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(100, 32))) { + deleteConfirmStage = 0; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::End(); }