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));
|
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.
|
// Help menu.
|
||||||
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
|
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
|
||||||
{
|
{
|
||||||
|
@ -1755,6 +1764,12 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
||||||
msg);
|
msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulatorWindow::ShowAchievements() {
|
||||||
|
if (!emulator_->is_title_open()) return;
|
||||||
|
|
||||||
|
emulator_->kernel_state()->achievement_manager()->ShowAchievementsUI(0);
|
||||||
|
}
|
||||||
|
|
||||||
std::string EmulatorWindow::CanonicalizeFileExtension(
|
std::string EmulatorWindow::CanonicalizeFileExtension(
|
||||||
const std::filesystem::path& path) {
|
const std::filesystem::path& path) {
|
||||||
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
||||||
|
|
|
@ -235,6 +235,8 @@ class EmulatorWindow {
|
||||||
bool IsUseNexusForGameBarEnabled();
|
bool IsUseNexusForGameBarEnabled();
|
||||||
void DisplayHotKeysConfig();
|
void DisplayHotKeysConfig();
|
||||||
|
|
||||||
|
void ShowAchievements();
|
||||||
|
|
||||||
static std::string CanonicalizeFileExtension(
|
static std::string CanonicalizeFileExtension(
|
||||||
const std::filesystem::path& path);
|
const std::filesystem::path& path);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/imgui_notification.h"
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
DEFINE_bool(show_achievement_notification, false,
|
DEFINE_bool(show_achievement_notification, false,
|
||||||
|
@ -181,5 +182,53 @@ void AchievementManager::Load(uint32_t user_index) {
|
||||||
fclose(file);
|
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 kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -50,6 +50,8 @@ class AchievementManager {
|
||||||
uint32_t achievement_id);
|
uint32_t achievement_id);
|
||||||
void Save(uint32_t user_index);
|
void Save(uint32_t user_index);
|
||||||
void Load(uint32_t user_index);
|
void Load(uint32_t user_index);
|
||||||
|
std::string GetAchievementsUIText(uint32_t user_index);
|
||||||
|
void ShowAchievementsUI(uint32_t user_index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<uint32_t, uint64_t> unlocked_achievements[4];
|
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(); }
|
dword_result_t XamIsUIActive_entry() { return xeXamIsUIActive(); }
|
||||||
DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency);
|
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);
|
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 xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -87,5 +87,32 @@ ImGuiDialog* ImGuiDialog::ShowMessageBox(ImGuiDrawer* imgui_drawer,
|
||||||
return new MessageBoxDialog(imgui_drawer, std::move(title), std::move(body));
|
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 ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -53,6 +53,25 @@ class ImGuiDialog {
|
||||||
std::vector<xe::threading::Fence*> waiting_fences_;
|
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 ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue