From a5c2bacfe0386d6cd533f06289c193dfa3854392 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Tue, 20 Jun 2023 22:04:32 -0400 Subject: [PATCH 01/24] fix: restore internal resolution and msaa slider functionality (#3026) Co-authored-by: briaguya --- soh/soh/SohMenuBar.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 337a848e0..64e6a0c4a 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -249,16 +249,16 @@ void DrawSettingsMenu() { if (ImGui::BeginMenu("Graphics")) { #ifndef __APPLE__ - UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true); + if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true)) { + LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1)); + }; UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); - // OTRTODO: fix this - // LUS::SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1)); #endif #ifndef __WIIU__ - UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false); + if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false)) { + LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1)); + }; UIWidgets::Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); - // OTRTODO: fix this - // LUS::SetMSAALevel(CVarGetInteger("gMSAAValue", 1)); #endif { // FPS Slider From a05d8131ec894458b99ca1fff12780da164e81e1 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Tue, 20 Jun 2023 22:05:52 -0400 Subject: [PATCH 02/24] invert culling for collision viewer (#3021) --- soh/soh/Enhancements/debugger/colViewer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp index 7aa7ab40a..e46628305 100644 --- a/soh/soh/Enhancements/debugger/colViewer.cpp +++ b/soh/soh/Enhancements/debugger/colViewer.cpp @@ -693,11 +693,23 @@ extern "C" void DrawColViewer() { OPEN_DISPS(gPlayState->state.gfxCtx); + uint8_t mirroredWorld = CVarGetInteger("gMirroredWorld", 0); + // Col viewer needs inverted culling in mirror mode for both OPA and XLU buffers + if (mirroredWorld) { + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + } + opaDl.push_back(gsSPEndDisplayList()); gSPDisplayList(POLY_OPA_DISP++, opaDl.data()); xluDl.push_back(gsSPEndDisplayList()); gSPDisplayList(POLY_XLU_DISP++, xluDl.data()); + if (mirroredWorld) { + gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + } + CLOSE_DISPS(gPlayState->state.gfxCtx); } From def7a15354f54c48e965def0b73c39304040cc52 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:58:36 -0400 Subject: [PATCH 03/24] handle random mq dungeon count better (#3036) Co-authored-by: briaguya --- .../randomizer/3drando/spoiler_log.cpp | 9 +++++++++ soh/soh/Enhancements/randomizer/randomizer.cpp | 15 ++++++++++++++- .../randomizer/randomizer_check_tracker.cpp | 8 ++++---- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index dc4f41ae0..098652f10 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -402,6 +402,15 @@ static void WriteSettings(const bool printAll = false) { // 3drando doesn't have a "skip child zelda" setting, manually add it to the spoilerfile jsonData["settings"]["Skip Child Zelda"] = Settings::skipChildZelda; + // 3drando uses an MQ dungeon count of 13 to mean random, manually add that to the spoilerfile as a bool + if (Settings::MQDungeonCount.GetSelectedOptionIndex() == 0) { + jsonData["settings"]["World Settings:MQ Dungeons"] = "None"; + } else if (Settings::MQDungeonCount.GetSelectedOptionIndex() == 13) { + jsonData["settings"]["World Settings:MQ Dungeons"] = "Random Number"; + } else { + jsonData["settings"]["World Settings:MQ Dungeons"] = "Set Number"; + } + // spoilerLog.RootElement()->InsertEndChild(parentNode); // for (const uint32_t key : allLocations) { diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 202147bb9..08b213725 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -348,6 +348,7 @@ std::unordered_map SpoilerfileSettingNameToEn { "Timesaver Settings:Complete Mask Quest", RSK_COMPLETE_MASK_QUEST }, { "Timesaver Settings:Skip Scarecrow's Song", RSK_SKIP_SCARECROWS_SONG }, { "Timesaver Settings:Enable Glitch-Useful Cutscenes", RSK_ENABLE_GLITCH_CUTSCENES }, + { "World Settings:MQ Dungeons", RSK_RANDOM_MQ_DUNGEONS }, { "World Settings:MQ Dungeon Count", RSK_MQ_DUNGEON_COUNT } }; @@ -1032,6 +1033,15 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_KAK_TOKENS; } break; + case RSK_RANDOM_MQ_DUNGEONS: + if (it.value() == "None") { + gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_NONE; + } else if (it.value() == "Random Number") { + gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_RANDOM_NUMBER; + } else if (it.value() == "Set Number") { + gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_SET_NUMBER; + } + break; case RSK_SKIP_CHILD_ZELDA: gSaveContext.randoSettings[index].value = it.value(); break; @@ -5409,7 +5419,10 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { break; } - if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) { + if (this->randoSettings[RSK_RANDOM_MQ_DUNGEONS] == RO_MQ_DUNGEONS_NONE || + (this->randoSettings[RSK_RANDOM_MQ_DUNGEONS] == RO_MQ_DUNGEONS_SET_NUMBER && + this->randoSettings[RSK_MQ_DUNGEON_COUNT] == 12) + ) { messageEntry.Replace("{{typeHint}}", ""); } else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) { messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]); diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 6998c6bb5..d1e5514be 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -567,10 +567,10 @@ void InitializeChecks() { areasSpoiled |= (1 << rcObj.rcArea); } - showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) > 0); - //Bug: the above will spoil that everything is vanilla if the random count rolled 0. - // Should use the below instead, but the setting isn't currently saved to the savefile - //showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) != RO_GENERIC_OFF); + showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER || + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12) + ); UpdateChecks(); UpdateInventoryChecks(); From 531b346fc9f78881f968321010ea4de3ee362fe3 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:50:51 +0100 Subject: [PATCH 04/24] Add exact item and check to the spoiler log for WotH hints (#3043) --- soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 098652f10..cb87bc115 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -766,9 +766,9 @@ static void WriteHints(int language) { std::string textStr = AutoFormatHintTextString(unformattedHintTextString); jsonData["hints"][location->GetName()]["hint"] = textStr; jsonData["hints"][location->GetName()]["type"] = hintTypeNames.find(hintType)->second; - if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM) { + if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) { jsonData["hints"][location->GetName()]["item"] = hintedLocation->GetPlacedItemName().GetEnglish(); - if (hintType != HINT_TYPE_NAMED_ITEM) { + if (hintType != HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) { jsonData["hints"][location->GetName()]["location"] = hintedLocation->GetName(); } } From b7dca5d5a8709b1560495cb50f7e58ac5750beb7 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Mon, 3 Jul 2023 10:29:06 -0400 Subject: [PATCH 05/24] Fix: Various PAL1.1 asset offsets and Main Menu options screen (#3045) * fix pal11 ganondorf falling platform offsets * fix pal11 dins fire offsets * add game region and platform methods * fix pal11 file menu options * move --- .../overlays/ovl_Bg_Ganon_Otyuka.xml | 6 +-- .../N64_PAL_11/overlays/ovl_File_Choose.xml | 17 ++++--- .../N64_PAL_11/overlays/ovl_Magic_Fire.xml | 10 ++-- soh/soh/OTRGlobals.cpp | 46 +++++++++++++++++++ soh/soh/OTRGlobals.h | 8 ++++ .../ovl_file_choose/z_file_nameset_PAL.c | 37 +++++++++++++-- 6 files changed, 104 insertions(+), 20 deletions(-) diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml index 5bba7f35b..99d32f6e6 100644 --- a/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml +++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml @@ -1,8 +1,8 @@ - - + + - + diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml index 55c8779e4..de41882eb 100644 --- a/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml +++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml @@ -3,18 +3,20 @@ - + + - + + + - + @@ -24,5 +26,6 @@ + diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml index b5b7ba82d..0164ab203 100644 --- a/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml +++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml @@ -1,10 +1,10 @@ - - - + + + - - + + diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index b044c0533..9c48d7c77 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1030,6 +1030,52 @@ extern "C" uint32_t ResourceMgr_GetGameVersion(int index) { return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index]; } +extern "C" uint32_t ResourceMgr_GetGamePlatform(int index) { + uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index]; + + switch (version) { + case OOT_NTSC_US_10: + case OOT_NTSC_US_11: + case OOT_NTSC_US_12: + case OOT_PAL_10: + case OOT_PAL_11: + return GAME_PLATFORM_N64; + case OOT_NTSC_JP_GC: + case OOT_NTSC_US_GC: + case OOT_PAL_GC: + case OOT_NTSC_JP_MQ: + case OOT_NTSC_US_MQ: + case OOT_PAL_MQ: + case OOT_PAL_GC_DBG1: + case OOT_PAL_GC_DBG2: + case OOT_PAL_GC_MQ_DBG: + return GAME_PLATFORM_GC; + } +} + +extern "C" uint32_t ResourceMgr_GetGameRegion(int index) { + uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index]; + + switch (version) { + case OOT_NTSC_US_10: + case OOT_NTSC_US_11: + case OOT_NTSC_US_12: + case OOT_NTSC_JP_GC: + case OOT_NTSC_US_GC: + case OOT_NTSC_JP_MQ: + case OOT_NTSC_US_MQ: + return GAME_REGION_NTSC; + case OOT_PAL_10: + case OOT_PAL_11: + case OOT_PAL_GC: + case OOT_PAL_MQ: + case OOT_PAL_GC_DBG1: + case OOT_PAL_GC_DBG2: + case OOT_PAL_GC_MQ_DBG: + return GAME_REGION_PAL; + } +} + uint32_t IsSceneMasterQuest(s16 sceneNum) { uint32_t value = 0; uint8_t mqMode = CVarGetInteger("gBetterDebugWarpScreenMQMode", WARP_MODE_OVERRIDE_OFF); diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 05ea04e12..329f4f0bf 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -6,6 +6,12 @@ #include "SaveManager.h" #include +#define GAME_REGION_NTSC 0 +#define GAME_REGION_PAL 1 + +#define GAME_PLATFORM_N64 0 +#define GAME_PLATFORM_GC 1 + #ifdef __cplusplus #include #include "Enhancements/savestates.h" @@ -59,6 +65,8 @@ uint32_t ResourceMgr_GameHasMasterQuest(); uint32_t ResourceMgr_GameHasOriginal(); uint32_t ResourceMgr_GetNumGameVersions(); uint32_t ResourceMgr_GetGameVersion(int index); +uint32_t ResourceMgr_GetGamePlatform(int index); +uint32_t ResourceMgr_GetGameRegion(int index); void ResourceMgr_LoadDirectory(const char* resName); char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize); uint8_t ResourceMgr_FileExists(const char* resName); diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c index 17d63fb0d..3aca1ee8c 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c @@ -909,7 +909,13 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) { } } - if (gSaveContext.language == LANGUAGE_GER) { + uint8_t versionIndex = ResourceMgr_GameHasMasterQuest() && ResourceMgr_GameHasOriginal(); + uint8_t isPalN64 = ResourceMgr_GetGameRegion(versionIndex) == GAME_REGION_PAL && + ResourceMgr_GetGamePlatform(versionIndex) == GAME_PLATFORM_N64; + uint8_t isPalGC = ResourceMgr_GetGameRegion(versionIndex) == GAME_REGION_PAL && + ResourceMgr_GetGamePlatform(versionIndex) == GAME_PLATFORM_GC; + + if (gSaveContext.language == LANGUAGE_GER && isPalGC) { gSPVertex(POLY_OPA_DISP++, D_80811E30, 32, 0); } else { gSPVertex(POLY_OPA_DISP++, D_80811D30, 32, 0); @@ -926,13 +932,24 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) { G_IM_SIZ_8b, gOptionsMenuHeaders[i].width[gSaveContext.language], gOptionsMenuHeaders[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + + if (i == 2 && gSaveContext.language == LANGUAGE_GER && isPalN64) { + // Pal N64 German vertex for Z target header are offset by 12 vertices + gSP1Quadrangle(POLY_OPA_DISP++, vtx + 12, vtx + 2 + 12, vtx + 3 + 12, vtx + 1 + 12, 0); + } else { + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + } } - if (gSaveContext.language == LANGUAGE_GER) { + if (gSaveContext.language == LANGUAGE_GER && isPalGC) { gSPVertex(POLY_OPA_DISP++, D_80812130, 32, 0); } else { - gSPVertex(POLY_OPA_DISP++, D_80811F30, 32, 0); + // PAL N64 has extra german vertices combined in the regular array instead of a dedicated array + if (isPalN64) { + gSPVertex(POLY_OPA_DISP++, D_80811F30, 40, 0); + } else { + gSPVertex(POLY_OPA_DISP++, D_80811F30, 32, 0); + } } for (i = 0, vtx = 0; i < 4; i++, vtx += 4) { @@ -979,7 +996,17 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) { G_IM_SIZ_8b, gOptionsMenuSettings[i].width[gSaveContext.language], gOptionsMenuSettings[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + // Pal N64 German vertices for z target options are offset an by 8 + if (gSaveContext.language == LANGUAGE_GER && isPalN64) { + gSP1Quadrangle(POLY_OPA_DISP++, vtx + 8, vtx + 2 + 8, vtx + 3 + 8, vtx + 1 + 8, 0); + } else { + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + } + } + + // Pal N64 needs to skip over the extra german vertices to get to brightness vertices + if (isPalN64) { + vtx += 8; } gDPPipeSync(POLY_OPA_DISP++); From 360a97ecfd37a2f81f3d7f53bedbb95b9e814bc9 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Mon, 3 Jul 2023 10:29:21 -0400 Subject: [PATCH 06/24] fix: various pal gc asset offset issues (#3052) --- .../xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml | 14 +++++++------- .../GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml | 16 ++++++++-------- .../xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml | 10 +++++----- .../GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml | 10 +++++----- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml b/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml index 5c598c8b4..a3b79fb17 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml @@ -1,14 +1,14 @@ - - - - - - + + + + + + - + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml index 900ec46ac..a5fc99347 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml @@ -1,21 +1,21 @@ - - + + - + - + - + - + - + - + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml index 9a53952f0..d16cca39b 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml @@ -1,10 +1,10 @@ - - - + + + - - + + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml index 11f278866..b1022da79 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml @@ -1,10 +1,10 @@ - - - + + + - - + + From 22e4020265ffba7b8b9ef5a8ec9655b6b4074ef4 Mon Sep 17 00:00:00 2001 From: aMannus Date: Thu, 13 Jul 2023 03:57:43 +0200 Subject: [PATCH 07/24] Fix Market Sneak with masks (#3047) --- soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c index 9fffe1c7a..a11973426 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c +++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c @@ -334,7 +334,12 @@ void func_80A56B40(EnHeishi4* this, PlayState* play) { return; } if (this->type == HEISHI4_AT_MARKET_NIGHT) { - if (CVarGetInteger("gMarketSneak", 0)) { + Player* player = GET_PLAYER(play); + // Only allow sneaking when not wearing a mask as that triggers different dialogue. MM Bunny hood disables + // these interactions, so bunny hood is fine in that case. + if (CVarGetInteger("gMarketSneak", 0) && + (player->currentMask == PLAYER_MASK_NONE || + (player->currentMask == PLAYER_MASK_BUNNY && CVarGetInteger("gMMBunnyHood", 0)))) { this->actionFunc = EnHeishi4_MarketSneak; } else { this->actionFunc = func_80A56614; From 423e8b96b148e0efc69a3c811acfda88160ad6a6 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Wed, 12 Jul 2023 21:58:43 -0400 Subject: [PATCH 08/24] fix: add missing hashes in mac and linux launch scripts (#3070) --- scripts/linux/appimage/soh.sh | 8 ++++++++ soh/macosx/soh-macos.sh | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/scripts/linux/appimage/soh.sh b/scripts/linux/appimage/soh.sh index 1afcbb9b7..e503680ac 100644 --- a/scripts/linux/appimage/soh.sh +++ b/scripts/linux/appimage/soh.sh @@ -69,6 +69,14 @@ while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do continue fi ;; + cfecfdc58d650e71a200c81f033de4e6d617a9f6) + if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then + ROM=GC_MQ_D + OTRNAME="oot-mq.otr" + else + continue + fi + ;; 517bd9714c73cb96c21e7c2ef640d7b55186102f) if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then ROM=GC_MQ_D diff --git a/soh/macosx/soh-macos.sh b/soh/macosx/soh-macos.sh index 41b406295..b77a5a9fa 100755 --- a/soh/macosx/soh-macos.sh +++ b/soh/macosx/soh-macos.sh @@ -44,10 +44,14 @@ if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then ROM_TYPE=0;; 0227d7c0074f2d0ac935631990da8ec5914597b4) ROM_TYPE=0;; + cfbb98d392e4a9d39da8285d10cbef3974c2f012) + ROM_TYPE=0;; 50bebedad9e0f10746a52b07239e47fa6c284d03) ROM_TYPE=1;; 079b855b943d6ad8bd1eb026c0ed169ecbdac7da) ROM_TYPE=1;; + cfecfdc58d650e71a200c81f033de4e6d617a9f6) + ROM_TYPE=1;; 517bd9714c73cb96c21e7c2ef640d7b55186102f) ROM_TYPE=1;; *) @@ -130,6 +134,9 @@ if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then 079b855b943d6ad8bd1eb026c0ed169ecbdac7da) ROM=GC_MQ_D OTRNAME="oot-mq.otr";; + cfecfdc58d650e71a200c81f033de4e6d617a9f6) + ROM=GC_MQ_D + OTRNAME="oot-mq.otr";; 517bd9714c73cb96c21e7c2ef640d7b55186102f) ROM=GC_MQ_D OTRNAME="oot-mq.otr";; From 93ab9f0072a1883acff1672f266d589b964cf6c8 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 12 Jul 2023 19:03:43 -0700 Subject: [PATCH 09/24] [Fix] Merchant Check Fixes (#3067) * Change Granny's Shop collection type to RandomizerInf with proper scene and inf IDs so the tracker tracks it properly. * Changed pendingSale code to be checked outside of decrementing if block. --- .../randomizer/3drando/item_location.cpp | 2 +- soh/src/code/z_parameter.c | 32 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp index bf0257064..7283635a3 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp @@ -120,7 +120,7 @@ void LocationTable_Init() { locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, 0x3E, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, 0x30, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, 0x20, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cKakarikoVillage, Category::cKakariko, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); - locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, 0x10, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cKakarikoVillage, Category::cKakariko, Category::cMerchant}, SpoilerCollectionCheck::EventChkInf(0x32), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); + locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, 0x10, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cKakarikoVillage, Category::cKakariko, Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(0x4E, 0x88), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, 0x1D, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, 0x0F, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, 0x0E, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cKakarikoVillage, Category::cKakariko, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(38), SpoilerCollectionCheckGroup::GROUP_KAKARIKO); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 43be8b854..d708770e1 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -6303,26 +6303,24 @@ void Interface_Update(PlayState* play) { gSaveContext.rupees--; Audio_PlaySoundGeneral(NA_SE_SY_RUPY_COUNT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } - if (gSaveContext.rupeeAccumulator == 0) { - if (gSaveContext.pendingSale != ITEM_NONE) { - u16 tempSaleItem = gSaveContext.pendingSale; - u16 tempSaleMod = gSaveContext.pendingSaleMod; - gSaveContext.pendingSale = ITEM_NONE; - gSaveContext.pendingSaleMod = MOD_NONE; - if (tempSaleMod == MOD_NONE) { - s16 giid = GetGIID(tempSaleItem); - if (giid == -1) { - tempSaleMod = MOD_RANDOMIZER; - } else { - tempSaleItem = giid; - } - } - GameInteractor_ExecuteOnSaleEndHooks(ItemTable_RetrieveEntry(tempSaleMod, tempSaleItem)); - } - } } else { gSaveContext.rupeeAccumulator = 0; } + if (gSaveContext.rupeeAccumulator == 0 && gSaveContext.pendingSale != ITEM_NONE) { + u16 tempSaleItem = gSaveContext.pendingSale; + u16 tempSaleMod = gSaveContext.pendingSaleMod; + gSaveContext.pendingSale = ITEM_NONE; + gSaveContext.pendingSaleMod = MOD_NONE; + if (tempSaleMod == MOD_NONE) { + s16 giid = GetGIID(tempSaleItem); + if (giid == -1) { + tempSaleMod = MOD_RANDOMIZER; + } else { + tempSaleItem = giid; + } + } + GameInteractor_ExecuteOnSaleEndHooks(ItemTable_RetrieveEntry(tempSaleMod, tempSaleItem)); + } } switch (interfaceCtx->unk_1EC) { From 855e7442ea0eb807ffb777b1be218bef10163144 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 23 Jul 2023 18:13:51 -0400 Subject: [PATCH 10/24] fix granny not checking for bottle properly (#3068) --- .../Enhancements/randomizer/randomizer.cpp | 5 ++- soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c | 31 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 08b213725..4a9ebb487 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3973,7 +3973,10 @@ void RandomizerSettingsWindow::DrawElement() { "A Giant's Knife and a pack of Bombchus will be added to the item pool, and " "one of the bottles will contain a Blue Potion.\n\n" "On (no hints) - Salesmen will be included but won't tell you what you'll get.\n" - "On (with hints) - Salesmen will be included and you'll know what you're buying." + "On (with hints) - Salesmen will be included and you'll know what you're buying.\n" + "\n" + "Granny's item will only be offered after you have traded in the Odd Mushroom when Shuffle Adult Trade is on. " + "Otherwise when off, you will need to have found the Claim Check to buy her item (simulating the trade quest is complete)." ); UIWidgets::EnhancementCombobox("gRandomizeShuffleMerchants", randoShuffleMerchants, RO_SHUFFLE_MERCHANTS_OFF); diff --git a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c index e8b1cf673..24ada35b5 100644 --- a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c +++ b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c @@ -174,11 +174,20 @@ void EnDs_OfferOddPotion(EnDs* this, PlayState* play) { } } +u8 EnDs_RandoCanGetGrannyItem() { + return gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && + !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) && + // Traded odd mushroom when adult trade is on + ((Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) && (gSaveContext.itemGetInf[3] & 1)) || + // Found claim check when adult trade is off + (!Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) && + INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK)); +} + s32 EnDs_CheckRupeesAndBottle() { if (gSaveContext.rupees < 100) { return 0; - } else if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && - !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) { + } else if (EnDs_RandoCanGetGrannyItem()) { // Allow buying the rando item regardless of having a bottle return 2; } else if (Inventory_HasEmptyBottle() == 0) { return 1; @@ -189,18 +198,14 @@ s32 EnDs_CheckRupeesAndBottle() { void EnDs_GiveBluePotion(EnDs* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { - if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && - (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) && - !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) { + if (EnDs_RandoCanGetGrannyItem()) { Flags_SetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP); } this->actor.parent = NULL; this->actionFunc = EnDs_Talk; } else { - if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && - (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) && - !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) { + if (EnDs_RandoCanGetGrannyItem()) { GetItemEntry entry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE); GiveItemEntryFromActor(&this->actor, play, entry, 10000.0f, 50.0f); } else { @@ -226,9 +231,7 @@ void EnDs_OfferBluePotion(EnDs* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; GetItemEntry itemEntry; - if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && - (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) && - !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) { + if (EnDs_RandoCanGetGrannyItem()) { itemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE); GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f); } else { @@ -258,8 +261,10 @@ void EnDs_Wait(EnDs* this, PlayState* play) { Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); player->actor.textId = 0x504A; this->actionFunc = EnDs_OfferOddPotion; - } else if ((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF) || - gSaveContext.itemGetInf[3] & 1) { + } else if ( + // Always offer blue potion when adult trade is off + (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF) || + gSaveContext.itemGetInf[3] & 1) { // Traded odd mushroom player->actor.textId = 0x500C; this->actionFunc = EnDs_OfferBluePotion; } else { From 63f61023d46619bfc8eff338b99ba4194032f3bb Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 6 Aug 2023 11:56:43 -0400 Subject: [PATCH 11/24] Fix: Carpet merchant rando item give distance requirement (#3102) --- soh/src/overlays/actors/ovl_En_Js/z_en_js.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c index 9b558f3fc..30ed425f4 100644 --- a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c +++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c @@ -136,7 +136,7 @@ void func_80A89160(EnJs* this, PlayState* play) { GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_WASTELAND_BOMBCHU_SALESMAN, GI_BOMBCHUS_10); gSaveContext.pendingSale = itemEntry.itemId; gSaveContext.pendingSaleMod = itemEntry.modIndex; - GiveItemEntryFromActor(&this->actor, play, itemEntry, 90.0f, 10.0f); + GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f); Flags_SetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN); } else { GetItemEntry itemEntry = ItemTable_Retrieve(GI_BOMBCHUS_10); From f68a4e930df6432f21b1c9f7d2c7e5649eae6e39 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 6 Aug 2023 09:01:19 -0700 Subject: [PATCH 12/24] [Bugfix] Add check for animation count to prevent continuing final saw textbox while putaway is happening (#3103) * Add check for animation count to prevent continuing final saw textbox while putaway is happening. * Added Fix toggle for it. Enabling Skip Text forces the behavior, regardless of previous selection for the Fix toggle itself (so it works if you have the Fix toggle enabled, or if you have Skip Text enabled). * Clarified the comment in `z_en_toryo`. --- soh/soh/SohMenuBar.cpp | 3 +++ soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 64e6a0c4a..76fef97ed 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -1007,6 +1007,9 @@ void DrawEnhancementsMenu() { ApplyAuthenticGfxPatches(); } UIWidgets::Tooltip("Fixes authentic out of bounds texture reads, instead loading textures with the correct size"); + UIWidgets::PaddedEnhancementCheckbox("Fix Poacher's Saw Softlock", "gFixSawSoftlock", true, false, CVarGetInteger("gSkipText", 0), + "This is disabled because it is forced on when Skip Text is enabled.", UIWidgets::CheckboxGraphics::Checkmark); + UIWidgets::Tooltip("Prevents the Poacher's Saw softlock from mashing through the text, or with Skip Text enabled."); ImGui::EndMenu(); } diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c index 289a4394a..b7f30f1a6 100644 --- a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c +++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c @@ -291,7 +291,10 @@ void func_80B20768(EnToryo* this, PlayState* play) { s16 sp32; s16 sp30; - if (this->unk_1E4 == 3) { + // Animation Count should be no more than 1 to guarantee putaway is complete after giving the saw + // As this is vanilla behavior, it only applies with the Fix toggle or Skip Text enabled. + bool checkAnim = (CVarGetInteger("gFixSawSoftlock", 0) != 0 || CVarGetInteger("gSkipText", 0) != 0) ? play->animationCtx.animationCount <= 1 : true; + if (this->unk_1E4 == 3 && checkAnim) { Actor_ProcessTalkRequest(&this->actor, play); Message_ContinueTextbox(play, this->actor.textId); this->unk_1E4 = 1; From f5ab1a9b5dc86ffd15b33008cfb26d8c46a8fa42 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 6 Aug 2023 12:05:34 -0400 Subject: [PATCH 13/24] fix: disable Idle camera re-center no longer stuck on level geometry (#3094) --- soh/src/code/z_camera.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index e0d827107..325eca828 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -1678,12 +1678,13 @@ s32 Camera_Normal1(Camera* camera) { Camera_ClampDist(camera, eyeAdjustment.r, norm1->distMin, norm1->distMax, anim->unk_28); if (anim->startSwingTimer <= 0) { + // idle camera re-center if (CVarGetInteger("gA11yDisableIdleCam", 0)) return; eyeAdjustment.pitch = atEyeNextGeo.pitch; eyeAdjustment.yaw = Camera_LERPCeilS(anim->swingYawTarget, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); } else if (anim->swing.unk_18 != 0) { - if (CVarGetInteger("gA11yDisableIdleCam", 0)) return; + // camera adjustments when obstructed/pushed by scene geometry eyeAdjustment.yaw = Camera_LERPCeilS(anim->swing.unk_16, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); eyeAdjustment.pitch = @@ -1708,19 +1709,19 @@ s32 Camera_Normal1(Camera* camera) { if ((camera->status == CAM_STAT_ACTIVE) && (!(norm1->interfaceFlags & 0x10))) { anim->swingYawTarget = BINANG_ROT180(camera->playerPosRot.rot.y); if (!CVarGetInteger("gFixCameraSwing", 0)) { - if (anim->startSwingTimer > 0) { - func_80046E20(camera, &eyeAdjustment, norm1->distMin, norm1->unk_0C, &sp98, &anim->swing); - } else { - sp88 = *eyeNext; - anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f; - if (Camera_BGCheck(camera, at, &sp88)) { - anim->swingYawTarget = atEyeNextGeo.yaw; - anim->startSwingTimer = -1; + if (anim->startSwingTimer > 0) { + func_80046E20(camera, &eyeAdjustment, norm1->distMin, norm1->unk_0C, &sp98, &anim->swing); } else { - *eye = *eyeNext; + sp88 = *eyeNext; + anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f; + if (Camera_BGCheck(camera, at, &sp88)) { + anim->swingYawTarget = atEyeNextGeo.yaw; + anim->startSwingTimer = -1; + } else { + *eye = *eyeNext; + } + anim->swing.unk_18 = 0; } - anim->swing.unk_18 = 0; - } } else { if (anim->startSwingTimer <= 0) { anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f; From 06548b3ceb24ec00a59e9199bdadf24ddf038fab Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 6 Aug 2023 17:11:14 +0100 Subject: [PATCH 14/24] Fix various randomiser text issues and related improvments. (#3066) * Reduce length of overflow prone item names * Overhaul Trick Names and change Desert Waterfall to Valley waterfall * Readd some old Trick names, Change Trick name table to a vector, improve french translations. * Resolve #2682 --- .../hint_list/hint_list_exclude_overworld.cpp | 2 +- .../3drando/hint_list/hint_list_item.cpp | 4 +- .../randomizer/3drando/item_list.cpp | 6 +- .../Enhancements/randomizer/3drando/shops.cpp | 252 ++++++++++++------ 4 files changed, 182 insertions(+), 82 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 721776418..954976031 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -483,7 +483,7 @@ void HintTable_Init_Exclude_Overworld() { hintTable[GV_WATERFALL_FREESTANDING_POH] = HintText::Exclude({ //obscure text - Text{"behind a #desert waterfall# is", /*french*/"#derrière la cascade du désert# se cache", /*spanish*/"tras una #desierta cascada# yace"}, + Text{"behind a #valley waterfall# is", /*french*/"#derrière la cascade du désert# se cache", /*spanish*/"tras una #desierta cascada# yace"}, }); hintTable[GV_CRATE_FREESTANDING_POH] = HintText::Exclude({ diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index c074ffbf2..9b9913c8c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -206,8 +206,8 @@ void HintTable_Init_Item() { hintTable[STONE_OF_AGONY] = HintText::Item({ //obscure text - Text{"the shake shard", /*french*/"le fragment vibrant", /*spanish*/"el fragmento tintineante"}, - Text{"a blue alarm", /*french*/"une alerte bleue", /*spanish*/"una azul alarma"}, + Text{"the shake stone", /*french*/"le fragment vibrant", /*spanish*/"el fragmento tintineante"}, + Text{"a gray alarm", /*french*/"une alerte bleue", /*spanish*/"una azul alarma"}, }, { //ambiguous text Text{"a prize of the House of Skulltulas", /*french*/"un prix de la maison des Skulltulas", /*spanish*/"un obsequio de la Casa Skulltula"}, diff --git a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp index 59d8518f3..dd7533b98 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp @@ -66,7 +66,7 @@ void ItemTable_Init() { // RandomizerGet //Progression Items itemTable[PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{"Progressive Hookshot", "Grappin (prog.)", "Gancho progresivo"}, ITEMTYPE_ITEM, 0x80, true, &ProgressiveHookshot, PROGRESSIVE_HOOKSHOT); - itemTable[PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{"Progressive Strength Upgrade", "Amélioration de Force (prog.)", "Fuerza progresiva"}, ITEMTYPE_ITEM, 0x81, true, &ProgressiveStrength, PROGRESSIVE_STRENGTH); + itemTable[PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{"Strength Upgrade", "Amélioration de Force (prog.)", "Fuerza progresiva"}, ITEMTYPE_ITEM, 0x81, true, &ProgressiveStrength, PROGRESSIVE_STRENGTH); itemTable[PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{"Progressive Bomb Bag", "Sac de Bombes (prog.)", "Saco de bombas progresivo"}, ITEMTYPE_ITEM, 0x82, true, &ProgressiveBombBag, PROGRESSIVE_BOMB_BAG); itemTable[PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{"Progressive Bow", "Arc (prog.)", "Arco progresivo"}, ITEMTYPE_ITEM, 0x83, true, &ProgressiveBow, PROGRESSIVE_BOW); itemTable[PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{"Progressive Slingshot", "Lance-Pierre (prog.)", "Resortera progresiva"}, ITEMTYPE_ITEM, 0x84, true, &ProgressiveBulletBag, PROGRESSIVE_SLINGSHOT); @@ -207,8 +207,8 @@ void ItemTable_Init() { // RandomizerGet itemTable[BLUE_POTION_REFILL] = Item(RG_BLUE_POTION_REFILL, Text{"Blue Potion Refill", "Recharge de Potion Bleue", "Recarga de poción azul"}, ITEMTYPE_REFILL, GI_POTION_BLUE, false, &noVariable, NONE); //Treasure Game - itemTable[TREASURE_GAME_HEART] = Item(RG_TREASURE_GAME_HEART, Text{"Piece of Heart (Treasure Chest Minigame)", "Quart de Coeur (Chasse-aux-Trésors)", "Pieza de corazón (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_HEART_PIECE_WIN, true, &PieceOfHeart, TREASURE_GAME_HEART); - itemTable[TREASURE_GAME_GREEN_RUPEE] = Item(RG_TREASURE_GAME_GREEN_RUPEE, Text{"Green Rupee (Treasure Chest Minigame)", "Rubis Vert (Chasse-aux-Trésors)", "Rupia Verde (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_RUPEE_GREEN_LOSE, false, &noVariable, TREASURE_GAME_GREEN_RUPEE); + itemTable[TREASURE_GAME_HEART] = Item(RG_TREASURE_GAME_HEART, Text{"Piece of Heart (WINNER)", "Quart de Coeur (Chasse-aux-Trésors)", "Pieza de corazón (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_HEART_PIECE_WIN, true, &PieceOfHeart, TREASURE_GAME_HEART); + itemTable[TREASURE_GAME_GREEN_RUPEE] = Item(RG_TREASURE_GAME_GREEN_RUPEE, Text{"Green Rupee (LOSER)", "Rubis Vert (Chasse-aux-Trésors)", "Rupia Verde (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_RUPEE_GREEN_LOSE, false, &noVariable, TREASURE_GAME_GREEN_RUPEE); //Shop Items price itemTable[BUY_DEKU_NUT_5] = Item(RG_BUY_DEKU_NUT_5, Text{"Buy Deku Nut (5)", "Acheter: Noix Mojo (5)", "Comprar nueces deku (5)"}, ITEMTYPE_SHOP, 0x00, true, &Nuts, DEKU_NUTS_5, 15); diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 4049ee628..aa2e64a78 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -14,7 +14,7 @@ using namespace Settings; std::vector NonShopItems = {}; -static std::array, 0xD5> trickNameTable; //Table of trick names for ice traps +static std::array, 0xD5> trickNameTable; //Table of trick names for ice traps bool initTrickNames = false; //Indicates if trick ice trap names have been initialized yet //Set vanilla shop item locations before potentially shuffling @@ -225,18 +225,19 @@ void InitTrickNames() { trickNameTable[GI_SWORD_KOKIRI] = { Text{"Korok Sword", "Épée Korok", "Espada Korok"}, Text{"Hero's Sword", "Épée du Héros", "Espada del héroe"}, - Text{"Razor Sword", "Lame Rasoir", "Espada de esmeril"}}; + Text{"Butter Knife","Couteau à Beurre","cuchillo de mantequilla"}}; /* trickNameTable[GI_SWORD_MASTER] = { Text{"Goddess Sword", "Épée de la déesse", "Espada Divina"}, Text{"Gilded Sword", "Excalibur", "Espada de los Sabios"}, Text{"Magical Sword", "Lame dorée", "Fay"}};*/ trickNameTable[GI_SWORD_KNIFE] = { - Text{"Big Goron's Sword", "Épée de Gros Goron", "Espada de Big Goron"}, - Text{"Fierce Deity's Sword", "Épée du Dieu Démon", "Espada de la Fiera Deidad"}, - Text{"Biggoron's Knife", "Lame de Grogoron", "Daga de Biggoron"}}; + Text{"Medigoron's Sword", "l'Épée de Medigoron", "La espada de Medigoron"}, + Text{"Razor Sword", "Lame Rasoir", "Espada de esmeril"}, + Text{"Royal Claymore", "Claymore Royale", "Royal Claymore"}}; trickNameTable[GI_SWORD_BGS] = { - Text{"Big Goron's Sword", "Épée de Biggoron", "Espada de Big Goron"}, - Text{"Fierce Deity's Sword", "Épée du dieu démon", "Espada de la Fiera Deidad"}, + Text{"Power Sword", "Épée de Puissance", "Espada de poder"}, + Text{"Fierce Deity Sword", "Épée du dieu démon", "Espada de la Fiera Deidad"}, + Text{"Tempered Sword", "Épée de Légende Nv.2", "Espada Maestra mejorada"}, Text{"Biggoron's Knife", "Lame de Grogoron", "Daga de Biggoron"}}; trickNameTable[GI_SHIELD_DEKU] = { Text{"Boko Shield", "Bouclier Boko", "Escudo Boko"}, @@ -251,19 +252,23 @@ void InitTrickNames() { Text{"Magical Shield", "Bouclier Magique", "Escudo arcano"}, Text{"Mirror of Twilight", "Miroir des Ombres", "Espejo del Crepúsculo"}}; trickNameTable[GI_TUNIC_GORON] = { - Text{"Gerudo Tunic", "Tunique Gerudo", "Sayo gerudo"}, - Text{"Magic Armor", "Armure Magique", "Túnica Goron"}, + Text{"Gerudo Top", "Tunique Gerudo", "Pechera gerudo"}, + Text{"Flamebreaker Armor", "Armure de Pierre", " Armadura ignífuga"}, Text{"Red Mail", "Habits Rouges", "Ropas rojas"}}; trickNameTable[GI_TUNIC_ZORA] = { Text{"Rito Tunic", "Tunique Rito", "Sayo rito"}, + Text{"Mermaid Suit", "Costume de sirène", "Costume de sirène"}, Text{"Zora Armor", "Armure Zora", "Túnica Zora"}, Text{"Blue Mail", "Habits Bleus", "Ropas azules"}}; trickNameTable[GI_BOOTS_IRON] = { Text{"Iron Hoofs", "Patins de Plomb", "Botas férreas"}, Text{"Snow Boots", "Bottes de Neige", "Botas de nieve"}, + Text{"Red Boots", "Bottes rouges", "Botas rojas"}, + Text{"Zora Greaves", "Bottes Zora", "Zora Greaves"}, Text{"Boots of Power", "Bottes de Puissance", "Botas de plomo"}}; trickNameTable[GI_BOOTS_HOVER] = { Text{"Hover Hoofs", "Patins des airs", "Botas flotadoras"}, + Text{"Golden Boots", "Bottes dorées", "Botas de Oro"}, Text{"Pegasus Boots", "Bottes pégase", "Botas de Pegaso"}, Text{"Boots of Speed", "Bottes de vitesse", "Botas del desierto"}}; trickNameTable[GI_WEIRD_EGG] = { @@ -271,65 +276,81 @@ void InitTrickNames() { Text{"Lon Lon Egg", "Oeuf Lon Lon", "Huevo Lon Lon"}, Text{"Zora Egg", "Oeuf Zora", "Huevo Zora"}}; trickNameTable[GI_LETTER_ZELDA] = { - Text{"Ruto's Letter", "Lettre de Ruto", "Carta de Ruto"}, + Text{"Ruto's Letter", "Lettre de Ruto", "Carta de Ruto"}, Text{"Royal Letter", "Lettre Eoyale", "Carta para Kafei"}, - Text{"Zelda's Business Card", "Carte d'affaires de Zelda", "Carta"}}; + Text{"Zelda's Business Card", "Carte d'affaires de Zelda", "Carta"}, + Text{"Letter to Kafei", "Lettre pour Kafei", "Carta para Kafei "}, + Text{"Goat's Letter", "Lettre de la Chèvre", "Carta de la Cabra"}, + Text{"Maggie's Letter", "Lettre de Maggy", "Carta de Dolores"}}; trickNameTable[GI_BOOMERANG] = { + Text{"Banana", "Banane", "Plátano"}, Text{"Prank Fetch Toy", "Inséparable Bâtonnet", "Bumerang"}, Text{"Gale Boomerang", "Boomerang Tornade", "Bumerán tornado"}, Text{"Magic Boomerang", "Boomerang Magique", "Bumerán mágico"}}; trickNameTable[GI_LENS] = { Text{"Sheikah-leidoscope", "Sheikah-léidoscope", "Monóculo de la Verdad"}, Text{"Sheikah Sensor", "Sonar Sheikah", "Sensor Sheikah"}, + Text{"Crystal of Vision", "Cristal de Vision", "Cristal de Visión"}, Text{"Magnifying Lens", "Loupe", "Lente Aumentadora"}}; trickNameTable[GI_HAMMER] = { Text{"Goron Gavel", "Masse Perforatrice", "Mazo Goron"}, Text{"Magic Hammer", "Marteau Magique", "Martillo mágico"}, Text{"Skull Hammer", "Maillet Ressort", "Martillo de hierro"}}; trickNameTable[GI_STONE_OF_AGONY] = { - Text{"Shard of Agahnim", "Fragment d'Agahnim", "Piedra de Agahnim"}, + Text{"Cave Charm", "Charme de grotte", "Amuleto de la cueva"}, + Text{"Stone of Agahnim", "Fragment d'Agahnim", "Piedra de Agahnim"}, Text{"Shard of Agony", "Fragment de Souffrance", "Piedra de la Agonía"}, Text{"Pirate's Charm", "Pierre de Pirate", "Amuleto Pirata"}}; trickNameTable[GI_DINS_FIRE] = { Text{"Eldin's Fire", "Feu d'Eldin", "Fuego de Eldin"}, Text{"Din's Blaze", "Flamme de Din", "Poder de Din"}, - Text{"Din's Pearl", "Perle de Din", "Orbe de Din"}}; + Text{"Magic Lantern", "Lanterne Magique", "Linterna mágica"}, + Text{"Ether Medallion", "Médaillon d'Éther", "Medallón de Tesoro"}, + Text{"Bombos Medallion", "Médaillon des Flammes", "Medallón del Temblor"}}; trickNameTable[GI_FARORES_WIND] = { Text{"Faron's Wind", "Vent de Firone", "Viento de Farone"}, Text{"Farore's Windfall", "Zéphyr de Farore", "Valor de Farore"}, - Text{"Farore's Pearl", "Perle de Farore", "Orbe de Farore"}}; + Text{"Tingle Air", "Tingle Air", "Tingle de aire"}, + Text{"Travel Medallion", "Amulette de téléportation", "Medallón Maligno"}, + Text{"Irene's Taxi", "Le taxi d'Aëline", "El taxi de Airín"}}; trickNameTable[GI_NAYRUS_LOVE] = { Text{"Lanayru's Love", "Amour de Lanelle", "Amor de Lanayru"}, Text{"Nayru's Passion", "Passion de Nayru", "Sabiduría de Nayru"}, - Text{"Nayru's Pearl", "Perle de Nayru", "Orbe de Nayru"}}; + Text{"Tingle Shield", "Bouclier Tingle", "Escudo de hormigueo"}, + Text{"Shield Spell", "Bouclier Magique", "Hechizo de Protección"}, + Text{"Magic Armor", "Armure Magique", "Armadura mágica"}}; trickNameTable[GI_ARROW_FIRE] = { - Text{"Soul Arrow", "Flèche des Esprits", "Flecha del Espíritu"}, + Text{"Fire Rod", "Baguette de feu", "Cetro de fuego"}, Text{"Bomb Arrow", "Flèche-Bombe", "Flecha bomba"}, - Text{"Fire Candy", "Bonbon deFfeu", "Cetro de fuego"}}; + Text{"Red Candle", "Bougie Rouge", "Vela roja"}}; trickNameTable[GI_ARROW_ICE] = { - Text{"Shadow Arrow", "Flèche d'Ombre", "Flecha de las Sombras"}, + Text{"Ice Rod", "Baguette des Glaces", "Cetro de Hielo"}, Text{"Ancient Arrow", "Flèche Archéonique", "Flecha ancestral"}, Text{"Ice Trap Arrow", "Flèche de Piège de Glace", "Cetro de hielo"}}; trickNameTable[GI_ARROW_LIGHT] = { Text{"Wind Arrow", "Flèche de Vent", "Flecha del Viento"}, + Text{"Wand of Gamelon", "Baguette de Gamelon", "Varita de Gamelón"}, Text{"Shock Arrow", "Flèches Électriques", "Flecha eléctrica"}, Text{"Silver Arrow", "Flèches d'Argent", "Flecha de plata"}}; trickNameTable[GI_GERUDO_CARD] = { Text{"Desert Title Deed", "Abonnement Gerudo", "Escritura del desierto"}, + Text{"Sickle Moon Flag", "Drapeau du croissant de lune", "Bandera de la Luna Creciente"}, + Text{"Complimentary ID", "Bon de félicitation", "Cupón especial"}, Text{"Gerudo's Card", "Carte Goron", "Tóken Gerudo"}, Text{"Gerudo's Membership Card", "Autographe de Nabooru", "Tarjeta Gerudo"}}; trickNameTable[0xC9] = { Text{"Funky Bean Pack", "Paquet de Fèves Magiques", "Lote de frijoles mágicos"}, + Text{"Grapple Berries", "Baies de grappin", "Bayas de garfio"}, Text{"Crenel Bean Pack", "Paquet de Haricots Gonggle", "Lote de alubias mágicas"}, - Text{"Mystic Bean Pack", "Paquet de Haricots Mystiques", "Lote de porotos mágicos"}}; + Text{"Mystical Seed Pack", "Pack de graines mystiques", "Paquete de semillas místicas"}}; trickNameTable[0xB8] = { Text{"Diamond Hearts", "Coeurs de Diamant", "Contenedor de diamante"}, Text{"Double Damage", "Double Souffrance", "Doble daño receptivo"}, Text{"Quadruple Defence", "Quadruple Défence", "Defensa cuádruple"}}; trickNameTable[GI_POCKET_EGG] = { - Text{"Poached Egg", "oeuf à la coque", "Huevo pasado"}, + Text{"Arpagos Egg", "Oeuf d'Arpagos", "Huevo de Arpagos"}, Text{"Lon Lon Egg", "oeuf Lon Lon", "Huevo Lon Lon"}, Text{"Zora Egg", "oeuf Zora", "Huevo del Pez Viento"}}; trickNameTable[GI_POCKET_CUCCO] = { @@ -338,113 +359,161 @@ void InitTrickNames() { Text{"Hatched Cucco", "Cocotte éclose", "Pollo de bolsillo"}}; trickNameTable[GI_COJIRO] = { Text{"Blucco", "Chair-Qui-Poule", "Cucazul"}, - Text{"Grog's Cucco", "Cocotte de Grog", "Cuco de Grog"}, - Text{"Corijo", "Cojiro", "Corijo"}}; + Text{"Piyoko", "Piyoko", "Piyoko"}, + Text{"Dark Cucco", "Cocotte Sombre", "Cucco oscuro"}, + Text{"Grog's Cucco", "Cocotte de Grog", "Cuco de Grog"}}; trickNameTable[GI_ODD_MUSHROOM] = { Text{"Magic Mushroom", "Champignon magique", "Champiñón mágico"}, Text{"Endura Shroom", "Champi Vigueur", "Champiñón del bosque"}, + Text{"Sleepy Toadstool", "Crapaud Fatigué", "Seta durmiente"}, Text{"Mushroom", "Champignon", "Seta"}}; trickNameTable[GI_ODD_POTION] = { Text{"Odd Medicine", "Élixir suspect", "Poción rara"}, Text{"Granny's Poultice", "Mixture de Granny", "Medicina de la abuela"}, - Text{"Mushroom Poultice", "Mixture de champignon", "Medicina de champiñones"}}; + Text{"Mushroom Poultice", "Mixture de champignon", "Medicina de champiñones"}, + Text{"Secret Medicine", "Médicament", "Pócima secreta"}, + Text{"Mushroom Spores", "Spores de Champignons", "Esporas de hongos"}, + Text{"Hanyu Spore", "Hanyu Spore", "Espora Hanyu"}}; trickNameTable[GI_SAW] = { Text{"Carpenter's Saw", "Scie du charpentier", "Sierra del carpintero"}, Text{"Poacher's Sword", "Hache du chasseur", "Espada del capataz"}, + Text{"Ancient Bladesaw", "Longue Épée Archéonique", "Mandoble ancestral"}, + Text{"Woodcutter's Axe", "Hache du Bûcheron", "Hacha de leñador"}, Text{"Grog's Saw", "Scie de Grog", "Sierra del Cazador Furtivo"}}; trickNameTable[GI_SWORD_BROKEN] = { Text{"Broken Biggoron's Sword", "Épée brisée de Grogoron", "Espada de Biggoron rota"}, Text{"Broken Giant's Knife", "Lame des Géants brisée", "Daga gigante rota"}, - Text{"Biggoron's Sword", "Épée de Biggoron", "Espada de Biggoron"}}; + Text{"Broken Noble Sword", "Épée noble brisée", "Espada noble rota"}, + Text{"Broken Picori Blade", "Épée Minish brisée", "Espada minish rota"}, + Text{"Decayed Master Sword", "Épée de légende pourrie", "Espada decadente de leyenda"}}; trickNameTable[GI_PRESCRIPTION] = { Text{"Biggoron's Prescription", "Ordonnance de Grogoron", "Receta de Biggoron"}, Text{"Eyedrop Prescription", "Ordonnance de gouttes", "Receta ocular"}, - Text{"Urgent Prescription", "Ordonnance urgente", "Prescripción"}}; + Text{"Urgent Prescription", "Ordonnance urgente", "Prescripción"}, + Text{"Swordsman's Scroll", "Précis d'escrime", "Esgrimidorium"}, + Text{"Portrait of Oren", "Portrait d'Orlène", "Retrato de Oren"}, + Text{"Letter to King Zora", "Lettre au roi Zora", "Carta al Rey Zora"}}; trickNameTable[GI_FROG] = { Text{"Don Gero", "Don Gero", "Don Gero"}, - Text{"Eyedrop Frog", "Grenouille-qui-louche", "Globo Ocular de Rana"}, - Text{"Frog", "Crapaud", "Rana"}}; + Text{"Hot-Footed Frog", "Grenouille à pieds chauds", "Rana de patas calientes"}, + Text{"Lost Swordsmith", "Forgeron perdu", "Espadachín perdido"}, + Text{"Eyedrop Frog", "Grenouille-qui-louche", "Globo Ocular de Rana"}}; trickNameTable[GI_EYEDROPS] = { Text{"Biggoron's Eyedrops", "Gouttes de Grogoron", "Gotas de Biggoron"}, Text{"Hyrule's Finest Eyedrops", "Eau du Lac Hylia", "Gotas oculares"}, + Text{"Moon's Tear", "Larme de Lune", "Lágrima de Luna"}, + Text{"Engine Grease", "Graisse moteur", "Grasa del motor"}, Text{"Zora Perfume", "Parfum Zora", "Perfume Zora"}}; trickNameTable[GI_CLAIM_CHECK] = { Text{"Clay Check", "Certificat Grogoron", "Comprobante de Reclamación"}, + Text{"Ancient Tablet", "Stèle ancienne", "Litografía arcana"}, Text{"Sheikah Slate", "Tablette Sheikah", "Piedra Sheikah"}, Text{"Cyclone Slate", "Ardoise des tornades", "Pizarra de los Torbellinos"}}; trickNameTable[GI_SKULL_TOKEN] = { Text{"Skulltula Token", "Bon de Skulltula dorée", "Símbolo de Skulltula"}, Text{"Golden Skulltula Spirit", "Pièce de Skulltula dorée", "Tóken de Skulltula Dorada"}, - Text{"Gold Walltula Token", "Jeton de Walltula dorée", "Skulltula dorada"}}; + Text{"Gold Walltula Token", "Jeton de Walltula dorée", "Skulltula dorada"}, + Text{"Maiamai", "Ti'gorneau", "Maimai"}, + Text{"Gratitude Crystal", "Cristal de gratitude", "Gema de gratitud"}, + Text{"Korok Seed", "Noix korogu", "Semilla de kolog"}}; trickNameTable[0x80] = { Text{"Progressive Grappling Hook", "Lance-chaîne (prog.)", "Garra progresiva"}, Text{"Progressive Clawshot", "Grappin-griffe (prog.)", "Zarpa progresiva"}, - Text{"Progressive Gripshot", "Grappince (prog.)", "Enganchador progresivo"}}; + Text{"Progressive Gripshot", "Grappince (prog.)", "Enganchador progresivo"}, + Text{"Progressive Rope", "Corde (prog.)", "Cuerda progresivo"}}; trickNameTable[0x81] = { - Text{"Progressive Glove", "Gant de puissance (prog.)", "Guanteletes progresivos"}, - Text{"Progressive Power Bracelet", "Bracelet de force (prog.)", "Brasaletes progresivos"}, - Text{"Progressive Magic Bracelet", "Bracelet magique (prog.)", "Manoplas progresivas"}}; + Text{"Power Glove", "Gant de Puissance (prog.)", "Guanteletes progresivos"}, + Text{"Power Bracelet", "Bracelet de Force (prog.)", "Brasaletes progresivos"}, + Text{"Magic Bracelet", "Bracelet Magique (prog.)", "Manoplas progresivas"}}; trickNameTable[0x82] = { Text{"Progressive Bomb Capacity", "Capacité de bombes (prog.)", "Mayor capacidad de bombas"}, Text{"Progressive Bomb Pack", "Paquet de bombes (prog.)", "Zurrón de bombas progresivo"}, - Text{"Progressive Bomb Box", "Boîte à bombes (prog.)", "Bolsa de bombas progresiva"}}; + Text{"Progressive Bomb Box", "Boîte à bombes (prog.)", "Bolsa de bombas progresiva"}, + Text{"Progressive Blast Mask", "Masque d'Explosion (prog.)", "Máscara explosiva progresiva"}, + Text{"Progressive Powder Kegs", "Baril de Poudre (prog.)", "Barril de polvo progresivo"}, + Text{"Progressive Remote Bombs", "Bombes à distance (prog.)", "Bombas remotas progresivas"}}; trickNameTable[0x83] = { Text{"Progressive Arrow Capacity", "Capacité de flèches (prog.)", "Mayor capacidad de flechas"}, Text{"Progressive Hero's Bow", "Arc du héros (prog.)", "Arco del héroe progresivo"}, - Text{"Progressive Arrow Holder", "Arbalète (prog.)", "Ballesta progresiva"}}; + Text{"Progressive Arrow Holder", "Arbalète (prog.)", "Ballesta progresiva"}, + Text{"Progressive Crossbow", "Arbalète (prog.)", "Ballesta progresiva"}, + Text{"Progressive Sacred Bow", "Arc sacré (prog)", "Arco Sagrado Progresivo"}, + Text{"Progressive Lynel Bow", "Arc de Lynel (prog.)", "Arco de centaleón Progresivo"}}; trickNameTable[0x84] = { Text{"Progressive Seed Capacity", "Capacité de graines (prog.)", "Mayor capacidad de semillas"}, + Text{"Progressive Catapult", "Catapulte (prog.)", "Catapulta progresiva"}, Text{"Progressive Scattershot", "Lance-Pierre rafale (prog.)", "Resortera múltiple progresiva"}, + Text{"Progressive Seed Launcher", "Lanceur de semences (prog.)", "Lanzador de semillas progresivo"}, Text{"Progressive Seed Satchel", "Sac de graines (prog.)", "Bolsa de semillas progresiva"}}; trickNameTable[0x85] = { Text{"Progressive Rupee Capacity", "Capacité de rubis (prog.)", "Mayor capacidad de rupias"}, Text{"Progressive Purse", "Sacoche (prog.)", "Cartera de rupias progresiva"}, - Text{"Progressive Rupee Bag", "Sac à rubis (prog.)", "Zurrón de rupias progresivo"}}; + Text{"Progressive Rupee Bag", "Sac à rubis (prog.)", "Zurrón de rupias progresivo"}, + Text{"Progressive Rupoor Capacity", "Capacité de Roupir (prog.)", "Capacidad progresiva Rupobre"}, + Text{"Progressive Spoils Bag", "Sac à Butin (prog.)", "Bolsa de trofeos progresiva"}, + Text{"Progressive Ruby Bag", "Capacité du sac Ruby (prog.)", "Bolso Ruby progresivo"}}; trickNameTable[0x86] = { + Text{"Progressive Flippers", "Palmes de Zora (prog.)", "Aletas de zora progresiva"}, + Text{"Progressive Dragon's Scale", "Écaille du dragon d'eau (prog.)", "Escama dragón acuático progresiva"}, Text{"Progressive Diving Ability", "Plongée (prog.)", "Buceo progresivo"}, Text{"Progressive Pearl", "Perle (prog.)", "Perla progresiva"}, Text{"Progressive Scute", "Bulle (prog.)", "Fragmento Zora progresivo"}}; trickNameTable[0x87] = { Text{"Progressive Nut Pack", "Paquet de noix (prog.)", "Mayor capacidad de semillas"}, + Text{"Progressive Bait Bag", "Sac à Appâts (prog.)", "Bolsa de cebo progresiva"}, + Text{"Progressive Pear Capacity", "Capacité de poire (prog.)", "Capacidad progresiva de pera"}, Text{"Progressive Nut Bag", "Sac de noix (prog.)", "Bolsa de nueces progresiva"}, Text{"Progressive Husk Capacity", "Capacité de noisettes (prog.)", "Mayor capacidad de castañas"}}; trickNameTable[0x88] = { - Text{"Progressive Stick Pack", "Paquet de bâtons Mojo (prog.)", "Mayor capacidad de bastones"}, Text{"Progressive Stick Bag", "Sac de bâtons (prog.)", "Mayor capacidad de ramas deku"}, + Text{"Progressive Stick Pack", "Paquet de bâtons Mojo (prog.)", "Mayor capacidad de bastones"}, + Text{"Progressive Branch Capacity", "Capacité de la succursale (prog.)", "Capacidad progresiva de la sucursal"}, Text{"Progressive Rod Capacity", "Capacité de tiges (prog.)", "Mayor capacidad de cetros deku"}}; trickNameTable[0x89] = { Text{"Progressive Bomblings", "Bombinsectes (prog.)", "Bombinsectos progresivos"}, + Text{"Progressive Sentrobe Bombs", "Bombe de Sphérodrone (prog.)", "Bomba de helicobot progresivo"}, + Text{"Progressive Bomb-ombs", "Bombe Soldat (prog.)", "Soldado bomba progresivo"}, Text{"Progressive Missiles", "Missiles (prog.)", "Misiles progresivos"}, Text{"Progressive Bombchu Bag", "Sac à Bombchu (prog.)", "Bombachus progresivos"}}; trickNameTable[0x8A] = { Text{"Progressive Stamina Meter", "Jauge d'endurance (prog.)", "Medidor de vigor progresivo"}, - Text{"Progressive Energy Meter", "Jauge d'énergie (prog.)", "Medidor de energía progresivo"}, + Text{"Progressive Energy Gauge", "Jauge d'énergie (prog.)", "Medidor de energía progresivo"}, Text{"Progressive Magic Powder", "Poudre magique (prog.)", "Medidor de carga progresivo"}}; trickNameTable[0x8B] = { Text{"Progressive Memento", "Souvenir (prog.)", "Silbato progresivo"}, + Text{"Progressive Whistle", "Siffler (prog.)", "Silbido progresivo"}, Text{"Progressive Flute", "Flûte (prog.)", "Flauta progresiva"}, Text{"Progressive Recorder", "Harmonica (prog.)", "Armónica progresiva"}}; trickNameTable[0xD4] = { Text{"Progressive Titan Blade", "Lame des Titans (prog.)", "Hoja del Titán progresiva"}, Text{"Progressive Goron Knife", "Lame Goron (prog.)", "Daga Goron progresiva"}, - Text{"Progressive Giant Sword", "Épée géante (prog.)", "Espada gigante progresiva"}}; + Text{"Progressive Giant Sword", "Épée géante (prog.)", "Espada gigante progresiva"}, + Text{"Progressive Darknut Sword", "Épée de Darknut (prog.)", "Espada Darknut progresiva"}, + Text{"Progressive Power Sword", "Épée de Puissance (prog.)", "Espada de poder progresiva"}, + Text{"Progressive Big Stabby", "Gros coup de poignard (prog.)", "Gran puñalada progresiva"}}; trickNameTable[0x0F] = { + Text{"Empty Canteen", "Cantine vide", "cantimplora vacía"}, + Text{"Vial of Winds", "Fiole de vents", "Vial de Vientos"}, + Text{"Tingle Bottle", "Flacon de Tingle", "Botella de Tingle"}, Text{"Magic Bottle", "Flacon magique", "Frasco feérico"}, Text{"Glass Bottle", "Flacon de verre", "Botella de cristal"}, Text{"Bottle with Water", "Flacon d'eau", "Botella Tingle"}}; trickNameTable[0x14] = { Text{"Bottle with Chateau Romani", "Flacon de cuvée Romani", "Botella de Reserva Romani"}, - Text{"Bottle with Fresh Milk", "Flacon de lait frais", "Botella de leche fresca"}, - Text{"Bottle with Mystery Milk", "Flacon de lait grand cru", "Botella de leche extra"}}; + Text{"Bottle with Premium Milk", "Flacon avec lait de qualité supérieure", "Biberón con leche Premium"}, + Text{"Bottle with Mystery Milk", "Flacon de lait grand cru", "Botella de leche extra"}, + Text{"Bottle with Fresh Milk", "Flacon de lait frais", "Botella de leche fresca"},}; trickNameTable[0x8C] = { Text{"Bottle with Red Chu Jelly", "Flacon de gelée Chuchu rouge", "Jugo de Chuchu Rojo"}, + Text{"Bottle with Hibiscus Potion", "Flacon de potion de Hibiscus", "Botella de poción de Hibisco"}, Text{"Bottle with Medicine of Life", "Flacon d'élixir rouge", "Botella de medicina de la vida"}, Text{"Bottle with Heart Potion", "Flacon de potion de soin", "Botella de poción de salud"}}; trickNameTable[0x8D] = { Text{"Bottle with Green Chu Jelly", "Flacon de gelée Chuchu verte", "Jugo de Chuchu Verde"}, + Text{"Bottle with Lamp Oil", "Flacon de Huile à lanterne", "Botella de Aceite de candil "}, Text{"Bottle with Medicine of Magic", "Flacon d'élixir vert", "Botella de medicina mágica"}, Text{"Bottle with Stamina Potion", "Flacon d'Endurol", "Botella de elixir vigorizante"}}; trickNameTable[0x8E] = { @@ -453,22 +522,28 @@ void InitTrickNames() { Text{"Bottle with Air Potion", "Flacon de potion d'oxygène", "Botella de oxígeno"}}; trickNameTable[0x8F] = { Text{"Bottle with Forest Firefly", "Flacon avec une luciole", "Luciérnaga del bosque"}, - Text{"Bottle with Faerie", "Flacon de poudre féérique", "Gran Hada embotellada"}, + Text{"Bottle with Deku Princess", "Flacon avec Deku Princess", "Botella con Deku Princess"}, Text{"Bottle with Stray Fairy", "Flacon avec une fée perdue", "Hada perdida en una botella"}}; trickNameTable[0x90] = { Text{"Bottle with Small Jabu-Jabu", "Flacon avec mini Jabu-Jabu", "Lord Chapu-Chapu embotellado"}, + Text{"Bottle with Reekfish", "Flacon avec Reekfish", "Reekfish embotellada"}, Text{"Bottle with Hyrule Bass", "Flacon avec perche d'Hyrule", "Locha de Hyrule embotellada"}, Text{"Bottle with Hyrule Loach", "Flacon avec loche d'Hyrule", "Perca de Términa embotellada"}}; trickNameTable[0x91] = { Text{"Bottle with Will-O-Wisp", "Flacon avec feu follet", "Botella de llama azul"}, Text{"Bottle with Ancient Flame", "Flacon de flamme ancienne", "Botella de fuego ancestral"}, + Text{"Bottle with a Blue Candle", "Flacon avec une bougie bleue", "Botella con una vela azul"}, + Text{"Bottle with Red Ice", "Flacon de Glace Rouge", "Botella de Hielo rojo"}, Text{"Bottle with Nayru's Flame", "Flacon de flamme de Nayru", "Botella de llamas de Nayru"}}; trickNameTable[0x92] = { Text{"Bottle with Baby Tektites", "Flacon de bébé Araknon", "Tektites en una botella"}, + Text{"Bottle with A Beetle", "Flacon avec un scarabée", "Botella con un escarabajo"}, Text{"Bottle with Lanayru Ants", "Flacon de fourmis de Lanelle", "Celestarabajo embotellado"}, - Text{"Bottle with Insects", "Flacon de bibittes", "Saltabosques embotellados"}}; + Text{"Bottle with Insects", "Flacon de bibittes", "Saltabosques embotellados"}, + Text{"Bottle with a Golden Bee", "Flacon avec une abeille dorée", "Botella con una abeja dorada"}}; trickNameTable[0x94] = { Text{"Bottle with Ghini", "Flacon avec Ghini", "Ghini en una botella"}, + Text{"Bottle with Reapling", "Flacon avec Âme Damnée", "Reapling en una botella"}, Text{"Bottle with Imp Poe", "Flacon avec Spectre", "Espectro en una botella"}, Text{"Bottle with Anti-Fairy", "Flacon avec Tetdoss", "Whisp en una botella"}}; @@ -484,121 +559,146 @@ void InitTrickNames() { trickNameTable[0xC1] = { Text{"Ballad of the Goddess", "Chant de la déesse", "Cántico de la Diosa"}, Text{"Song of Healing", "Chant de l'apaisement", "Canción de curación"}, - Text{"Bolero of Fire", "Boléro du feu", "Bolero del fuego"}}; + Text{"Song of the Hero", "Chant du héros", "Canción del héroe"}}; trickNameTable[0xC2] = { - Text{"Earth God's Lyric", "Hymne du dieu de la terre", "Melodía del Espíritu de la Tierra"}, + Text{"Song of Birds","Chant des oiseaux","Cantar del ave"}, Text{"Song of Soaring", "Chant de l'envol", "Canción del viento"}, - Text{"Requiem of Spirit", "Requiem des esprits", "Réquiem del espíritu"}}; + Text{"Song of Horse", "Chant du cheval", "Chant du cheval"}}; trickNameTable[0xC3] = { - Text{"Wind God's Aria", "Hymne du dieu du vent", "Melodía del Espíritu del Viento"}, - Text{"Wind's Requiem", "Mélodie du vent", "Melodía del Viento"}, - Text{"Minuet of Forest", "Menuet de la forêt", "Minueto del bosque"}}; + Text{"Mido's Song", "La chanson de Mido", "La canción de Mido"}, + Text{"Kass' Theme", "Le thème de Kass", "El tema de Kass"}, + Text{"Tune of Echoes", "Chant des Échos ", "Melodía del Eco "}}; trickNameTable[0xC4] = { Text{"Song of Passing", "Mambo de Manbo", "Melodía del transcurrir"}, Text{"Command Melody", "Air du marionnettiste", "Cara al Sol"}, - Text{"Prelude of Light", "Prélude de la lumière", "Preludio de la luz"}}; + Text{"Moon's Song", "La chanson de Moon", "La canción de la luna"}}; trickNameTable[0xC5] = { Text{"Song of Double Time", "Chant accéléré", "Canción del doble tiempo"}, Text{"Inverted Song of Time", "Chant du temps inversé", "Canción del tiempo invertida"}, - Text{"Serenade of Water", "Sérénade de l'eau", "Serenata del agua"}}; + Text{"Tune of Ages", "Chant du Temps", "Melodía del Tiempo"}}; trickNameTable[0xC6] = { Text{"Ballad of Gales", "Requiem de la tornade", "Melodía del Tornado"}, Text{"Frog's Song of Soul", "Rap des grenouilles", "Canción del alma de la rana"}, - Text{"Nocturne of Shadow", "Nocturne de l'ombre", "Nocturno de la sombra"}}; + Text{"Wind's Requiem", "Mélodie du vent", "Melodía del Viento"}}; trickNameTable[0xBB] = { Text{"Saria's Karaoke", "Karaoké de Saria", "Dueto del bosque"}, Text{"Sonata of Awakening", "Sonate de l'éveil", "Sonata del despertar"}, - Text{"Saria's Song", "Chant de Saria", "Canción de Saria"}}; + Text{"Wind God's Aria", "Hymne du dieu du vent", "Melodía del Espíritu del Viento"}}; trickNameTable[0xBC] = { Text{"Darunia's Tango", "Tango de Darunia", "Coro del fuego"}, - Text{"Goron Lullaby", "Berceuse des Gorons", "Nana goron"}, - Text{"Zelda's Lullaby", "Berceuse de Zelda", "Nana de Zelda"}}; + Text{"Tune of Currents", "Chants des Flux", "Melodía de las Corrientes"}, + Text{"Goron Lullaby", "Berceuse des Gorons", "Nana goron"}}; trickNameTable[0xBD] = { Text{"Ruto's Blues", "Blues de Ruto", "Sonata del agua"}, Text{"New Wave Bossa Nova", "Bossa-nova des flots", "Bossanova de las olas"}, - Text{"Song of Time", "Chant du temps", "Canción del tiempo"}}; + Text{"Manbo's Mambo", "Mambo de Manbo", "Mambo de Manbo"}}; trickNameTable[0xBE] = { Text{"Nabooru's Reggae", "Reggae de Nabooru", "Reggae del espíritu"}, Text{"Elegy of Emptiness", "Hymne du vide", "Elegía al vacío"}, - Text{"Epona's Song", "Chant d'Épona", "Canción de Epona"}}; + Text{"Earth God's Lyric", "Hymne du dieu de la terre", "Melodía del Espíritu de la Tierra"}}; trickNameTable[0xBF] = { Text{"Impa's Death Metal", "Death métal d'Impa", "Diurno de la sombra"}, Text{"Oath to Order", "Ode de l'appel", "Oda al orden"}, - Text{"Song of Storms", "Chant des tempêtes", "Canción de la tormenta"}}; + Text{"Song of Discovery", "Chant des secrets", "Canto revelador"}}; trickNameTable[0xC0] = { Text{"Rauru's Sing-Along", "Chansonnette de Rauru", "Predulio de luz"}, Text{"Ballad of the Wind Fish", "Ballade sur Poisson-Rêve", "Balada del Piez Viento"}, - Text{"Sun's Song", "Chant du soleil", "Canción del Sol"}}; + Text{"Song of Light", "Chant de la lumière", "Sonidos de la luz"}}; trickNameTable[0xCB] = { Text{"Pendant of Courage", "Pendentif du courage", "Colgante del valor"}, + Text{"Farore's Pearl", "Perle de Farore", "Orbe de Farore"}, + Text{"Aquanine", "Smaragdine", "Yerbánida"}, Text{"Farore's Emerald", "Émeraude de Farore", "Esmeralda de Farore"}, Text{"Kokiri's Peridot", "Péridot Kokiri", "Ágata de los Kokiri"}}; trickNameTable[0xCC] = { Text{"Pendant of Power", "Pendentif de la force", "Colgante del poder"}, + Text{"Din's Pearl", "Perle de Din", "Orbe de Din"}, + Text{"Crimsonine", "Alzanine", "Bermellina"}, Text{"Din's Ruby", "Rubis de Din", "Rubí de Din"}, Text{"Goron's Garnet", "Grenat Goron", "Topacio de los Goron"}}; trickNameTable[0xCD] = { Text{"Pendant of Wisdom", "Pendentif de la sagesse", "Colgante de la sabiduría"}, + Text{"Nayru's Pearl", "Perle de Nayru", "Orbe de Nayru"}, + Text{"Azurine", "Aquanine", "Azurina"}, Text{"Nayru's Sapphire", "Saphir de Nayru", "Zafiro de Nayru"}, Text{"Zora's Aquamarine", "Aquamarine Zora", "Lapislázuli de los Zora"}}; trickNameTable[0xCE] = { Text{"Wind Medallion", "Médaillon du vent", "Medallón del Viento"}, + Text{"Wind Element", "Elément Vent", "Elemento de aire"}, Text{"Saria's Medallion", "Médaillon de Saria", "Medallón de Saria"}, + Text{"Sign of Air", "Glyphe de l'air", "Glifo de aire"}, Text{"Medallion of Forest", "Médaillon du Temple de la Forêt", "Medalla del Bosque"}}; trickNameTable[0xCF] = { - Text{"Bombos Medallion", "Médaillon des flammes", "Medallón del Temblor"}, + Text{"Fire Element", "Elément Feu", "Elemento de fuego"}, Text{"Darunia's Medallion", "Médaillon de Darunia", "Medallón de Darunia"}, + Text{"Sign of Fire", "Glyphe de feu", "Glifo de fuego"}, Text{"Medallion of Fire", "Médaillon du Temple du Feu", "Medalla del Fuego"}}; trickNameTable[0xD0] = { + Text{"Water Element", "Elément Eau", "Elemento de agua"}, Text{"Ice Medallion", "Médaillon de glace", "Medallón Helado"}, Text{"Ruto's Medallion", "Médaillon de Ruto", "Medallón de Ruto"}, + Text{"Sign of Water", "Glyphe de l'eau", "Glifo de agua"}, Text{"Medallion of Water", "Médaillon du Temple de l'Eau", "Medalla del Agua"}}; trickNameTable[0xD1] = { - Text{"Quake Medallion", "Médaillon des secousses", "Medallón Llamarada"}, + Text{"Earth Element", "Elément Terre", "Elemento de tierra"}, Text{"Nabooru's Medallion", "Médaillon de Nabooru", "Medallón de Nabooru"}, + Text{"Sign of Earth", "Glyphe de la Terre", "Glifo de la tierra"}, Text{"Medallion of Spirit", "Médaillon du Temple de l'Esprit", "Medalla del Espíritu"}}; trickNameTable[0xD2] = { - Text{"Travel Medallion", "Amulette de téléportation", "Medallón Maligno"}, + Text{"Fused Shadow", "Cristal d'ombre", "Sombra Fundida"}, Text{"Impa's Medallion", "Médaillon d'Impa", "Medallón de Impa"}, + Text{"Sign of Illusion", "Glyphe de l'illusion", "Glifo de ilusión"}, Text{"Medallion of Shadow", "Médaillon du Temple de l'Ombre", "Medalla de la Sombra"}}; trickNameTable[0xD3] = { - Text{"Ether Medallion", "Médaillon d'éther", "Medallón de Tesoro"}, + Text{"Compass of Light", "Boussole de lumière", "Brújula de Luz"}, Text{"Rauru's Medallion", "Médaillon de Rauru", "Medallón de Rauru"}, + Text{"Sign of Destiny", "Glyphe du destin", "Glifo del destino"}, Text{"Medallion of Light", "Médaillon du temple de lumière", "Medalla de la Luz"}}; trickNameTable[GI_HEART] = { Text{"Love", "Bisou", "Te amo"}, - Text{"Heart Container", "Réceptacle de coeur", "Contenedor de corazón"}, - Text{"Piece of Heart", "Quart de coeur", "Pieza de corazón"}}; + Text{"Life", "Vie", "vida"}, + Text{"HP", "VP", "VP"}}; trickNameTable[GI_RUPEE_GREEN] = { - Text{"Green Rupy", "Rupee vert", "Rubia verde"}, + Text{"False Greg", "Faux Greg", "Falso Greg"}, + Text{"One Ruby", "Un rubis", "Un rubí"}, + Text{"Rupoor (1)", "Roupir (1)", "Rupobre (1)"}, Text{"One Rupee", "Un rubis", "Guaraní hyliano"}, Text{"Rupee (1)", "Rubis (1)", "Peso hyliano"}}; trickNameTable[GI_RUPEE_BLUE] = { - Text{"Blue Rupy", "Rupee bleu", "Rubia azul"}, + Text{"Blupee", "Bleubi", "Azupia"}, + Text{"Five Rubies", "Cinq Rubys", "Cinco rubíes"}, Text{"Five Rupees", "Cinq rubis", "Bolívar hyliano"}, - Text{"Rupee (5)", "Rubis (5)", "Peso hyliano"}}; + Text{"Rupee (5)", "Rubis (5)", "Peso hyliano"}, + Text{"Rupoor (5)", "Roupir (5)", "Rupobre (5)"}}; trickNameTable[GI_RUPEE_RED] = { - Text{"Red Rupy", "Rupee rouge", "Rubia roja"}, + Text{"Big 20", "Grand 20", "Los 20 grandes"}, + Text{"Twenty Rubies", "vingt rubis", "Veinte rubíes"}, + Text{"Rupoor (20)", "Roupir (20)", "Rupobre (20)"}, Text{"Twenty Rupees", "Vingt rubis", "Colon hyliano"}, Text{"Rupee (20)", "Rubis (20)", "Peso hyliano"}}; trickNameTable[GI_RUPEE_PURPLE] = { - Text{"Purple Rupy", "Rupee pourpre", "Rubia morada"}, + Text{"Purpee", "pourbi", "morupiua"}, + Text{"Fifty Rubies", "cinquante rubis", "Cincuenta rubíes"}, + Text{"Rupoor (50)", "Roupir (50)", "Rupobre (50)"}, Text{"Fifty Rupees", "Cinquante rubis", "Balboa hyliano"}, Text{"Rupee (50)", "Rubis (50)", "Peso hyliano"}}; trickNameTable[GI_RUPEE_GOLD] = { - Text{"Huge Rupy", "Énorme Rupee", "Rubia gigante"}, + Text{"Hugo", "Or Rubi", "Oro Rubi"}, + Text{"Two Hundred Rubies", "deux cents rubis", "Doscientos rubíes"}, + Text{"Diamond", "Diamant", "Diamante"}, + Text{"Huge Ruby", "Énorme rubis", "Rubi gigante"}, Text{"Two Hundred Rupees", "Deux cent rubis", "Euro hyliano"}, Text{"Rupee (200)", "Rubis (200)", "Dólar hyliano"}}; trickNameTable[GI_HEART_PIECE] = { - Text{"Piece of Health", "Quart d'énergie", "Pieza de amor"}, - Text{"Recovery Heart", "Coeur d'énergie", "Corazón"}, - Text{"Heart Container", "Réceptacle de coeur", "Contenedor de corazón"}}; + Text{"Pizza Heart", "Fromage de cœur", "Pieza de Chorizo"}, + Text{"Little Bit Of Love", "Un peu d'amour", "Un poco de amor"}, + Text{"Rare Peach Stone", "Pierre de pêche rare", "Pierre de pêche rare"}}; trickNameTable[GI_HEART_CONTAINER_2] = { - Text{"Health Container", "Réceptacle d'énergie", "Contenedor de amor"}, - Text{"Recovery Heart", "Quart de coeur", "Corazón"}, - Text{"Piece of Heart", "Coeur d'énergie", "Pieza de corazón"}}; + Text{"Crystal Heart", "Cœur de cristal", "Corazón de cristal"}, + Text{"Life Heart", "Cœur de vie", "Vida Corazón"}, + Text{"Lots of Love", "Beaucoup d'amour", "Mucho amor"}}; /* //Names for individual upgrades, in case progressive names are replaced From d22ca3cfc365f17874108367d345ffdb86d7c4f6 Mon Sep 17 00:00:00 2001 From: louist103 <35883445+louist103@users.noreply.github.com> Date: Fri, 11 Aug 2023 23:02:05 -0400 Subject: [PATCH 15/24] Rewrite the logic to check for CRC32 at runtime (#3072) * rewrite the logic to check for CRC32 at runtime * fix include for windows * fix pragmas * more clang fixes * MORE * Please apple * I hate this * MAC AGAIN * Clarify the ifdefs * ARM64 fixes --- soh/soh/Extractor/FastCrc32C.c | 68 +++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/soh/soh/Extractor/FastCrc32C.c b/soh/soh/Extractor/FastCrc32C.c index 5ec097193..d88b04beb 100644 --- a/soh/soh/Extractor/FastCrc32C.c +++ b/soh/soh/Extractor/FastCrc32C.c @@ -1,14 +1,28 @@ #include #include +// Force the compiler to assume we have support for the CRC32 intrinsic. We will check for our selves later. +// Clang will define both __llvm__ and __GNUC__ but GCC will only define __GNUC__. So we need to check for __llvm__ first. +#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__)))) +#pragma clang attribute push(__attribute__((target("crc32"))), apply_to = function) +#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)))) +// GCC Only lets you enable all of sse4.2 so we will for just this file and reset it at the end. +#pragma GCC push_options +#pragma GCC target("sse4.2") +#endif + +// Include headers for the CRC32 intrinsic and cpuid instruction on windows. No need to do any other checks because it assumes the target will support CRC32 #ifdef _WIN32 #include -#elif ((defined(__GNUC__) && defined(__x86_64__) || defined(__i386__)) && defined(__SSE4_2__)) +#include +// Same as above but these platforms use slightly different headers +#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)))) #include +#include #elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) // Nothing cause its a compiler builtin #else -#define USE_CRC_TABLE +#define NO_CRC_INTRIN #endif #if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) @@ -16,14 +30,13 @@ #define INTRIN_CRC32_32(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value)) #define INTRIN_CRC32_16(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value)) #define INTRIN_CRC32_8(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value)) -#elif defined(__SSE4_2__) || defined(_MSC_VER) +#elif defined(__GNUC__) || defined(_MSC_VER) #define INTRIN_CRC32_64(crc, data) crc = _mm_crc32_u64(crc, data) #define INTRIN_CRC32_32(crc, data) crc = _mm_crc32_u32(crc, data) #define INTRIN_CRC32_16(crc, data) crc = _mm_crc32_u16(crc, data) #define INTRIN_CRC32_8(crc, data) crc = _mm_crc32_u8(crc, data) #endif -#ifdef USE_CRC_TABLE static const uint32_t crc32Table[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, @@ -55,17 +68,13 @@ static const uint32_t crc32Table[256] = { 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L }; -#endif +// On platforms that we know will never support a crc32 instruction (such as the WiiU) we will skip compiling this function in. +#ifndef NO_CRC_INTRIN -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef USE_CRC_TABLE -uint32_t CRC32C(unsigned char* data, size_t dataSize) { +static uint32_t CRC32IntrinImpl(unsigned char* data, size_t dataSize) { uint32_t ret = 0xFFFFFFFF; int64_t sizeSigned = dataSize; - +// Only 64bit platforms support doing a CRC32 operation on a 64bit value #if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) while ((sizeSigned -= sizeof(uint64_t)) >= 0) { INTRIN_CRC32_64(ret, *(uint64_t*)data); @@ -77,6 +86,7 @@ uint32_t CRC32C(unsigned char* data, size_t dataSize) { data += sizeof(uint32_t); } +// On 32 bit we can only do 32bit operations #elif defined(_M_IX86) || defined(__i386__) while ((sizeSigned -= sizeof(uint32_t)) >= 0) { INTRIN_CRC32_32(ret, *(uint32_t*)data); @@ -94,17 +104,41 @@ uint32_t CRC32C(unsigned char* data, size_t dataSize) { return ~ret; } -#else -uint32_t CRC32C(const void* buf, size_t size) { - const uint8_t* p = buf; +#endif + +static uint32_t CRC32TableImpl(unsigned char* data, size_t dataSize) { + const uint8_t* p = data; uint32_t crc = 0xFFFFFFFF; - while (size--) + while (dataSize--) crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); return ~crc; } + +uint32_t CRC32C(unsigned char* data, size_t dataSize) { +#ifndef NO_CRC_INTRIN + // Test to make sure the CPU supports the CRC32 intrinsic + unsigned int cpuidData[4]; +#ifdef _WIN32 + __cpuid(cpuidData, 1); +#elif __APPLE__ || (defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)) +// Every Mac that supports SoH should support this instruction. Also check for ARM64 at the same time + return CRC32IntrinImpl(data, dataSize); +#else + __get_cpuid(1, &cpuidData[0], &cpuidData[1], &cpuidData[2], &cpuidData[3]); #endif -#ifdef __cplusplus + + if (cpuidData[2] & (1 << 20)) { // bit_SSE4_2 + return CRC32IntrinImpl(data, dataSize); + } +#endif // NO_CRC_INTRIN + return CRC32TableImpl(data, dataSize); } + +#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__)))) +#pragma clang attribute pop +#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)))) +#pragma GCC pop_options +#else #endif From 325c7fe36515c091482d417999efd02a4201dd8b Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 11 Aug 2023 23:05:26 -0400 Subject: [PATCH 16/24] fix dark links input in mirror mode (#3109) --- soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index d7bd610a2..120a924ff 100644 --- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -591,6 +591,10 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) { input->prev.button = input->cur.button & (u16) ~(BTN_A | BTN_B); PadUtils_UpdateRelXY(input); + if (CVarGetInteger("gMirroredWorld", 0)) { + input->rel.stick_x *= -1; + } + input->press.stick_x += (s8)(input->cur.stick_x - input->prev.stick_x); input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y); From b2e9d547baf8e13ffe123b937a30ee7c80c7ef6b Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 11 Aug 2023 23:05:54 -0400 Subject: [PATCH 17/24] fix anju rando item gives when performing an action (#3107) --- .../actors/ovl_En_Niw_Lady/z_en_niw_lady.c | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c index 50df2657a..ef6f7cbaa 100644 --- a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c +++ b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c @@ -401,8 +401,8 @@ void func_80ABA9B8(EnNiwLady* this, PlayState* play) { if (!gSaveContext.n64ddFlag) { func_8002F434(&this->actor, play, GI_POCKET_EGG, 200.0f, 100.0f); } else { - // TODO: get-item-rework Adult trade sequence this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_ANJU_AS_ADULT, GI_POCKET_EGG); + GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); gSaveContext.itemGetInf[2] |= 0x1000; } @@ -436,9 +436,9 @@ void func_80ABAB08(EnNiwLady* this, PlayState* play) { if (!gSaveContext.n64ddFlag) { func_8002F434(&this->actor, play, GI_COJIRO, 200.0f, 100.0f); } else { - // TODO: get-item-rework Adult trade sequence this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_TRADE_POCKET_CUCCO, GI_COJIRO); Randomizer_ConsumeAdultTradeItem(play, ITEM_POCKET_CUCCO); + GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); gSaveContext.itemGetInf[2] |= 0x4000; } this->actionFunc = func_80ABAC00; @@ -462,21 +462,17 @@ void func_80ABAC00(EnNiwLady* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { this->actionFunc = func_80ABAC84; } else { + if (gSaveContext.n64ddFlag) { + getItemId = this->getItemEntry.getItemId; + GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); + return; + } + getItemId = this->getItemId; if (LINK_IS_ADULT) { - if (!gSaveContext.n64ddFlag) { - getItemId = !(gSaveContext.itemGetInf[2] & 0x1000) ? GI_POCKET_EGG : GI_COJIRO; - } else { - // TODO: get-item-rework Adult trade sequence - getItemId = this->getItemEntry.getItemId; - GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); - // Skip setting item flags because that was done earlier - this->actionFunc = func_80ABA778; - } - } - if (this->getItemEntry.getItemId == GI_NONE) { - func_8002F434(&this->actor, play, getItemId, 200.0f, 100.0f); + getItemId = !(gSaveContext.itemGetInf[2] & 0x1000) ? GI_POCKET_EGG : GI_COJIRO; } + func_8002F434(&this->actor, play, getItemId, 200.0f, 100.0f); } } @@ -486,10 +482,13 @@ void func_80ABAC84(EnNiwLady* this, PlayState* play) { } osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (LINK_IS_ADULT) { - if (!(gSaveContext.itemGetInf[2] & 0x1000)) { - gSaveContext.itemGetInf[2] |= 0x1000; - } else { - gSaveContext.itemGetInf[2] |= 0x4000; + // Flags for randomizer gives are set in the original message prompt choice handling + if (!gSaveContext.n64ddFlag) { + if (!(gSaveContext.itemGetInf[2] & 0x1000)) { + gSaveContext.itemGetInf[2] |= 0x1000; + } else { + gSaveContext.itemGetInf[2] |= 0x4000; + } } this->actionFunc = func_80ABA778; } else { From 535157ce00cc01bf2ef4f29cf38478d118308cb4 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 11 Aug 2023 23:06:38 -0400 Subject: [PATCH 18/24] Fix: Various Rando generation crashes (#3108) * fix rando gen crash when not enough remaining items to place * fix rando gen crash due to missing granny shop hint * add error sound if rando gen fails --- soh/soh/Enhancements/randomizer/3drando/fill.cpp | 8 ++++---- .../3drando/hint_list/hint_list_exclude_overworld.cpp | 5 +++++ .../overlays/gamestates/ovl_file_choose/z_file_choose.c | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index e50adcd52..8138339c8 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -548,13 +548,13 @@ static void CalculateWotH() { static void FastFill(std::vector items, std::vector locations, bool endOnItemsEmpty = false) { //Loop until locations are empty, or also end if items are empty and the parameters specify to end then while (!locations.empty() && (!endOnItemsEmpty || !items.empty())) { - uint32_t loc = RandomElement(locations, true); - Location(loc)->SetAsHintable(); - PlaceItemInLocation(loc, RandomElement(items, true)); - if (items.empty() && !endOnItemsEmpty) { items.push_back(GetJunkItem()); } + + uint32_t loc = RandomElement(locations, true); + Location(loc)->SetAsHintable(); + PlaceItemInLocation(loc, RandomElement(items, true)); } } diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 954976031..8b9f37f9e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -423,6 +423,11 @@ void HintTable_Init_Exclude_Overworld() { Text{"#Medigoron# sells", /*french*/"#Medigoron# vend", /*spanish*/"#Medigoron# vende"}, }); + hintTable[KAK_GRANNYS_SHOP] = HintText::Exclude({ + // obscure text + Text{"the #potion shop lady# sells", /*french*/"la #dame du magasin de potion# vend", /*spanish*/"la #señora de la tienda de pociones# vende" }, + }); + hintTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = HintText::Exclude({ //obscure text Text{"#imprisoned in a house# lies", /*french*/"#encagé dans une maison# gît", /*spanish*/"#en una casa entre rejas# yace"}, diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 5a97863c2..173b7e6c8 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -346,7 +346,11 @@ void FileChoose_UpdateRandomizer() { func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_HORSE, 0, 7, 1); return; } else if (CVarGetInteger("gRandoGenerating", 0) == 0 && generating) { - Audio_PlayFanfare(NA_BGM_HORSE_GOAL); + if (SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) { + Audio_PlayFanfare(NA_BGM_HORSE_GOAL); + } else { + func_80078884(NA_SE_SY_OCARINA_ERROR); + } func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1); generating = 0; return; From 05dde45a750461bc3f5ef6dc6e50783660716552 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Sun, 13 Aug 2023 08:40:44 -0700 Subject: [PATCH 19/24] Update AudioEditor.cpp (#3115) --- soh/soh/Enhancements/audio/AudioEditor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index fd24503bb..970685fb5 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -207,6 +207,10 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) { LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); UpdateCurrentBGM(defaultValue, type); } + + if (currentValue == value) { + ImGui::SetItemDefaultFocus(); + } } ImGui::EndCombo(); From 78790fe8aab4f33673a13e875a90240f9060248f Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 13 Aug 2023 11:41:04 -0400 Subject: [PATCH 20/24] [Accessibility] Tweak Pause menu TTS functions (#3098) * tweak kaleido tts * tts announce what items are assigned to buttons; announce page on open --- .../accessibility/texts/kaleidoscope_eng.json | 39 ++-- .../accessibility/texts/kaleidoscope_fra.json | 39 ++-- .../accessibility/texts/kaleidoscope_ger.json | 39 ++-- .../assets/accessibility/texts/misc_eng.json | 10 +- .../assets/accessibility/texts/misc_fra.json | 10 +- .../assets/accessibility/texts/misc_ger.json | 10 +- soh/soh/Enhancements/tts/tts.cpp | 219 +++++++++++++++--- 7 files changed, 291 insertions(+), 75 deletions(-) diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json index c1c472591..10b016b32 100644 --- a/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json @@ -1,24 +1,35 @@ { - "health": "health $0", - "magic": "magic $0", - "rupees": "rupees $0", - "0": "Deku Stick $0", - "1": "Deku Nut $0", - "2": "Bomb $0", - "3": "Fairy Bow $0", + "health": "Health - $0 Hearts", + "magic": "Magic - $0", + "rupees": "Rupees - $0", + "floor": "Floor $0", + "basement": "Basement $0", + "item_menu": "Select Item", + "map_menu": "Map - $0", + "quest_menu": "Quest Status", + "equip_menu": "Equipment", + "overworld": "Overworld", + "equipped": "$0 - Equipped", + "save_prompt": "Would you like to save?", + "game_saved": "Game saved", + "assigned_to": "Assigned to $0", + "0": "Deku Stick - $0", + "1": "Deku Nut - $0", + "2": "Bomb - $0", + "3": "Fairy Bow - $0", "4": "Fire Arrow", "5": "Din's Fire", - "6": "Fairy Slingshot $0", + "6": "Fairy Slingshot - $0", "7": "Fairy Ocarina", "8": "Ocarina of Time", - "9": "Bombchu $0", + "9": "Bombchu - $0", "10": "Hookshot", "11": "Longshot", "12": "Ice Arrow", "13": "Farore's Wind", "14": "Boomerang", "15": "Lens of Truth", - "16": "Magic Beans $0", + "16": "Magic Beans - $0", "17": "Megaton Hammer", "18": "Light Arrow", "19": "Nayru's Love", @@ -115,8 +126,8 @@ "110": "Zora Sapphire", "111": "Stone of Agony", "112": "Gerudo's Card", - "113": "Skulltula Token $0", - "114": "Heart Container $0", + "113": "Skulltula Token - $0", + "114": "Piece of Heart - $0", "115": "Piece of Heart", "116": "Boss Key", "117": "Compass", @@ -124,7 +135,7 @@ "119": "Small Key", "120": "MAGIC SMALL", "121": "MAGIC LARGE", - "122": "PIECE OF HEART 2", + "122": "Biggoron's Sword", "123": "INVALID 1", "124": "INVALID 2", "125": "INVALID 3", @@ -217,4 +228,4 @@ "311": "Lon Lon Ranch", "312": "Question Mark", "313": "Ganon's Castle" -} \ No newline at end of file +} diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json index 58898d70d..820eb4cbc 100644 --- a/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json @@ -1,24 +1,35 @@ { - "health": "vie $0", - "magic": "magie $0", - "rupees": "rubis $0", - "0": "Bâton Mojo $0", - "1": "Noix Mojo $0", - "2": "Bombes $0", - "3": "Arc des Fées $0", + "health": "Vie - $0 Coeurs", + "magic": "Magie - $0", + "rupees": "Rubis - $0", + "floor": "Étage $0", + "basement": "Sous-sol $0", + "item_menu": "Inventaire", + "map_menu": "Carte - $0", + "quest_menu": "Statut de la quête", + "equip_menu": "Equipment", + "overworld": "Surmonde", + "equipped": "$0 - Équipé", + "save_prompt": "Voulez-vous sauvegarder?", + "game_saved": "Jeu sauvegardé", + "assigned_to": "Assigné au $0", + "0": "Bâton Mojo - $0", + "1": "Noix Mojo - $0", + "2": "Bombes - $0", + "3": "Arc des Fées - $0", "4": "Flèche de Feu", "5": "Feu de Din", - "6": "Lance-Pierre des Fées $0", + "6": "Lance-Pierre des Fées - $0", "7": "Ocarina des Fées", "8": "Ocarina of Temps", - "9": "Missiles Teigneux $0", + "9": "Missiles Teigneux - $0", "10": "Grappin", "11": "Super Grappin", "12": "Flèche de Glace", "13": "Vent de Farore", "14": "Boomerang", "15": "Monocle de Vérité", - "16": "Haricot Magique $0", + "16": "Haricot Magique - $0", "17": "Masse des Titans", "18": "Flèche de Lumière", "19": "Amour de Nayru", @@ -115,8 +126,8 @@ "110": "Saphir Zora", "111": "Pierre de Souffrance", "112": "Carte Gerudo", - "113": "Skulltula d'or $0", - "114": "Coeur d'Énergie $0", + "113": "Skulltula d'or - $0", + "114": "Quart de Coeur - $0", "115": "Quart de Coeur", "116": "Clé d'or", "117": "Boussole", @@ -124,7 +135,7 @@ "119": "Petite Clé", "120": "PETITE BOUTEILLE DE MAGIE", "121": "GRANDE BOUTEILLE DE MAGIE", - "122": "QUART DE COEUR 2", + "122": "Épée de Biggoron", "123": "INVALIDE 1", "124": "INVALIDE 2", "125": "INVALIDE 3", @@ -217,4 +228,4 @@ "311": "Ranch Lon Lon", "312": "Point d'interrogation", "313": "Château de Ganon" -} \ No newline at end of file +} diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json index 460b32877..630d933fc 100644 --- a/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json @@ -1,24 +1,35 @@ { - "health": "Energie $0", - "magic": "Magie $0", - "rupees": "Rubine $0", - "0": "Deku-Stab $0", - "1": "Deku-Nuß $0", - "2": "Bombe $0", - "3": "Feen-Bogen $0", + "health": "Energie - $0 Herzen", + "magic": "Magie - $0", + "rupees": "Rubine - $0", + "floor": "Etage $0", + "basement": "Keller $0", + "item_menu": "Gegenstände", + "map_menu": "Karte - $0", + "quest_menu": "Quest Status", + "equip_menu": "Ausrüstung", + "overworld": "Überwelt", + "equipped": "$0 - Ausgerüstet", + "save_prompt": "Spielstand sichern?", + "game_saved": "Spielstand gesichert", + "assigned_to": "$0 zugeordnet", + "0": "Deku-Stab - $0", + "1": "Deku-Nuß - $0", + "2": "Bombe - $0", + "3": "Feen-Bogen - $0", "4": "Feuer-Pfeil", "5": "Dins Feuerinferno", - "6": "Feen-Schleuder $0", + "6": "Feen-Schleuder - $0", "7": "Feen-Okarina", "8": "Okarina der Zeit", - "9": "Krabbelmine $0", + "9": "Krabbelmine - $0", "10": "Fanghaken", "11": "Enterhaken", "12": "Eis-Pfeil", "13": "Farores Donnersturm", "14": "Bumerang", "15": "Auge der Wahrheit", - "16": "Wundererbsen $0", + "16": "Wundererbsen - $0", "17": "Stahlhammer", "18": "Licht-Pfeil", "19": "Nayrus Umarmung", @@ -115,8 +126,8 @@ "110": "Zora-Saphir", "111": "Stein des Wissens", "112": "Gerudo-Paß", - "113": "Skulltula-Symbol $0", - "114": "Herzcontainer $0", + "113": "Skulltula-Symbol - $0", + "114": "Herzteil - $0", "115": "Herzteil", "116": "Master-Schlüssel", "117": "Kompaß", @@ -124,7 +135,7 @@ "119": "Kleiner Schlüssel", "120": "MAGIE KLEIN", "121": "MAGIE GROß", - "122": "HERZTEIL 2", + "122": "Biggoron-Schwert", "123": "UNGÜLTIG 1", "124": "UNGÜLTIG 2", "125": "UNGÜLTIG 3", @@ -217,4 +228,4 @@ "311": "Lon Lon-Farm", "312": "Fragezeichen", "313": "Teufelsturm" -} \ No newline at end of file +} diff --git a/OTRExporter/assets/accessibility/texts/misc_eng.json b/OTRExporter/assets/accessibility/texts/misc_eng.json index db59f3deb..6bff3b328 100644 --- a/OTRExporter/assets/accessibility/texts/misc_eng.json +++ b/OTRExporter/assets/accessibility/texts/misc_eng.json @@ -14,5 +14,11 @@ "input_button_c_left": "C Left", "input_button_c_right": "C Right", "input_analog_stick": "the Analog Stick", - "input_d_pad": "the D-Pad" -} \ No newline at end of file + "input_d_pad": "the D-Pad", + "input_d_pad_up": "D-Pad Up", + "input_d_pad_down": "D-Pad Down", + "input_d_pad_left": "D-Pad Left", + "input_d_pad_right": "D-Pad Right", + "yes": "Yes", + "no": "No" +} diff --git a/OTRExporter/assets/accessibility/texts/misc_fra.json b/OTRExporter/assets/accessibility/texts/misc_fra.json index e37268b95..0d9073e50 100644 --- a/OTRExporter/assets/accessibility/texts/misc_fra.json +++ b/OTRExporter/assets/accessibility/texts/misc_fra.json @@ -14,5 +14,11 @@ "input_button_c_left": "C Gauche", "input_button_c_right": "C Droit", "input_analog_stick": "le Stick Analogique", - "input_d_pad": "D-Pad" -} \ No newline at end of file + "input_d_pad": "D-Pad", + "input_d_pad_up": "D-Pad Haut", + "input_d_pad_down": "D-Pad Bas", + "input_d_pad_left": "D-Pad Gauche", + "input_d_pad_right": "D-Pad Droit", + "yes": "Oui", + "no": "Non" +} diff --git a/OTRExporter/assets/accessibility/texts/misc_ger.json b/OTRExporter/assets/accessibility/texts/misc_ger.json index 23b887861..2e07143f0 100644 --- a/OTRExporter/assets/accessibility/texts/misc_ger.json +++ b/OTRExporter/assets/accessibility/texts/misc_ger.json @@ -14,5 +14,11 @@ "input_button_c_left": "C Links", "input_button_c_right": "C Rechts", "input_analog_stick": "den Analog-Stick", - "input_d_pad": "das Steuerkreuz" -} \ No newline at end of file + "input_d_pad": "das Steuerkreuz", + "input_d_pad_up": "Steuerkreuz Oben", + "input_d_pad_down": "Steuerkreuz Unten", + "input_d_pad_left": "Steuerkreuz Links", + "input_d_pad_right": "Steuerkreuz Rechts", + "yes": "Ja", + "no": "Nein" +} diff --git a/soh/soh/Enhancements/tts/tts.cpp b/soh/soh/Enhancements/tts/tts.cpp index fceb9c84a..5a646e3a9 100644 --- a/soh/soh/Enhancements/tts/tts.cpp +++ b/soh/soh/Enhancements/tts/tts.cpp @@ -12,6 +12,7 @@ #include "soh/Enhancements/boss-rush/BossRush.h" extern "C" { +extern MapData* gMapData; extern SaveContext gSaveContext; extern PlayState* gPlayState; } @@ -190,28 +191,109 @@ void RegisterOnInterfaceUpdateHook() { void RegisterOnKaleidoscopeUpdateHook() { GameInteractor::Instance->RegisterGameHook([](int16_t inDungeonScene) { if (!CVarGetInteger("gA11yTTS", 0)) return; - - static uint16_t prevCursorIndex = 0; + + static int16_t prevCursorIndex = 0; static uint16_t prevCursorSpecialPos = 0; static uint16_t prevCursorPoint[5] = { 0 }; - + static int16_t prevPromptChoice = -1; + static int16_t prevSubState = -1; + static int16_t prevState = -1; + PauseContext* pauseCtx = &gPlayState->pauseCtx; Input* input = &gPlayState->state.input[0]; - - if (pauseCtx->state != 6) { - //reset cursor index to so it is announced when pause is reopened - prevCursorIndex = -1; + + // Save game prompt + if (pauseCtx->state == 7) { + if (pauseCtx->unk_1EC == 1) { + // prompt + if (prevPromptChoice != pauseCtx->promptChoice) { + auto prompt = GetParameritizedText(pauseCtx->promptChoice == 0 ? "yes" : "no", TEXT_BANK_MISC, nullptr); + if (prevPromptChoice == -1) { + auto translation = GetParameritizedText("save_prompt", TEXT_BANK_KALEIDO, nullptr); + SpeechSynthesizer::Instance->Speak((translation + " - " + prompt).c_str(), GetLanguageCode()); + } else { + SpeechSynthesizer::Instance->Speak(prompt.c_str(), GetLanguageCode()); + } + + prevPromptChoice = pauseCtx->promptChoice; + } + } else if (pauseCtx->unk_1EC == 4 && prevSubState != 4) { + // Saved + auto translation = GetParameritizedText("game_saved", TEXT_BANK_KALEIDO, nullptr); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + } + prevSubState = pauseCtx->unk_1EC; + prevState = pauseCtx->state; return; } - + + // Announce page when + // Kaleido pages are rotating and page halfway rotated + // Or Kaleido was just opened + if ((pauseCtx->unk_1E4 == 1 && pauseCtx->unk_1EA == 32) || (pauseCtx->state == 4 && prevState != 4)) { + uint16_t modeNextPageMap[] = { + PAUSE_MAP, PAUSE_EQUIP, PAUSE_QUEST, PAUSE_ITEM, PAUSE_EQUIP, PAUSE_MAP, PAUSE_ITEM, PAUSE_QUEST, + }; + uint16_t nextPage = modeNextPageMap[pauseCtx->mode]; + + switch (nextPage) { + case PAUSE_ITEM: { + auto translation = GetParameritizedText("item_menu", TEXT_BANK_KALEIDO, nullptr); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + break; + } + case PAUSE_MAP: { + std::string map; + if (inDungeonScene) { + std::string key = std::to_string(gSaveContext.mapIndex); + map = GetParameritizedText(key, TEXT_BANK_SCENES, nullptr); + } else { + map = GetParameritizedText("overworld", TEXT_BANK_KALEIDO, nullptr); + } + auto translation = GetParameritizedText("map_menu", TEXT_BANK_KALEIDO, map.c_str()); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + break; + } + case PAUSE_QUEST: { + auto translation = GetParameritizedText("quest_menu", TEXT_BANK_KALEIDO, nullptr); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + break; + } + case PAUSE_EQUIP: { + auto translation = GetParameritizedText("equip_menu", TEXT_BANK_KALEIDO, nullptr); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + break; + } + } + prevState = pauseCtx->state; + return; + } + + prevState = pauseCtx->state; + + if (pauseCtx->state != 6) { + // Reset cursor index and values so it is announced when pause is reopened + prevCursorIndex = -1; + prevPromptChoice = -1; + prevSubState = -1; + return; + } + if ((pauseCtx->debugState != 1) && (pauseCtx->debugState != 2)) { char arg[8]; if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { - snprintf(arg, sizeof(arg), "%d", gSaveContext.health); + // Normalize hearts to fractional count similar to z_lifemeter + int curHeartFraction = gSaveContext.health % 16; + int fullHearts = gSaveContext.health / 16; + float fraction = ceilf((float)curHeartFraction / 5) * 0.25; + float health = (float)fullHearts + fraction; + snprintf(arg, sizeof(arg), "%g", health); auto translation = GetParameritizedText("health", TEXT_BANK_KALEIDO, arg); SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); - } else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { - snprintf(arg, sizeof(arg), "%d", gSaveContext.magic); + } else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT) && gSaveContext.magicCapacity != 0) { + // Normalize magic to percentage + float magicLevel = ((float)gSaveContext.magic / gSaveContext.magicCapacity) * 100; + snprintf(arg, sizeof(arg), "%.0f%%", magicLevel); auto translation = GetParameritizedText("magic", TEXT_BANK_KALEIDO, arg); SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { @@ -235,6 +317,17 @@ void RegisterOnKaleidoscopeUpdateHook() { if (pauseCtx->cursorSpecialPos > 0) { return; } + + std::string buttonNames[] = { + "input_button_c_left", + "input_button_c_down", + "input_button_c_right", + "input_d_pad_up", + "input_d_pad_down", + "input_d_pad_left", + "input_d_pad_right", + }; + int8_t assignedTo = -1; switch (pauseCtx->pageIndex) { case PAUSE_ITEM: @@ -247,36 +340,71 @@ void RegisterOnKaleidoscopeUpdateHook() { case ITEM_BOMBCHU: case ITEM_SLINGSHOT: case ITEM_BOW: - snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM])); - break; case ITEM_BEAN: - snprintf(arg, sizeof(arg), "%d", 0); + snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM])); break; default: arg[0] = '\0'; } - - if (pauseCtx->cursorItem[PAUSE_ITEM] == 999) { + + if (pauseCtx->cursorItem[PAUSE_ITEM] == PAUSE_ITEM_NONE || + pauseCtx->cursorItem[PAUSE_ITEM] == ITEM_NONE) { + prevCursorIndex = -1; return; } - + std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_ITEM]); - auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg); - SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + std::string itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg); + + // Check if item is assigned to a button + for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) { + if (gSaveContext.equips.buttonItems[i + 1] == pauseCtx->cursorItem[PAUSE_ITEM]) { + assignedTo = i; + break; + } + } + + if (assignedTo != -1) { + auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr); + auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str()); + SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode()); + } else { + SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode()); + } break; } case PAUSE_MAP: if (inDungeonScene) { + // Dungeon map items if (pauseCtx->cursorItem[PAUSE_MAP] != PAUSE_ITEM_NONE) { std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_MAP]); auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr); SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + } else { + // Dungeon map floor numbers + char arg[8]; + int cursorPoint = pauseCtx->cursorPoint[PAUSE_MAP]; + + // Cursor is on a dungeon floor position + if (cursorPoint >= 3 && cursorPoint < 11) { + int floorID = gMapData->floorID[gPlayState->interfaceCtx.unk_25A][pauseCtx->dungeonMapSlot - 3]; + // Normalize so F1 == 0, and negative numbers are basement levels + int normalizedFloor = (floorID * -1) + 8; + if (normalizedFloor >= 0) { + snprintf(arg, sizeof(arg), "%d", normalizedFloor + 1); + auto translation = GetParameritizedText("floor", TEXT_BANK_KALEIDO, arg); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + } else { + snprintf(arg, sizeof(arg), "%d", normalizedFloor * -1); + auto translation = GetParameritizedText("basement", TEXT_BANK_KALEIDO, arg); + SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + } + } } } else { std::string key = std::to_string(0x0100 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]); auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr); SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); - SPDLOG_INFO("Item: {}", key); } break; case PAUSE_QUEST: @@ -287,16 +415,17 @@ void RegisterOnKaleidoscopeUpdateHook() { snprintf(arg, sizeof(arg), "%d", gSaveContext.inventory.gsTokens); break; case ITEM_HEART_CONTAINER: - snprintf(arg, sizeof(arg), "%d", ((gSaveContext.inventory.questItems & 0xF) & 0xF) >> 0x1C); + snprintf(arg, sizeof(arg), "%d", (gSaveContext.inventory.questItems & 0xF0000000) >> 0x1C); break; default: arg[0] = '\0'; } - - if (pauseCtx->cursorItem[PAUSE_QUEST] == 999) { + + if (pauseCtx->cursorItem[PAUSE_QUEST] == PAUSE_ITEM_NONE) { + prevCursorIndex = -1; return; } - + std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_QUEST]); auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg); SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); @@ -304,15 +433,51 @@ void RegisterOnKaleidoscopeUpdateHook() { } case PAUSE_EQUIP: { + if (pauseCtx->namedItem == PAUSE_ITEM_NONE) { + prevCursorIndex = -1; + return; + } + std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_EQUIP]); - auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr); - SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode()); + auto itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr); + uint8_t checkEquipItem = pauseCtx->namedItem; + + // BGS from kaleido reports as ITEM_HEART_PIECE_2 (122) + // remap BGS and broken knife to be the BGS item for the current equip check + if (checkEquipItem == ITEM_HEART_PIECE_2 || checkEquipItem == ITEM_SWORD_KNIFE) { + checkEquipItem = ITEM_SWORD_BGS; + } + + // Check if equipment item is currently equipped or assigned to a button + if (checkEquipItem >= ITEM_SWORD_KOKIRI && checkEquipItem <= ITEM_BOOTS_HOVER) { + uint8_t checkEquipType = (checkEquipItem - ITEM_SWORD_KOKIRI) / 3; + uint8_t checkEquipValue = ((checkEquipItem - ITEM_SWORD_KOKIRI) % 3) + 1; + + if (CUR_EQUIP_VALUE(checkEquipType) == checkEquipValue) { + itemTranslation = GetParameritizedText("equipped", TEXT_BANK_KALEIDO, itemTranslation.c_str()); + } + + for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) { + if (gSaveContext.equips.buttonItems[i + 1] == checkEquipItem) { + assignedTo = i; + break; + } + } + } + + if (assignedTo != -1) { + auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr); + auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str()); + SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode()); + } else { + SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode()); + } break; } default: break; } - + prevCursorIndex = cursorIndex; memcpy(prevCursorPoint, pauseCtx->cursorPoint, sizeof(prevCursorPoint)); }); From 91c6eba0d0cbd00c2dbb5278c2f26a7e76aa2585 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 13 Aug 2023 11:41:19 -0400 Subject: [PATCH 21/24] tweak: easy pause buffer to track inputs better (#3054) --- soh/soh/Enhancements/bootcommands.c | 2 ++ soh/src/code/padmgr.c | 13 +++++++------ soh/src/code/z_kaleido_setup.c | 15 ++++++++++++--- soh/src/code/z_play.c | 9 ++++----- .../misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c | 6 ++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/soh/soh/Enhancements/bootcommands.c b/soh/soh/Enhancements/bootcommands.c index 05a682101..385a544a6 100644 --- a/soh/soh/Enhancements/bootcommands.c +++ b/soh/soh/Enhancements/bootcommands.c @@ -26,6 +26,8 @@ void BootCommands_Init() CVarClear("gOnFileSelectNameEntry"); // Clear when soh is killed on the file name entry page CVarClear("gBetterDebugWarpScreenMQMode"); CVarClear("gBetterDebugWarpScreenMQModeScene"); + CVarClear("gCheatEasyPauseBufferLastInputs"); + CVarClear("gCheatEasyPauseBufferTimer"); #if defined(__SWITCH__) || defined(__WIIU__) CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u #endif diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c index 81227f11e..b8d8d8b05 100644 --- a/soh/src/code/padmgr.c +++ b/soh/src/code/padmgr.c @@ -287,6 +287,13 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) { Fault_AddHungupAndCrash(__FILE__, __LINE__); } + // When 3 frames are left on easy pause buffer, re-apply the last held inputs to the prev inputs + // to compute the pressed difference. This makes it so previously held inputs are continued as "held", + // but new inputs when unpausing are "pressed" out of the pause menu. + if (CVarGetInteger("gCheatEasyPauseBufferTimer", 0) == 3) { + input->prev.button = CVarGetInteger("gCheatEasyPauseBufferLastInputs", 0); + } + buttonDiff = input->prev.button ^ input->cur.button; input->press.button |= (u16)(buttonDiff & input->cur.button); input->rel.button |= (u16)(buttonDiff & input->prev.button); @@ -298,12 +305,6 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) { uint8_t rumble = (padMgr->rumbleEnable[0] > 0); OTRControllerCallback(rumble); - if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) { - ControllerBlockGameInput(PAUSE_BUFFER_INPUT_BLOCK_ID); - } else { - ControllerUnblockGameInput(PAUSE_BUFFER_INPUT_BLOCK_ID); - } - PadMgr_UnlockPadData(padMgr); } diff --git a/soh/src/code/z_kaleido_setup.c b/soh/src/code/z_kaleido_setup.c index 94cb91d49..a335c2460 100644 --- a/soh/src/code/z_kaleido_setup.c +++ b/soh/src/code/z_kaleido_setup.c @@ -18,15 +18,24 @@ void KaleidoSetup_Update(PlayState* play) { play->shootingGalleryStatus <= 1 && gSaveContext.magicState != 8 && gSaveContext.magicState != 9 && (play->sceneNum != SCENE_BOWLING || !Flags_GetSwitch(play, 0x38))) { - if (CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) == 2 && !CHECK_BTN_ALL(input->press.button, BTN_START)) { - CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", 0); + u8 easyPauseBufferEnabled = CVarGetInteger("gCheatEasyPauseBufferEnabled", 0); + u8 easyPauseBufferTimer = CVarGetInteger("gCheatEasyPauseBufferTimer", 0); + + // If start is not seen as pressed on the 2nd to last frame then we should end the easy frame advance flow + if (easyPauseBufferEnabled && easyPauseBufferTimer == 2 && + !CHECK_BTN_ALL(input->press.button, BTN_START)) { + CVarSetInteger("gCheatEasyPauseBufferTimer", 0); } if (CHECK_BTN_ALL(input->cur.button, BTN_L) && CHECK_BTN_ALL(input->press.button, BTN_CUP)) { if (BREG(0)) { pauseCtx->debugState = 3; } - } else if ((CHECK_BTN_ALL(input->press.button, BTN_START) && !CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0)) || CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) == 1) { + } else if ((CHECK_BTN_ALL(input->press.button, BTN_START) && (!easyPauseBufferEnabled || !easyPauseBufferTimer)) || + (easyPauseBufferEnabled && easyPauseBufferTimer == 1)) { // Force Kaleido open when easy pause buffer reaches 0 + // Remember last held buttons for pause buffer cheat (minus start so easy frame advance works) + CVarSetInteger("gCheatEasyPauseBufferLastInputs", input->cur.button & ~(BTN_START)); + gSaveContext.unk_13EE = gSaveContext.unk_13EA; if (CHECK_BTN_ALL(input->cur.button, BTN_L)) diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 935b8b690..f6c6ed4d9 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -1754,12 +1754,11 @@ time_t Play_GetRealTime() { void Play_Main(GameState* thisx) { PlayState* play = (PlayState*)thisx; - if (CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0)) { - CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) - 1); - } - if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) { - CVarSetInteger("gPauseBufferBlockInputFrame", CVarGetInteger("gPauseBufferBlockInputFrame", 0) - 1); + // Decrease the easy pause buffer timer every frame + if (CVarGetInteger("gCheatEasyPauseBufferTimer", 0) > 0) { + CVarSetInteger("gCheatEasyPauseBufferTimer", CVarGetInteger("gCheatEasyPauseBufferTimer", 0) - 1); } + if (play->envCtx.unk_EE[2] == 0 && CVarGetInteger("gLetItSnow", 0)) { play->envCtx.unk_EE[3] = 64; Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_OBJECT_KANKYO, 0, 0, 0, 0, 0, 0, 3, 0); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index e8478e00a..7802f304b 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -3649,10 +3649,8 @@ void KaleidoScope_Update(PlayState* play) if (CHECK_BTN_ALL(input->press.button, BTN_START) || (CHECK_BTN_ALL(input->press.button, BTN_B) && gSaveContext.isBossRush)) { if (CVarGetInteger("gCheatEasyPauseBufferEnabled", 0) || CVarGetInteger("gCheatEasyInputBufferingEnabled", 0)) { - CVarSetInteger("gPauseBufferBlockInputFrame", 9); - } - if (CVarGetInteger("gCheatEasyPauseBufferEnabled", 0)) { - CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", 13); + // Easy pause buffer is 13 frames, 12 for kaledio to end, and one more to advance a single frame + CVarSetInteger("gCheatEasyPauseBufferTimer", 13); } Interface_SetDoAction(play, DO_ACTION_NONE); pauseCtx->state = 0x12; From fafd35cbeb73cd1b71694b31a004fedb016b6587 Mon Sep 17 00:00:00 2001 From: Spodi Date: Sun, 13 Aug 2023 17:41:43 +0200 Subject: [PATCH 22/24] Update libultraship (OpenGL window position fix) (#3113) --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 04ef63c74..0a5781296 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 04ef63c74270dfe9df458bd8335aac7a7097468a +Subproject commit 0a57812968539176bbeaa76c61532d0d6dec4881 From b94d7f135d056874bd19ddbbd85524416c91c9a3 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sun, 13 Aug 2023 18:45:45 -0400 Subject: [PATCH 23/24] #3040 give em to the distro packagers + LUS submodule bump (#3119) * implement for install method packagers * use std::filesystem::temp_directory_path * absolutely impeccable * include libultraship proof * fix windows compilation * rename "Installation" back to "Bundle" --------- Co-authored-by: Alto1772 <56553686+Alto1772@users.noreply.github.com> --- CMake/Packaging-2.cmake | 2 +- CMakeLists.txt | 18 ++++++------- soh/soh/Extractor/Extract.cpp | 50 ++++++++++++++++++++++++++++++++--- soh/soh/Extractor/Extract.h | 3 ++- soh/soh/OTRGlobals.cpp | 24 +++++++++++------ soh/soh/OTRGlobals.h | 1 + 6 files changed, 76 insertions(+), 22 deletions(-) diff --git a/CMake/Packaging-2.cmake b/CMake/Packaging-2.cmake index c6e501c73..3525ae1e4 100644 --- a/CMake/Packaging-2.cmake +++ b/CMake/Packaging-2.cmake @@ -1,6 +1,6 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY 0) -set(CPACK_COMPONENTS_ALL "ship" "appimage") +set(CPACK_COMPONENTS_ALL "ship" "extractor" "appimage") if (NOT CPACK_GENERATOR STREQUAL "External") list(REMOVE_ITEM CPACK_COMPONENTS_ALL "appimage") diff --git a/CMakeLists.txt b/CMakeLists.txt index 3091f8824..1c93f7df8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,14 +98,14 @@ set_property(TARGET soh PROPERTY APPIMAGE_ICON_FILE "${CMAKE_BINARY_DIR}/sohIcon if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") install(PROGRAMS "${CMAKE_SOURCE_DIR}/scripts/linux/appimage/soh.sh" DESTINATION . COMPONENT appimage) -install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT appimage) -install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT appimage) -install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT appimage) -install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/xml/" DESTINATION ./assets/extractor/xmls COMPONENT appimage) -install(DIRECTORY "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists/" DESTINATION ./assets/extractor/filelists COMPONENT appimage) -install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage) -install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage) -install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage) +install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT ship) +install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT extractor) +install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT extractor) +install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/xml/" DESTINATION ./assets/extractor/xmls COMPONENT extractor) +install(DIRECTORY "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists/" DESTINATION ./assets/extractor/filelists COMPONENT extractor) +install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor) +install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor) +install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor) endif() find_package(Python3 COMPONENTS Interpreter) @@ -198,4 +198,4 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") endif() set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_SOURCE_DIR}/CMake/Packaging-2.cmake) -include(CMake/Packaging.cmake) +include(CMake/Packaging.cmake) \ No newline at end of file diff --git a/soh/soh/Extractor/Extract.cpp b/soh/soh/Extractor/Extract.cpp index 143a86380..b5eff7bae 100644 --- a/soh/soh/Extractor/Extract.cpp +++ b/soh/soh/Extractor/Extract.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include extern "C" uint32_t CRC32C(unsigned char* data, size_t dataSize); extern "C" void RomToBigEndian(void* rom, size_t romSize); @@ -496,14 +498,50 @@ const char* Extractor::GetZapdVerStr() const { } } +std::string Extractor::Mkdtemp() { + std::string temp_dir = std::filesystem::temp_directory_path().string(); + + // create 6 random alphanumeric characters + static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist(0, sizeof(charset) - 1); + + char randchr[7]; + for (int i = 0; i < 6; i++) { + randchr[i] = charset[dist(gen)]; + } + randchr[6] = '\0'; + + std::string tmppath = temp_dir + "/extractor-" + randchr; + std::filesystem::create_directory(tmppath); + return tmppath; +} + extern "C" int zapd_main(int argc, char** argv); -bool Extractor::CallZapd() { +bool Extractor::CallZapd(std::string installPath, std::string exportdir) { constexpr int argc = 16; char xmlPath[1024]; char confPath[1024]; std::array argv; const char* version = GetZapdVerStr(); + const char* otrFile = IsMasterQuest() ? "oot-mq.otr" : "oot.otr"; + + std::string romPath = std::filesystem::absolute(mCurrentRomPath).string(); + installPath = std::filesystem::absolute(installPath).string(); + exportdir = std::filesystem::absolute(exportdir).string(); + // Work this out in the temporary folder + std::string tempdir = Mkdtemp(); + std::string curdir = std::filesystem::current_path().string(); +#ifdef _WIN32 + std::filesystem::copy(installPath + "/assets", tempdir + "/assets", + std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing); +#else + std::filesystem::create_symlink(installPath + "/assets", tempdir + "/assets"); +#endif + + std::filesystem::current_path(tempdir); snprintf(xmlPath, 1024, "assets/extractor/xmls/%s", version); snprintf(confPath, 1024, "assets/extractor/Config_%s.xml", version); @@ -513,7 +551,7 @@ bool Extractor::CallZapd() { argv[2] = "-i"; argv[3] = xmlPath; argv[4] = "-b"; - argv[5] = mCurrentRomPath.c_str(); + argv[5] = romPath.c_str(); argv[6] = "-fl"; argv[7] = "assets/extractor/filelists"; argv[8] = "-gsf"; @@ -523,7 +561,7 @@ bool Extractor::CallZapd() { argv[12] = "-se"; argv[13] = "OTR"; argv[14] = "--otrfile"; - argv[15] = IsMasterQuest() ? "oot-mq.otr" : "oot.otr"; + argv[15] = otrFile; #ifdef _WIN32 // Grab a handle to the command window. @@ -541,6 +579,12 @@ bool Extractor::CallZapd() { ShowWindow(cmdWindow, SW_HIDE); #endif + std::filesystem::copy(otrFile, exportdir + "/" + otrFile, std::filesystem::copy_options::overwrite_existing); + + // Go back to where this game was executed from + std::filesystem::current_path(curdir); + std::filesystem::remove_all(tempdir); + return 0; } diff --git a/soh/soh/Extractor/Extract.h b/soh/soh/Extractor/Extract.h index e4eb2e5bb..6c9b4a078 100644 --- a/soh/soh/Extractor/Extract.h +++ b/soh/soh/Extractor/Extract.h @@ -57,7 +57,8 @@ class Extractor { bool IsMasterQuest() const; bool Run(RomSearchMode searchMode = RomSearchMode::Both); - bool CallZapd(); + bool CallZapd(std::string installPath, std::string exportdir); const char* GetZapdStr(); + std::string Mkdtemp(); }; #endif diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9c48d7c77..02a079857 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -208,11 +208,11 @@ const char* constCameraStrings[] = { OTRGlobals::OTRGlobals() { std::vector OTRFiles; - std::string mqPath = LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr"); + std::string mqPath = LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName); if (std::filesystem::exists(mqPath)) { OTRFiles.push_back(mqPath); } - std::string ootPath = LUS::Context::GetPathRelativeToAppDirectory("oot.otr"); + std::string ootPath = LUS::Context::LocateFileAcrossAppDirs("oot.otr", appShortName); if (std::filesystem::exists(ootPath)) { OTRFiles.push_back(ootPath); } @@ -220,7 +220,7 @@ OTRGlobals::OTRGlobals() { if (std::filesystem::exists(sohOtrPath)) { OTRFiles.push_back(sohOtrPath); } - std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods"); + std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName); if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { if (std::filesystem::is_directory(patchesPath)) { for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) { @@ -248,7 +248,7 @@ OTRGlobals::OTRGlobals() { OOT_PAL_GC_DBG2 }; // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) - context = LUS::Context::CreateInstance("Ship of Harkinian", "soh", "shipofharkinian.json", OTRFiles, {}, 3); + context = LUS::Context::CreateInstance("Ship of Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared()); @@ -717,8 +717,16 @@ extern "C" void OTRExtScanner() { extern "C" void InitOTR() { #if not defined (__SWITCH__) && not defined(__WIIU__) - if (!std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr")) && - !std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot.otr"))){ + if (!std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName)) && + !std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot.otr", appShortName))){ + + std::string installPath = LUS::Context::GetAppBundlePath(); + if (!std::filesystem::exists(installPath + "/assets/extractor")) { + Extractor::ShowErrorBox("Extractor assets not found", + "No OTR files found. Missing assets/extractor folder needed to generate OTR file. Exiting..."); + exit(1); + } + bool generatedOtrIsMQ = false; if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) { Extractor extract; @@ -726,7 +734,7 @@ extern "C" void InitOTR() { Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated. Exiting..."); exit(1); } - extract.CallZapd(); + extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName)); generatedOtrIsMQ = extract.IsMasterQuest(); } else { exit(1); @@ -736,7 +744,7 @@ extern "C" void InitOTR() { if (!extract.Run(generatedOtrIsMQ ? RomSearchMode::Vanilla : RomSearchMode::MQ)) { Extractor::ShowErrorBox("Error", "An error occured, an OTR file may have been generated by a different step. Continuing..."); } else { - extract.CallZapd(); + extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName)); } } } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 329f4f0bf..8250b0814 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -19,6 +19,7 @@ #include const std::string customMessageTableID = "BaseGameOverrides"; +const std::string appShortName = "soh"; class OTRGlobals { From 8bc3077529f189d83262a658014f1c366945edf1 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sun, 13 Aug 2023 20:41:57 -0400 Subject: [PATCH 24/24] version bump to Sulu Bravo (#3120) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c93f7df8..f7ad4089d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 7.1.0 LANGUAGES C CXX) -set(PROJECT_BUILD_NAME "Sulu Alfa" CACHE STRING "") +project(Ship VERSION 7.1.1 LANGUAGES C CXX) +set(PROJECT_BUILD_NAME "Sulu Bravo" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)