Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions soh/include/z64save.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,18 @@ typedef struct ShipQuestSaveContextData {
ShipQuestSpecificSaveContextData data;
} ShipQuestSaveContextData;

typedef struct ShipAchievementSaveContextData {
u32 achievementFlags;
} ShipAchievementSaveContextData;

typedef struct ShipSaveContextData {
u16 pendingSale;
u16 pendingSaleMod;
u8 pendingIceTrapCount;
SohStats stats;
FaroresWindData backupFW;
ShipQuestSaveContextData quest;
ShipAchievementSaveContextData achievements;
u8 maskMemory;
u8 filenameLanguage;
//TODO: Move non-rando specific flags to a new sohInf and move the remaining randomizerInf to ShipRandomizerSaveContextData
Expand Down
120 changes: 120 additions & 0 deletions soh/soh/Enhancements/achievements.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "soh/Enhancements/achievements.h"
#include "soh/SaveManager.h"
#include "soh/Notification/Notification.h"
#include "soh/ShipInit.hpp"
#include "z64.h"
#include "functions.h"
#include "variables.h"
#include <fast/Fast3dGui.h>

extern "C" SaveContext gSaveContext;

static RegisterShipInitFunc initFunc(Achievements_Init);

void Achievements_InitDefaults(bool isDebug);
void Achievements_Load();
void Achievements_Save(SaveContext* saveContext, int sectionID, bool fullSave);

void Achievements_LoadIcons() {
auto gui = std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui());

gui->LoadTextureFromRawImage("gAchieveGohmaDefeat", "textures/achievement_icons/gAchieveGohmaDefeat.png");
gui->LoadTextureFromRawImage("gAchieveGohmaDefeat_Grayed", "textures/achievement_icons/gAchieveGohmaDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveKDDefeat", "textures/achievement_icons/gAchieveKDDefeat.png");
gui->LoadTextureFromRawImage("gAchieveKDDefeat_Grayed", "textures/achievement_icons/gAchieveKDDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveBarinadeDefeat", "textures/achievement_icons/gAchieveBarinadeDefeat.png");
gui->LoadTextureFromRawImage("gAchieveBarinadeDefeat_Grayed", "textures/achievement_icons/gAchieveBarinadeDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchievePGDefeat", "textures/achievement_icons/gAchievePGDefeat.png");
gui->LoadTextureFromRawImage("gAchievePGDefeat_Grayed", "textures/achievement_icons/gAchievePGDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveVolvagiaDefeat", "textures/achievement_icons/gAchieveVolvagiaDefeat.png");
gui->LoadTextureFromRawImage("gAchieveVolvagiaDefeat_Grayed", "textures/achievement_icons/gAchieveVolvagiaDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveMorphaDefeat", "textures/achievement_icons/gAchieveMorphaDefeat.png");
gui->LoadTextureFromRawImage("gAchieveMorphaDefeat_Grayed", "textures/achievement_icons/gAchieveMorphaDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveBongoDefeat", "textures/achievement_icons/gAchieveBongoDefeat.png");
gui->LoadTextureFromRawImage("gAchieveBongoDefeat_Grayed", "textures/achievement_icons/gAchieveBongoDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveTwinrovaDefeat", "textures/achievement_icons/gAchieveTwinrovaDefeat.png");
gui->LoadTextureFromRawImage("gAchieveTwinrovaDefeat_Grayed", "textures/achievement_icons/gAchieveTwinrovaDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveGanondorfDefeat", "textures/achievement_icons/gAchieveGanondorfDefeat.png");
gui->LoadTextureFromRawImage("gAchieveGanondorfDefeat_Grayed", "textures/achievement_icons/gAchieveGanondorfDefeat_Grayed.png");
gui->LoadTextureFromRawImage("gAchieveGanonDefeat", "textures/achievement_icons/gAchieveGanonDefeat.png");
gui->LoadTextureFromRawImage("gAchieveGanonDefeat_Grayed", "textures/achievement_icons/gAchieveGanonDefeat_Grayed.png");
}

