Skip to content

Commit

Permalink
Merge pull request HarbourMasters#3539 from Archez/merge-echo-develop
Browse files Browse the repository at this point in the history
Merge (8.0.4) develop-macready -> develop
  • Loading branch information
briaguya-ai authored Dec 18, 2023
2 parents 86044a1 + 03da69d commit 9b94761
Show file tree
Hide file tree
Showing 22 changed files with 351 additions and 143 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")

set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")

project(Ship VERSION 8.0.3 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Delta" CACHE STRING "")
project(Ship VERSION 8.0.4 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")

set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
Expand Down
1 change: 1 addition & 0 deletions soh/soh/Enhancements/custom-message/CustomMessageTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ typedef enum {
TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891,
TEXT_WARP_PRELUDE_OF_LIGHT = 0x892,
TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200,
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
} TextIDs;
Expand Down
2 changes: 2 additions & 0 deletions soh/soh/Enhancements/game-interactor/GameInteractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
#include <thread>
#include <vector>
#include <functional>
#include <string>

#ifdef ENABLE_REMOTE_CONTROL
#include <SDL2/SDL_net.h>
Expand Down Expand Up @@ -218,6 +219,7 @@ class GameInteractor {

DEFINE_HOOK(OnSetGameLanguage, void());

DEFINE_HOOK(OnFileDropped, void(std::string filePath));
DEFINE_HOOK(OnAssetAltChange, void());

// Helpers
Expand Down
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/gameplaystats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ void DrawGameplayStatsOptionsTab() {
}

void GameplayStatsWindow::DrawElement() {
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing);
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Gameplay Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::End();
return;
Expand Down
4 changes: 2 additions & 2 deletions soh/soh/Enhancements/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,8 @@ void RegisterRandomizedEnemySizes() {
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1);

// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
uint8_t smallOnlyEnemy =
actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || ACTOR_EN_DH;
uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;

// Only apply to enemies and bosses.
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void AreaTable_Init_DeathMountain() {
Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}),
Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}),
Entrance(GC_SHOP, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && (CanBlastOrSmash || GoronBracelet || GoronCityChildFire || CanUse(BOW)));}}),
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || GCDaruniasDoorOpenChild;}}),
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && GCDaruniasDoorOpenChild);}}),
Entrance(GC_GROTTO_PLATFORM, {[]{return IsAdult && ((CanPlay(SongOfTime) && ((EffectiveHealth > 2) || CanUse(GORON_TUNIC) || CanUse(LONGSHOT) || CanUse(NAYRUS_LOVE))) || (EffectiveHealth > 1 && CanUse(GORON_TUNIC) && CanUse(HOOKSHOT)) || (CanUse(NAYRUS_LOVE) && CanUse(HOOKSHOT)) || (EffectiveHealth > 2 && CanUse(HOOKSHOT) && LogicGoronCityGrotto));}}),
});

Expand Down
36 changes: 14 additions & 22 deletions soh/soh/Enhancements/randomizer/randomizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,31 +365,16 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
#pragma GCC push_options
#pragma GCC optimize ("O0")
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
try {
if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return false;
}

json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;

if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
return false;
}

if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return false;
} else {
return true;
}

return false;
} catch (std::exception& e) {
SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what());
return false;
} catch (...) {
SPDLOG_ERROR("Error checking if spoiler file exists");
return false;
}

return false;
}
#pragma GCC pop_options
#pragma optimize("", on)
Expand Down Expand Up @@ -495,6 +480,13 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
"Zu {{location}}?\x1B&%gOK&No%w\x02",
"Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02"));

// Bow Shooting Gallery reminder
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW,
CustomMessage("Come back when you have your own&bow and you'll get a %rdifferent prize%w!",
"Komm wieder sobald du deinen eigenen&Bogen hast, um einen %rspeziellen Preis%w zu&erhalten!",
"J'aurai %rune autre récompense%w pour toi&lorsque tu auras ton propre arc."));

