Improve the pause screen achievement list, show a game banner at the top

This commit is contained in:
Henrik Rydgård 2023-06-19 23:47:23 +02:00
parent c379f6f486
commit 80ff3f89bf
5 changed files with 102 additions and 24 deletions

View File

@ -11,22 +11,35 @@
void RetroAchievementsListScreen::CreateViews() {
auto di = GetI18NCategory(I18NCat::DIALOG);
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
using namespace UI;
root_ = new ScrollView(UI::ORIENT_VERTICAL);
LinearLayout *listLayout = root_->Add(new LinearLayout(UI::ORIENT_VERTICAL));
listLayout->SetSpacing(0.0f);
listLayout->SetSpacing(5.0f);
std::vector<Achievements::Achievement> achievements;
std::vector<Achievements::Achievement> unlockedAchievements;
std::vector<Achievements::Achievement> lockedAchievements;
listLayout->Add(new GameAchievementSummaryView(Achievements::GetGameID()));
Achievements::EnumerateAchievements([&](const Achievements::Achievement &achievement) {
achievements.push_back(achievement);
if (achievement.locked) {
lockedAchievements.push_back(achievement);
} else {
unlockedAchievements.push_back(achievement);
}
return true;
});
for (auto achievement : achievements) {
listLayout->Add(new ItemHeader(ac->T("Unlocked achievements")));
for (auto achievement : unlockedAchievements) {
listLayout->Add(new AchievementView(achievement));
}
listLayout->Add(new ItemHeader(ac->T("Locked achievements")));
for (auto achievement : lockedAchievements) {
listLayout->Add(new AchievementView(achievement));
}
}
@ -106,6 +119,11 @@ void MeasureAchievement(const Achievements::Achievement &achievement, float *w,
*h = 60.0f;
}
void MeasureGameAchievementSummary(int gameID, float *w, float *h) {
*w = 0.0f;
*h = 60.0f;
}
// Render style references:
@ -124,11 +142,11 @@ void RenderAchievement(UIContext &dc, const Achievements::Achievement &achieveme
dc.Begin();
dc.FillRect(background, bounds);
dc.SetFontScale(0.7f, 0.7f);
dc.SetFontScale(1.0f, 1.0f);
dc.DrawTextRect(achievement.title.c_str(), bounds.Inset(iconSpace + 5.0f, 5.0f, 5.0f, 5.0f), dc.theme->itemStyle.fgColor, ALIGN_TOPLEFT);
dc.SetFontScale(0.5f, 0.5f);
dc.DrawTextRect(achievement.description.c_str(), bounds.Inset(iconSpace + 5.0f, 30.0f, 5.0f, 5.0f), dc.theme->itemStyle.fgColor, ALIGN_TOPLEFT);
dc.SetFontScale(0.66f, 0.66f);
dc.DrawTextRect(achievement.description.c_str(), bounds.Inset(iconSpace + 5.0f, 37.0f, 5.0f, 5.0f), dc.theme->itemStyle.fgColor, ALIGN_TOPLEFT);
char temp[64];
snprintf(temp, sizeof(temp), "%d", achievement.points);
@ -150,6 +168,41 @@ void RenderAchievement(UIContext &dc, const Achievements::Achievement &achieveme
dc.Flush();
}
void RenderGameAchievementSummary(UIContext &dc, int gameID, const Bounds &bounds, float opacity) {
using namespace UI;
UI::Drawable background = dc.theme->itemStyle.background;
background.color = colorAlpha(background.color, opacity);
float iconSpace = 64.0f;
dc.Begin();
dc.FillRect(background, bounds);
dc.SetFontScale(1.0f, 1.0f);
dc.DrawTextRect(Achievements::GetGameTitle().c_str(), bounds.Inset(iconSpace + 5.0f, 5.0f, 5.0f, 5.0f), dc.theme->itemStyle.fgColor, ALIGN_TOPLEFT);
std::string description = Achievements::GetGameAchievementSummary();
std::string icon = Achievements::GetGameIcon();
dc.SetFontScale(0.66f, 0.66f);
dc.DrawTextRect(description.c_str(), bounds.Inset(iconSpace + 5.0f, 30.0f, 5.0f, 5.0f), dc.theme->itemStyle.fgColor, ALIGN_TOPLEFT);
dc.SetFontScale(1.0f, 1.0f);
dc.Flush();
std::string name = icon;
if (g_iconCache.BindIconTexture(&dc, name)) {
dc.Draw()->DrawTexRect(Bounds(bounds.x, bounds.y, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, 0xFFFFFFFF);
}
dc.Flush();
dc.RebindTexture();
dc.Flush();
}
void AchievementView::Draw(UIContext &dc) {
RenderAchievement(dc, achievement_, AchievementRenderStyle::LISTED, bounds_, 0.0f);
}
@ -157,3 +210,11 @@ void AchievementView::Draw(UIContext &dc) {
void AchievementView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
MeasureAchievement(achievement_, &w, &h);
}
void GameAchievementSummaryView::Draw(UIContext &dc) {
RenderGameAchievementSummary(dc, gameID_, bounds_, 0.0f);
}
void GameAchievementSummaryView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
MeasureGameAchievementSummary(gameID_, &w, &h);
}

View File

@ -38,6 +38,8 @@ enum class AchievementRenderStyle {
void MeasureAchievement(const Achievements::Achievement &achievement, float *w, float *h);
void RenderAchievement(UIContext &context, const Achievements::Achievement &achievement, AchievementRenderStyle style, const Bounds &bounds, float time_s);
void MeasureGameAchievementSummary(int gameID, float *w, float *h);
class AchievementView : public UI::Item {
public:
@ -48,3 +50,13 @@ public:
private:
Achievements::Achievement achievement_;
};
class GameAchievementSummaryView : public UI::Item {
public:
GameAchievementSummaryView(int gameID, UI::LayoutParams *layoutParams = nullptr) : UI::Item(layoutParams), gameID_(gameID) {}
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
private:
int gameID_;
};

View File

@ -1064,6 +1064,22 @@ void Achievements::DownloadImage(std::string url, std::string cache_filename)
}
}
std::string Achievements::GetGameAchievementSummary() {
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
std::string summary;
if (GetAchievementCount() > 0) {
summary = StringFromFormat(ac->T("Earned", "You have unlocked %d of %d achievements, earning %d of %d points"),
GetUnlockedAchiementCount(), GetAchievementCount(), GetCurrentPointsForGame(), GetMaximumPointsForGame());
} else {
summary = ac->T("This game has no achievements");
}
if (GetLeaderboardCount() > 0) {
if (LeaderboardsActive())
summary.append("\nLeaderboard submission is enabled.");
}
return summary;
}
void Achievements::DisplayAchievementSummary()
{
@ -1075,21 +1091,7 @@ void Achievements::DisplayAchievementSummary()
else
title = s_game_title;
std::string summary;
if (GetAchievementCount() > 0)
{
summary = StringFromFormat(ac->T("Earned", "You have earned %d of %d achievements, and %d of %d points"),
GetUnlockedAchiementCount(), GetAchievementCount(), GetCurrentPointsForGame(), GetMaximumPointsForGame());
} else
{
summary = ac->T("This game has no achievements");
}
if (GetLeaderboardCount() > 0)
{
summary.push_back('\n');
if (LeaderboardsActive())
summary.append("Leaderboard submission is enabled.");
}
std::string summary = GetGameAchievementSummary();
OSDAddNotification(10.0f, title, summary, s_game_icon);
@ -1814,7 +1816,7 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, std::string conten
rc_runtime_format_lboard_value(best_score, sizeof(best_score), response.best_score, lb->format);
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
const char *formatString = ac->T("Your Score");
const char *formatString = ac->T("Submitted Score");
std::string summary = StringFromFormat(formatString,
submitted_score, best_score, response.new_rank, response.num_entries);

View File

@ -140,6 +140,8 @@ const std::string &GetGameTitle();
const std::string &GetGameIcon();
bool EnumerateAchievements(std::function<bool(const Achievement &)> callback);
// TODO: Make these support multiple games, not just the current games, with cached info.
u32 GetUnlockedAchiementCount();
u32 GetAchievementCount();
u32 GetMaximumPointsForGame();
@ -154,6 +156,7 @@ u32 GetPrimedAchievementCount();
const Achievement *GetAchievementByID(u32 id);
std::pair<u32, u32> GetAchievementProgress(const Achievement &achievement);
std::string GetGameAchievementSummary();
std::string GetAchievementProgressText(const Achievement &achievement);
std::string GetAchievementBadgePath(const Achievement &achievement, bool download_if_missing = true,
bool force_unlocked_icon = false);

View File

@ -1279,4 +1279,4 @@ VR controllers = VR controllers
[Achievements]
Earned = You have earned %d of %d achievements, and %d of %d points
This game has no achievements = This game has no achievements
Your Score = Your Score: %d (Best: %d)\nLeaderboard Position: %d of %d
Submitted Score = Submitted score: %d (Best: %d)\nLeaderboard Position: %d of %d