void Achievements_Init() {
Achievements_LoadIcons();
SaveManager::Instance->AddLoadFunction("achievements", 1, Achievements_Load);
SaveManager::Instance->AddSaveFunction("achievements", 1, Achievements_Save, true, SECTION_PARENT_NONE);
SaveManager::Instance->AddInitFunction(Achievements_InitDefaults);
}

void Achievements_InitDefaults(bool isDebug) {
gSaveContext.ship.achievements.achievementFlags = 0;
}

void Achievements_Load() {
SaveManager::Instance->LoadData("achievementFlags", gSaveContext.ship.achievements.achievementFlags);

SPDLOG_INFO("Loaded achievementFlags={}",gSaveContext.ship.achievements.achievementFlags);
}

void Achievements_Save(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveManager::Instance->SaveData("achievementFlags", saveContext->ship.achievements.achievementFlags);
}

extern "C" bool Achievements_IsUnlocked(AchievementId id) {
return (gSaveContext.ship.achievements.achievementFlags & (1ULL << id)) != 0;
}

extern "C" void Achievements_TryUnlock(AchievementId id) {
if (!Achievements_IsUnlocked(id)) {
gSaveContext.ship.achievements.achievementFlags |= (1ULL << id);
const AchievementInfo* info = Achievements_GetInfo(id);
Notification::Emit({
.itemIcon = info->icon,
.prefix = "Achievement Unlocked!",
.prefixColor = ImVec4(1.0f, 0.85f, 0.0f, 1.0f),
.message = info->name,
.messageColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f),
.achievement = true,
});
Audio_PlaySoundGeneral(NA_SE_SY_KINSTA_MARK_APPEAR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);

SPDLOG_INFO("Unlock {} -> flags={}", static_cast<int>(id), gSaveContext.ship.achievements.achievementFlags);
}
}

const AchievementInfo* Achievements_GetInfo(AchievementId id) {
for (size_t i = 0; i < gAchievementCount; i++) {
if (gAchievements[i].id == id) {
return &gAchievements[i];
}
}
return NULL;
}

const AchievementInfo gAchievements[] = {
{ ACHIEVEMENT_DEFEAT_GOHMA, "Parasitic Armored Arachnid", "gAchieveGohmaDefeat", "gAchieveGohmaDefeat_Grayed",
"Defeat Queen Gohma." },
{ ACHIEVEMENT_DEFEAT_KD, "Infernal Dinosaur", "gAchieveKDDefeat", "gAchieveKDDefeat_Grayed",
"Defeat King Dodongo." },
{ ACHIEVEMENT_DEFEAT_BARINADE, "Bio-Electric Anemone", "gAchieveBarinadeDefeat", "gAchieveBarinadeDefeat_Grayed",
"Defeat Barinade." },
{ ACHIEVEMENT_DEFEAT_PG, "Evil Spirit from Beyond", "gAchievePGDefeat", "gAchievePGDefeat_Grayed",
"Defeat Phantom Ganon." },
{ ACHIEVEMENT_DEFEAT_VOLVAGIA, "Subterranean Lava Dragon", "gAchieveVolvagiaDefeat", "gAchieveVolvagiaDefeat_Grayed",
"Defeat Volvagia." },
{ ACHIEVEMENT_DEFEAT_MORPHA, "Giant Aquatic Amoeba", "gAchieveMorphaDefeat", "gAchieveMorphaDefeat_Grayed",
"Defeat Morpha." },
{ ACHIEVEMENT_DEFEAT_BONGO, "Phantom Shadow Beast", "gAchieveBongoDefeat", "gAchieveBongoDefeat_Grayed",
"Defeat Bongo." },
{ ACHIEVEMENT_DEFEAT_TWINROVA, "Sorceress Sisters", "gAchieveTwinrovaDefeat", "gAchieveTwinrovaDefeat_Grayed",
"Defeat Twinrova." },
{ ACHIEVEMENT_DEFEAT_GANONDORF, "Great King of Evil", "gAchieveGanondorfDefeat", "gAchieveGanondorfDefeat_Grayed",
"Defeat Ganondorf." },
{ ACHIEVEMENT_DEFEAT_GANON, "Ganon", "gAchieveGanonDefeat", "gAchieveGanonDefeat_Grayed",
"Defeat Ganon." },
};