// Lake Hylia water level system
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
CustomMessage("Water level control system.&Keep away!",
"Wasserstand Kontrollsystem&Finger weg!",
Expand Down
13 changes: 9 additions & 4 deletions soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
bool hideShopRightChecks = true;
bool alwaysShowGS = false;

std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
Expand Down Expand Up @@ -434,7 +435,6 @@ void CheckTrackerLoadGame(int32_t fileNum) {
} else {
realRcObj = rcObj;
}
if (!IsVisibleInCheckTracker(realRcObj)) continue;

checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
Expand Down Expand Up @@ -514,6 +514,10 @@ void CheckTrackerTransition(uint32_t sceneNum) {
}

void CheckTrackerFrame() {
if (IS_RANDO) {
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
}
if (!GameInteractor::IsSaveLoaded()) {
return;
}
Expand Down Expand Up @@ -1084,7 +1088,6 @@ void LoadSettings() {
showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
:false;
hideShopRightChecks = IS_RANDO ? CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1) : false;

if (IS_RANDO) {
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
Expand Down Expand Up @@ -1148,7 +1151,7 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
) &&
(rcObj.rcType != RCTYPE_MERCHANT || showMerchants) &&
(rcObj.rcType != RCTYPE_OCARINA || showOcarinas) &&
(rcObj.rcType != RCTYPE_SKULL_TOKEN ||
(rcObj.rcType != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) ||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea))
) &&
Expand Down Expand Up @@ -1518,7 +1521,9 @@ void CheckTrackerSettingsWindow::DrawElement() {
UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers");
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true);
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops. Requires save reload.");
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "");
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");

ImGui::TableNextColumn();

Expand Down
143 changes: 83 additions & 60 deletions soh/soh/OTRGlobals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ GameInteractorSail* GameInteractorSail::Instance;

#include "soh/config/ConfigUpdaters.h"

void SoH_ProcessDroppedFiles(std::string filePath);

OTRGlobals* OTRGlobals::Instance;
SaveManager* SaveManager::Instance;
CustomMessageManager* CustomMessageManager::Instance;
Expand Down Expand Up @@ -1087,9 +1089,9 @@ extern "C" void InitOTR() {
OTRGlobals::Instance = new OTRGlobals();
CustomMessageManager::Instance = new CustomMessageManager();
ItemTableManager::Instance = new ItemTableManager();
GameInteractor::Instance = new GameInteractor();
SaveManager::Instance = new SaveManager();
SohGui::SetupGuiElements();
GameInteractor::Instance = new GameInteractor();
AudioCollection::Instance = new AudioCollection();
ActorDB::Instance = new ActorDB();
#ifdef __APPLE__
Expand All @@ -1114,6 +1116,11 @@ extern "C" void InitOTR() {

InitMods();
ActorDB::AddBuiltInCustomActors();
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
CVarClear("gRandomizerNewFileDropped");
CVarClear("gRandomizerDroppedFile");
// #endregion
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFileDropped>(SoH_ProcessDroppedFiles);

time_t now = time(NULL);
tm *tm_now = localtime(&now);
Expand Down Expand Up @@ -1303,6 +1310,16 @@ extern "C" void Graph_StartFrame() {
}
}
#endif

if (CVarGetInteger("gNewFileDropped", 0)) {
std::string filePath = SohUtils::Sanitize(CVarGetString("gDroppedFile", ""));
if (!filePath.empty()) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnFileDropped>(filePath);
}
CVarClear("gNewFileDropped");
CVarClear("gDroppedFile");
}

OTRGlobals::Instance->context->GetWindow()->StartFrame();
}

Expand Down Expand Up @@ -2170,10 +2187,10 @@ Color_RGB8 GetColorForControllerLED() {
if (source == LED_SOURCE_CUSTOM) {
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
}
if (criticalOverride || source == LED_SOURCE_HEALTH) {
if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) {
if (HealthMeter_IsCritical()) {
color = { 0xFF, 0, 0 };
} else if (source == LED_SOURCE_HEALTH) {
} else if (gSaveContext.healthCapacity != 0 && source == LED_SOURCE_HEALTH) {
if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
color = { 0xFF, 0xFF, 0 };
} else {
Expand Down Expand Up @@ -2566,6 +2583,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, Randomizer_GetSettingValue(RSK_WARP_SONG_HINTS) == RO_GENERIC_OFF);
} else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId);
} else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW);
} else if (textId == 0x3052 || (textId >= 0x3069 && textId <= 0x3070)) { //Fire Temple gorons
u16 choice = Random(0, NUM_GORON_MESSAGES);
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
Expand Down Expand Up @@ -2655,70 +2674,74 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
gfx_register_blended_texture(name, mask, replacement);
}

// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
extern "C" void SoH_ProcessDroppedFiles() {
const char* droppedFile = CVarGetString("gDroppedFile", "");
if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) {
try {
std::ifstream configStream(SohUtils::Sanitize(droppedFile));
if (!configStream) {
return;
}
void SoH_ProcessDroppedFiles(std::string filePath) {
try {
std::ifstream configStream(filePath);
if (!configStream) {
return;
}

nlohmann::json configJson;
configStream >> configJson;
nlohmann::json configJson;
configStream >> configJson;

if (!configJson.contains("CVars")) {
return;
}
// #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer
if (configJson.contains("version") && configJson.contains("finalSeed")) {
CVarSetString("gRandomizerDroppedFile", filePath.c_str());
CVarSetInteger("gRandomizerNewFileDropped", 1);
return;
}
// #endregion

clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);
if (!configJson.contains("CVars")) {
return;
}

// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();
clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);

for (auto& [key, value] : cvars.items()) {
// Replace slashes with dots in key, and remove leading dot
std::string path = key;
std::replace(path.begin(), path.end(), '/', '.');
if (path[0] == '.') {
path.erase(0, 1);
}
if (value.is_string()) {
CVarSetString(path.c_str(), value.get<std::string>().c_str());
} else if (value.is_number_integer()) {
CVarSetInteger(path.c_str(), value.get<int>());
} else if (value.is_number_float()) {
CVarSetFloat(path.c_str(), value.get<float>());
}
}
// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();

auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGuiWindow("Console")->Hide();
gui->GetGuiWindow("Actor Viewer")->Hide();
gui->GetGuiWindow("Collision Viewer")->Hide();
gui->GetGuiWindow("Save Editor")->Hide();
gui->GetGuiWindow("Display List Viewer")->Hide();
gui->GetGuiWindow("Stats")->Hide();
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();

gui->SaveConsoleVariablesOnNextTick();

uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
} catch (std::exception& e) {
SPDLOG_ERROR("Failed to load config file: {}", e.what());
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
} catch (...) {
SPDLOG_ERROR("Failed to load config file");
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
for (auto& [key, value] : cvars.items()) {
// Replace slashes with dots in key, and remove leading dot
std::string path = key;
std::replace(path.begin(), path.end(), '/', '.');
if (path[0] == '.') {
path.erase(0, 1);
}
if (value.is_string()) {
CVarSetString(path.c_str(), value.get<std::string>().c_str());
} else if (value.is_number_integer()) {
CVarSetInteger(path.c_str(), value.get<int>());
} else if (value.is_number_float()) {
CVarSetFloat(path.c_str(), value.get<float>());
}
}

auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGuiWindow("Console")->Hide();
gui->GetGuiWindow("Actor Viewer")->Hide();
gui->GetGuiWindow("Collision Viewer")->Hide();
gui->GetGuiWindow("Save Editor")->Hide();
gui->GetGuiWindow("Display List Viewer")->Hide();
gui->GetGuiWindow("Stats")->Hide();
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();

gui->SaveConsoleVariablesOnNextTick();

uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
} catch (std::exception& e) {
SPDLOG_ERROR("Failed to load config file: {}", e.what());
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
} catch (...) {
SPDLOG_ERROR("Failed to load config file");
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
}
}
// #endregion
1 change: 0 additions & 1 deletion soh/soh/OTRGlobals.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
void SoH_ProcessDroppedFiles();

GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);
Expand Down
Loading

0 comments on commit 9b94761

Please sign in to comment.