Files
Xenia-Canary/src/xenia/kernel/xam/ui/game_achievements_ui.cc
2025-11-17 13:36:25 +01:00

230 lines
7.2 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2025 Xenia Canary. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/kernel/xam/ui/game_achievements_ui.h"
namespace xe {
namespace kernel {
namespace xam {
namespace ui {
GameAchievementsUI::GameAchievementsUI(xe::ui::ImGuiDrawer* imgui_drawer,
const ImVec2 drawing_position,
const TitleInfo* title_info,
const UserProfile* profile)
: XamDialog(imgui_drawer),
drawing_position_(drawing_position),
title_info_(*title_info),
profile_(profile),
window_id_(GetWindowId()) {
LoadAchievementsData();
}
GameAchievementsUI::~GameAchievementsUI() {
for (auto& entry : achievements_icons_) {
entry.second.release();
}
}
bool GameAchievementsUI::LoadAchievementsData() {
achievements_info_ =
kernel_state()->xam_state()->achievement_manager()->GetTitleAchievements(
profile_->xuid(), title_info_.id);
if (achievements_info_.empty()) {
return false;
}
xe::ui::IconsData data;
for (const Achievement& entry : achievements_info_) {
const auto icon =
kernel_state()->xam_state()->achievement_manager()->GetAchievementIcon(
profile_->xuid(), title_info_.id, entry.achievement_id);
data.insert({entry.image_id, icon});
}
achievements_icons_ = imgui_drawer()->LoadIcons(data);
return true;
}
std::string GameAchievementsUI::GetAchievementTitle(
const Achievement& achievement_entry) const {
std::string title = "Secret trophy";
if (achievement_entry.IsUnlocked() || show_locked_info_ ||
achievement_entry.flags &
static_cast<uint32_t>(AchievementFlags::kShowUnachieved)) {
title = xe::to_utf8(achievement_entry.achievement_name);
}
return title;
}
std::string GameAchievementsUI::GetAchievementDescription(
const Achievement& achievement_entry) const {
std::string description = "Hidden description";
if (achievement_entry.flags &
static_cast<uint32_t>(AchievementFlags::kShowUnachieved)) {
description = xe::to_utf8(achievement_entry.locked_description);
}
if (achievement_entry.IsUnlocked() || show_locked_info_) {
description = xe::to_utf8(achievement_entry.unlocked_description);
}
return description;
}
xe::ui::ImmediateTexture* GameAchievementsUI::GetIcon(
const Achievement& achievement_entry) const {
if (!achievement_entry.IsUnlocked() && !show_locked_info_) {
return imgui_drawer()->GetLockedAchievementIcon();
}
if (achievements_icons_.count(achievement_entry.image_id)) {
return achievements_icons_.at(achievement_entry.image_id).get();
}
if (achievement_entry.IsUnlocked()) {
return nullptr;
}
return imgui_drawer()->GetLockedAchievementIcon();
}
std::string GameAchievementsUI::GetUnlockedTime(
const Achievement& achievement_entry) const {
if (achievement_entry.IsUnlockedOnline()) {
const auto unlock_time =
std::chrono::system_clock::to_time_t(chrono::WinSystemClock::to_sys(
achievement_entry.unlock_time.to_time_point()));
return fmt::format("Unlocked: Online {:%Y-%m-%d %H:%M}",
*std::localtime(&unlock_time));
}
if (achievement_entry.unlock_time.is_valid()) {
const auto unlock_time =
std::chrono::system_clock::to_time_t(chrono::WinSystemClock::to_sys(
achievement_entry.unlock_time.to_time_point()));
return fmt::format("Unlocked: Offline ({:%Y-%m-%d %H:%M})",
*std::localtime(&unlock_time));
}
return fmt::format("Unlocked: Offline");
}
void GameAchievementsUI::DrawTitleAchievementInfo(
ImGuiIO& io, const Achievement& achievement_entry) const {
const auto start_drawing_pos = ImGui::GetCursorPos();
ImGui::TableSetColumnIndex(0);
const auto icon = GetIcon(achievement_entry);
if (icon) {
ImGui::Image(reinterpret_cast<ImTextureID>(GetIcon(achievement_entry)),
xe::ui::default_image_icon_size);
} else {
ImGui::Dummy(xe::ui::default_image_icon_size);
}
ImGui::TableNextColumn();
ImGui::PushFont(imgui_drawer()->GetTitleFont());
const auto primary_line_height = ImGui::GetTextLineHeight();
ImGui::Text("%s", GetAchievementTitle(achievement_entry).c_str());
ImGui::PopFont();
ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f);
ImGui::TextWrapped("%s",
GetAchievementDescription(achievement_entry).c_str());
ImGui::PopTextWrapPos();
ImGui::SetCursorPosY(start_drawing_pos.y + xe::ui::default_image_icon_size.x -
ImGui::GetTextLineHeight());
if (achievement_entry.IsUnlocked()) {
ImGui::Text("%s", GetUnlockedTime(achievement_entry).c_str());
}
ImGui::TableNextColumn();
// TODO(Gliniak): There is no easy way to align text to middle, so I have to
// do it manually.
const float achievement_row_middle_alignment =
((xe::ui::default_image_icon_size.x / 2.f) -
ImGui::GetTextLineHeight() / 2.f) *
0.85f;
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
achievement_row_middle_alignment);
ImGui::PushFont(imgui_drawer()->GetTitleFont());
ImGui::TextUnformatted(
fmt::format("{} G", achievement_entry.gamerscore).c_str());
ImGui::PopFont();
}
void GameAchievementsUI::OnDraw(ImGuiIO& io) {
ImGui::SetNextWindowPos(drawing_position_, ImGuiCond_FirstUseEver);
const auto xenia_window_size = ImGui::GetMainViewport()->Size;
ImGui::SetNextWindowSizeConstraints(
ImVec2(xenia_window_size.x * 0.2f, xenia_window_size.y * 0.3f),
ImVec2(xenia_window_size.x * 0.6f, xenia_window_size.y * 0.8f));
ImGui::SetNextWindowBgAlpha(0.8f);
bool dialog_open = true;
std::string title_name = xe::to_utf8(title_info_.title_name);
title_name.erase(std::remove(title_name.begin(), title_name.end(), '\0'),
title_name.end());
const std::string window_name =
fmt::format("{} Achievements###{}", title_name, window_id_);
if (!ImGui::Begin(window_name.c_str(), &dialog_open,
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_HorizontalScrollbar)) {
Close();
ImGui::End();
return;
}
ImGui::Checkbox("Show locked achievements information", &show_locked_info_);
ImGui::Separator();
if (achievements_info_.empty()) {
ImGui::TextUnformatted(fmt::format("No achievements data!").c_str());
} else {
if (ImGui::BeginTable("", 3, ImGuiTableFlags_BordersInnerH)) {
for (const auto& entry : achievements_info_) {
ImGui::TableNextRow(0, xe::ui::default_image_icon_size.y);
DrawTitleAchievementInfo(io, entry);
}
ImGui::EndTable();
}
}
if (!dialog_open) {
Close();
ImGui::End();
return;
}
ImGui::End();
};
} // namespace ui
} // namespace xam
} // namespace kernel
} // namespace xe