const size_t gAchievementCount =
sizeof(gAchievements) / sizeof(gAchievements[0]);
38 changes: 38 additions & 0 deletions soh/soh/Enhancements/achievements.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

typedef enum {
ACHIEVEMENT_DEFEAT_GOHMA = 0,
ACHIEVEMENT_DEFEAT_KD = 1,
ACHIEVEMENT_DEFEAT_BARINADE = 2,
ACHIEVEMENT_DEFEAT_PG = 3,
ACHIEVEMENT_DEFEAT_VOLVAGIA = 4,
ACHIEVEMENT_DEFEAT_MORPHA = 5,
ACHIEVEMENT_DEFEAT_BONGO = 6,
ACHIEVEMENT_DEFEAT_TWINROVA = 7,
ACHIEVEMENT_DEFEAT_GANONDORF = 8,
ACHIEVEMENT_DEFEAT_GANON = 9,
} AchievementId;

typedef struct {
AchievementId id;
const char* name;
const char* icon;
const char* grayedIcon;
const char* description;
} AchievementInfo;

extern const AchievementInfo gAchievements[];
extern const size_t gAchievementCount;

#ifdef __cplusplus
extern "C" {
#endif

void Achievements_Init();
bool Achievements_IsUnlocked(AchievementId id);
void Achievements_TryUnlock(AchievementId id);
const AchievementInfo* Achievements_GetInfo(AchievementId id);

#ifdef __cplusplus
}
#endif
71 changes: 55 additions & 16 deletions soh/soh/Notification/Notification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace Notification {
static uint32_t nextId = 0;
static std::vector<Options> notifications = {};

void drawStandardNotification(const Options& notification);
void drawAchievementNotification(const Options& notification);

void Window::Draw() {
auto vp = ImGui::GetMainViewport();

Expand Down Expand Up @@ -61,6 +64,11 @@ void Window::Draw() {
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0f);
}

if(notification.achievement) {
ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 215, 0, 255));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 3.0f);
}

ImGui::Begin(("notification#" + std::to_string(notification.id)).c_str(), nullptr,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar |
Expand Down Expand Up @@ -91,24 +99,17 @@ void Window::Draw() {

ImGui::SetWindowPos(notificationPos);

if (notification.itemIcon != nullptr) {
ImGui::Image(
std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui())
->GetTextureByName(notification.itemIcon),
ImVec2(24, 24));
ImGui::SameLine();
}
if (!notification.prefix.empty()) {
ImGui::TextColored(notification.prefixColor, "%s", notification.prefix.c_str());
ImGui::SameLine();
}
ImGui::TextColored(notification.messageColor, "%s", notification.message.c_str());
if (!notification.suffix.empty()) {
ImGui::SameLine();
ImGui::TextColored(notification.suffixColor, "%s", notification.suffix.c_str());
if (notification.achievement) {
drawAchievementNotification(notification);
} else {
drawStandardNotification(notification);
}

ImGui::End();
if(notification.achievement) {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::PopStyleVar();
}

Expand Down Expand Up @@ -137,10 +138,48 @@ void Emit(Options notification) {
notification.remainingTime = CVarGetFloat(CVAR_SETTING("Notifications.Duration"), 10.0f);
}
notifications.push_back(notification);
if (!notification.mute && !CVarGetInteger(CVAR_SETTING("Notifications.Mute"), 0)) {
if (!notification.mute && !notification.achievement && !CVarGetInteger(CVAR_SETTING("Notifications.Mute"), 0)) {
Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}
}

void drawStandardNotification(const Options& notification) {
if (notification.itemIcon != nullptr) {
ImGui::Image(
std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui())
->GetTextureByName(notification.itemIcon),
ImVec2(24, 24));
ImGui::SameLine();
}
if (!notification.prefix.empty()) {
ImGui::TextColored(notification.prefixColor, "%s", notification.prefix.c_str());
ImGui::SameLine();
}
ImGui::TextColored(notification.messageColor, "%s", notification.message.c_str());
if (!notification.suffix.empty()) {
ImGui::SameLine();
ImGui::TextColored(notification.suffixColor, "%s", notification.suffix.c_str());
}
}

void drawAchievementNotification(const Options& notification) {
if (notification.itemIcon != nullptr) {
ImGui::Image(
std::dynamic_pointer_cast<Fast::Fast3dGui>(Ship::Context::GetRawInstance()->GetWindow()->GetGui())
->GetTextureByName(notification.itemIcon),
ImVec2(80, 80));
ImGui::SameLine();
}

ImGui::BeginGroup();

if (!notification.prefix.empty()) {
ImGui::TextColored(notification.prefixColor, "%s", notification.prefix.c_str());
ImGui::Dummy(ImVec2(0.0f, 2.0f)); //spacing
}
ImGui::TextColored(notification.messageColor, "%s", notification.message.c_str());
ImGui::EndGroup();
}

} // namespace Notification
1 change: 1 addition & 0 deletions soh/soh/Notification/Notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct Options {
ImVec4 suffixColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f);
float remainingTime = 0.0f; // Seconds
bool mute = false; // whether notification should make a noise
bool achievement = false; // whether this notification is for an achievement (custom visuals)
};

