Added UI dialog for viewing achievements
This commit is contained in:
parent
65b326fba3
commit
f61152c0b4
|
@ -669,6 +669,15 @@ bool EmulatorWindow::Initialize() {
|
|||
}
|
||||
main_menu->AddChild(std::move(hid_menu));
|
||||
|
||||
// Game menu.
|
||||
auto game_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Game");
|
||||
{
|
||||
game_menu->AddChild(
|
||||
MenuItem::Create(MenuItem::Type::kString, "&Show achievements", "",
|
||||
std::bind(&EmulatorWindow::ShowAchievements, this)));
|
||||
}
|
||||
main_menu->AddChild(std::move(game_menu));
|
||||
|
||||
// Help menu.
|
||||
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
|
||||
{
|
||||
|
@ -1755,6 +1764,12 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
|||
msg);
|
||||
}
|
||||
|
||||
void EmulatorWindow::ShowAchievements() {
|
||||
if (!emulator_->is_title_open()) return;
|
||||
|
||||
emulator_->kernel_state()->achievement_manager()->ShowAchievementsUI(0);
|
||||
}
|
||||
|
||||
std::string EmulatorWindow::CanonicalizeFileExtension(
|
||||
const std::filesystem::path& path) {
|
||||
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
||||
|
|
|
@ -235,6 +235,8 @@ class EmulatorWindow {
|
|||
bool IsUseNexusForGameBarEnabled();
|
||||
void DisplayHotKeysConfig();
|
||||
|
||||
void ShowAchievements();
|
||||
|
||||
static std::string CanonicalizeFileExtension(
|
||||
const std::filesystem::path& path);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "xenia/gpu/graphics_system.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/ui/imgui_notification.h"
|
||||
|
||||
DEFINE_bool(show_achievement_notification, false,
|
||||
|
@ -181,5 +182,53 @@ void AchievementManager::Load(uint32_t user_index) {
|
|||
fclose(file);
|
||||
}
|
||||
|
||||
std::string AchievementManager::GetAchievementsUIText(uint32_t user_index) {
|
||||
if (user_index >= 4) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ui_desc = "";
|
||||
|
||||
const util::XdbfGameData db = kernel_state()->title_xdbf();
|
||||
|
||||
if (db.is_valid()) {
|
||||
const XLanguage language =
|
||||
db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
|
||||
const std::vector<util::XdbfAchievementTableEntry> achievement_list =
|
||||
db.GetAchievements();
|
||||
|
||||
for (const util::XdbfAchievementTableEntry& entry : achievement_list) {
|
||||
bool is_unlocked = IsAchievementUnlocked(user_index, entry.id);
|
||||
|
||||
std::string mark = is_unlocked ? "[X]" : "[ ]";
|
||||
std::string label = db.GetStringTableEntry(language, entry.label_id);
|
||||
uint16_t score = entry.gamerscore;
|
||||
std::string description;
|
||||
if (is_unlocked) {
|
||||
description = db.GetStringTableEntry(language, entry.description_id);
|
||||
} else {
|
||||
description = db.GetStringTableEntry(language, entry.unachieved_id);
|
||||
}
|
||||
|
||||
ui_desc += fmt::format("{} {}\n {}\n {}G\n\n", mark, label,
|
||||
description, score);
|
||||
}
|
||||
}
|
||||
|
||||
return ui_desc;
|
||||
}
|
||||
|
||||
void AchievementManager::ShowAchievementsUI(uint32_t user_index) {
|
||||
if (user_index >= 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string ui_desc = GetAchievementsUIText(user_index);
|
||||
|
||||
const Emulator* emulator = kernel_state()->emulator();
|
||||
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
||||
new ui::AchievementsDialog(imgui_drawer, ui_desc);
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -50,6 +50,8 @@ class AchievementManager {
|
|||
uint32_t achievement_id);
|
||||
void Save(uint32_t user_index);
|
||||
void Load(uint32_t user_index);
|
||||
std::string GetAchievementsUIText(uint32_t user_index);
|
||||
void ShowAchievementsUI(uint32_t user_index);
|
||||
|
||||
private:
|
||||
std::map<uint32_t, uint64_t> unlocked_achievements[4];
|
||||
|
|
|
@ -208,6 +208,49 @@ X_RESULT xeXamDispatchHeadlessEx(
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
X_RESULT xeXamDispatchDialogAsync(T* dialog,
|
||||
std::function<void(T*)> close_callback) {
|
||||
// Broadcast XN_SYS_UI = true
|
||||
kernel_state()->BroadcastNotification(0x9, true);
|
||||
++xam_dialogs_shown_;
|
||||
|
||||
// Important to pass captured vars by value here since we return from this
|
||||
// without waiting for the dialog to close so the original local vars will be
|
||||
// destroyed.
|
||||
// FIXME: Probably not the best idea to call Sleep in UI thread.
|
||||
dialog->set_close_callback([dialog, close_callback]() {
|
||||
close_callback(dialog);
|
||||
|
||||
--xam_dialogs_shown_;
|
||||
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
// Broadcast XN_SYS_UI = false
|
||||
kernel_state()->BroadcastNotification(0x9, false);
|
||||
});
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
X_RESULT xeXamDispatchHeadlessAsync(std::function<void()> run_callback) {
|
||||
// Broadcast XN_SYS_UI = true
|
||||
kernel_state()->BroadcastNotification(0x9, true);
|
||||
++xam_dialogs_shown_;
|
||||
|
||||
auto display_window = kernel_state()->emulator()->display_window();
|
||||
display_window->app_context().CallInUIThread([run_callback]() {
|
||||
run_callback();
|
||||
|
||||
--xam_dialogs_shown_;
|
||||
|
||||
xe::threading::Sleep(std::chrono::milliseconds(100));
|
||||
// Broadcast XN_SYS_UI = false
|
||||
kernel_state()->BroadcastNotification(0x9, false);
|
||||
});
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
dword_result_t XamIsUIActive_entry() { return xeXamIsUIActive(); }
|
||||
DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency);
|
||||
|
||||
|
@ -620,6 +663,31 @@ dword_result_t XamGetDashContext_entry(const ppc_context_t& ctx) {
|
|||
|
||||
DECLARE_XAM_EXPORT1(XamGetDashContext, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamShowAchievementsUI_entry(dword_t user_index) {
|
||||
if (user_index >= 4) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||
return X_ERROR_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (cvars::headless) {
|
||||
return xeXamDispatchHeadlessAsync([]() {});
|
||||
}
|
||||
|
||||
std::string ui_desc =
|
||||
kernel_state()->achievement_manager()->GetAchievementsUIText(user_index);
|
||||
|
||||
const Emulator* emulator = kernel_state()->emulator();
|
||||
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
|
||||
return xeXamDispatchDialogAsync<ui::AchievementsDialog>(
|
||||
new ui::AchievementsDialog(imgui_drawer, ui_desc),
|
||||
[](ui::AchievementsDialog*) {});
|
||||
}
|
||||
|
||||
DECLARE_XAM_EXPORT1(XamShowAchievementsUI, kUI, kSketchy);
|
||||
|
||||
} // namespace xam
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -87,5 +87,32 @@ ImGuiDialog* ImGuiDialog::ShowMessageBox(ImGuiDrawer* imgui_drawer,
|
|||
return new MessageBoxDialog(imgui_drawer, std::move(title), std::move(body));
|
||||
}
|
||||
|
||||
void AchievementsDialog::OnDraw(ImGuiIO& io) {
|
||||
if (!has_opened_) {
|
||||
ImGui::OpenPopup("Achievements");
|
||||
has_opened_ = true;
|
||||
}
|
||||
if (ImGui::BeginPopupModal("Achievements", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
char* text = const_cast<char*>(body_.c_str());
|
||||
ImGui::InputTextMultiline(
|
||||
"##body", text, body_.size(), ImVec2(600, 600),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_ReadOnly);
|
||||
if (ImGui::Button("OK")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementsDialog::OnClose() {
|
||||
if (close_callback_) {
|
||||
close_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
|
|
@ -53,6 +53,25 @@ class ImGuiDialog {
|
|||
std::vector<xe::threading::Fence*> waiting_fences_;
|
||||
};
|
||||
|
||||
class AchievementsDialog : public xe::ui::ImGuiDialog {
|
||||
public:
|
||||
AchievementsDialog(xe::ui::ImGuiDrawer* imgui_drawer, std::string& body)
|
||||
: xe::ui::ImGuiDialog(imgui_drawer), body_(body) {}
|
||||
virtual ~AchievementsDialog() {}
|
||||
|
||||
void set_close_callback(std::function<void()> close_callback) {
|
||||
close_callback_ = close_callback;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnDraw(ImGuiIO& io) override;
|
||||
void OnClose() override;
|
||||
|
||||
bool has_opened_ = false;
|
||||
std::string body_;
|
||||
std::function<void()> close_callback_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
|
|
Loading…
Reference in New Issue