class Window final : public Ship::GuiWindow {
Expand Down
1 change: 1 addition & 0 deletions soh/soh/SaveManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ void SaveManager::StartupCheckAndInitMeta(int fileNum) {
SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].buildVersion,
metaSaveBlock["sections"]["sohStats"]["data"]["buildVersion"],
ARRAY_COUNT(fileMetaInfo[fileNum].buildVersion));

}

void SaveManager::InitMeta(int fileNum) {
Expand Down
23 changes: 21 additions & 2 deletions soh/soh/SohGui/Menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,10 @@ void Menu::DrawElement() {
headerWidth += 200.0f;
}
for (auto& label : menuOrder) {
if (label == "Achievements") {
continue; //ignore achievements for header sizing
}

ImVec2 size = ImGui::CalcTextSize(label.c_str());
headerSizes.push_back(size);
headerWidth += size.x + style.FramePadding.x * 2 + style.ItemSpacing.x;
Expand Down Expand Up @@ -713,10 +717,17 @@ void Menu::DrawElement() {
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_HorizontalScrollbar);
uint8_t curIndex = 0;
for (auto& label : menuOrder) {
auto& entry = menuEntries.at(label);

if (label == "Achievements") {
if (headerIndex == label) {
sidebar = &entry.sidebars;
}
continue; //make the achievement menu header hidden
}
if (curIndex != 0) {
ImGui::SameLine();
}
auto& entry = menuEntries.at(label);
std::string nextIndex = label;
UIWidgets::PushStyleButton(menuThemeIndex);
if (headerIndex != label) {
Expand Down Expand Up @@ -763,7 +774,15 @@ void Menu::DrawElement() {
ImGui::PopStyleColor();
}
ImGui::EndChild();
ImGui::SameLine(menuSize.x - (buttonSize.x * 3) - (style.ItemSpacing.x * 2));
ImGui::SameLine(menuSize.x - (buttonSize.x * 4) - (style.ItemSpacing.x * 3));
UIWidgets::ButtonOptions options4 = {};
options4.color = UIWidgets::Colors::Violet;
options4.size = UIWidgets::Sizes::Inline;
options4.tooltip = "Achievements";
if (UIWidgets::Button(ICON_FA_TROPHY, options4)) {
CVarSetString(CVAR_SETTING("Menu.ActiveHeader"), "Achievements");
}
ImGui::SameLine();
UIWidgets::ButtonOptions options3 = {};
options3.color = UIWidgets::Colors::Red;
options3.size = UIWidgets::Sizes::Inline;
Expand Down
3 changes: 2 additions & 1 deletion soh/soh/SohGui/SohMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ void SohMenu::AddMenuElements() {
AddMenuRandomizer();
AddMenuNetwork();
AddMenuDevTools();

AddMenuAchievements();

if (CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0)) {
InsertSidebarSearch();
}
Expand Down
1 change: 1 addition & 0 deletions soh/soh/SohGui/SohMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class SohMenu : public Ship::Menu {
void AddMenuDevTools();
void AddMenuRandomizer();
void AddMenuNetwork();
void AddMenuAchievements();
static void UpdateLanguageMap(std::map<int32_t, const char*>& languageMap);

private:
Expand Down
Loading