summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPepe20129 <72659707+Pepe20129@users.noreply.github.com>2026-09-06 20:52:08 +0200
committerGitHub <noreply@github.com>2026-09-06 18:52:08 +0000
commit97f4fd5925596a493e7cd4f45b4f5fb34400a9db (patch)
tree0148f1bb260572a43cddeb1864085706913cca04
parent61523391e29be22c85d6efbabc0c08820783f0a5 (diff)
Ice Trap Name Setting (#6588)HEADdevelop
Co-authored-by: Philip Dubé <159546+serprex@users.noreply.github.com>
-rw-r--r--soh/assets/custom/lang/en_US.json16
-rw-r--r--soh/soh/Enhancements/custom-message/text.cpp62
-rw-r--r--soh/soh/Enhancements/custom-message/text.h4
-rw-r--r--soh/soh/Enhancements/randomizer/Plandomizer.cpp3
-rw-r--r--soh/soh/Enhancements/randomizer/SeedContext.cpp3
-rw-r--r--soh/soh/Enhancements/randomizer/ShuffleRocks.cpp4
-rw-r--r--soh/soh/Enhancements/randomizer/Traps.cpp45
-rw-r--r--soh/soh/Enhancements/randomizer/Traps.h7
-rw-r--r--soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h8
-rw-r--r--soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h1
-rw-r--r--soh/soh/Enhancements/randomizer/settings.cpp2
11 files changed, 139 insertions, 16 deletions
diff --git a/soh/assets/custom/lang/en_US.json b/soh/assets/custom/lang/en_US.json
index fd0fdcf25..8e6c0cf4f 100644
--- a/soh/assets/custom/lang/en_US.json
+++ b/soh/assets/custom/lang/en_US.json
@@ -1869,6 +1869,22 @@
},
"starting_mask_of_truth": {
"name": "Start with Mask of Truth"
+ },
+ "ice_trap_names": {
+ "name": "Ice Trap Trick Names",
+ "description": [
+ "Sets how Ice Traps disguise themselves in text.\n",
+ "\n",
+ "Identical - There's no tell for an item being an Ice Trap.\n",
+ "\n",
+ "Similar - The name will be similar to the item's real name (for example, \"Korok Sword\" instead of \"Kokiri Sword\").\n",
+ "\n",
+ "Misspelled (Vowel) - The name is misspelled by changing a random vowel with another random vowel.\n",
+ "\n",
+ "Misspelled (Duplicate) - The name is misspelled by duplicating a random letter.\n",
+ "\n",
+ "Revealed - Ice Traps will not disguise themselves in text."
+ ]
}
},
"tricks": {
diff --git a/soh/soh/Enhancements/custom-message/text.cpp b/soh/soh/Enhancements/custom-message/text.cpp
index 0023261db..cf2644332 100644
--- a/soh/soh/Enhancements/custom-message/text.cpp
+++ b/soh/soh/Enhancements/custom-message/text.cpp
@@ -1,4 +1,5 @@
#include "text.h"
+#include "soh/ShipUtils.h"
#include <functional>
Text::Text() = default;
@@ -96,3 +97,64 @@ void Text::Replace(const std::string& oldStr, const Text& newText) {
replaceAll(german, oldStr, newText.GetGerman());
replaceAll(spanish, oldStr, newText.GetSpanish());
}
+
+static bool isAsciiVowel(char c) {
+ return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'A' || c == 'E' || c == 'I' || c == 'O' ||
+ c == 'U';
+}
+
+static bool isAsciiLetter(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+// Uniformly picks one position where pred holds, in a single pass. npos if nothing matches.
+static size_t randomMatch(const std::string& target, bool (*pred)(char), uint64_t* randState) {
+ uint32_t seen = 0;
+ size_t pos = std::string::npos;
+
+ for (size_t i = 0; i < target.size(); ++i) {
+ if (pred(target[i]) && ShipUtils::Random(0, ++seen, randState) == 0) {
+ pos = i;
+ }
+ }
+
+ return pos;
+}
+
+static void replaceRandomVowel(std::string& target, uint64_t* randState) {
+ size_t pos = randomMatch(target, isAsciiVowel, randState);
+
+ if (pos == std::string::npos) {
+ return;
+ }
+
+ static constexpr char vowels[] = { 'a', 'e', 'i', 'o', 'u' };
+ bool upper = target[pos] >= 'A' && target[pos] <= 'Z';
+ char oldVowel = upper ? target[pos] + ('a' - 'A') : target[pos];
+
+ // don't replace vowel with itself
+ uint32_t idx = ShipUtils::Random(0, 4, randState);
+ idx += vowels[idx] >= oldVowel;
+
+ target[pos] = upper ? vowels[idx] - ('a' - 'A') : vowels[idx];
+}
+
+void Text::ReplaceRandomVowel(uint64_t* randState) {
+ for (std::string& str : { std::ref(english), std::ref(french), std::ref(german), std::ref(spanish) }) {
+ replaceRandomVowel(str, randState);
+ }
+}
+
+static void duplicateRandomLetter(std::string& target, uint64_t* randState) {
+ size_t pos = randomMatch(target, isAsciiLetter, randState);
+
+ if (pos != std::string::npos) {
+ target.insert(pos + 1, 1, target[pos]);
+ }
+}
+
+void Text::DuplicateRandomLetter(uint64_t* randState) {
+ for (std::string& str : { std::ref(english), std::ref(french), std::ref(german), std::ref(spanish) }) {
+ duplicateRandomLetter(str, randState);
+ }
+}
diff --git a/soh/soh/Enhancements/custom-message/text.h b/soh/soh/Enhancements/custom-message/text.h
index 3cca37593..4cc91128f 100644
--- a/soh/soh/Enhancements/custom-message/text.h
+++ b/soh/soh/Enhancements/custom-message/text.h
@@ -26,6 +26,10 @@ class Text {
void Replace(const std::string& oldStr, const std::string& newStr);
void Replace(const std::string& oldStr, const Text& newText);
+ void ReplaceRandomVowel(uint64_t* randState = nullptr);
+
+ void DuplicateRandomLetter(uint64_t* randState = nullptr);
+
std::string english = "";
std::string french = "";
std::string german = "";
diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.cpp b/soh/soh/Enhancements/randomizer/Plandomizer.cpp
index d2ff59445..f48db2210 100644
--- a/soh/soh/Enhancements/randomizer/Plandomizer.cpp
+++ b/soh/soh/Enhancements/randomizer/Plandomizer.cpp
@@ -878,7 +878,8 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) {
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.f, 6.f)))) {
plandoLogData[index].iceTrapName =
- Rando::Traps::GetTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet())
+ Rando::Traps::GetTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet(),
+ RO_ICE_TRAP_NAMES_SIMILAR)
.name.GetForLanguage(CVarGetInteger(CVAR_SETTING("Languages"), 0))
.c_str();
}
diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp
index e74d96b40..8fb9a8e80 100644
--- a/soh/soh/Enhancements/randomizer/SeedContext.cpp
+++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp
@@ -342,7 +342,8 @@ void Context::CreateItemOverrides() {
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
ItemOverride val(locKey, Traps::GetTrapTrickModel(&rando_state));
iceTrapModels[locKey] = val.LooksLike();
- Traps::TrickName trickName = Traps::GetTrapName(val.LooksLike(), &rando_state);
+ Traps::TrickName trickName = Traps::GetTrapName(
+ val.LooksLike(), static_cast<RandoIceTrapNames>(mOptions[RSK_ICE_TRAP_NAMES].Get()), &rando_state);
val.SetTrickName(trickName.name);
val.SetTrickArticle(trickName.article);
// If this is ice trap is in a shop, change the name based on what the model will look like
diff --git a/soh/soh/Enhancements/randomizer/ShuffleRocks.cpp b/soh/soh/Enhancements/randomizer/ShuffleRocks.cpp
index d5317ff19..b190bd4aa 100644
--- a/soh/soh/Enhancements/randomizer/ShuffleRocks.cpp
+++ b/soh/soh/Enhancements/randomizer/ShuffleRocks.cpp
@@ -215,7 +215,9 @@ static CheckIdentity IdentifyRock(s32 sceneNum, s32 posX, s32 posZ) {
Rando::Location* location = OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(
ACTOR_EN_ISHI, sceneNum, TWO_ACTOR_PARAMS(posX, posZ));
- IdentifyCheck(&rockIdentity, location);
+ if (!IdentifyCheck(&rockIdentity, location)) {
+ SPDLOG_WARN("IdentifyRock did not receive a valid RC value %d,%d.", posX, posZ);
+ }
return rockIdentity;
}
diff --git a/soh/soh/Enhancements/randomizer/Traps.cpp b/soh/soh/Enhancements/randomizer/Traps.cpp
index 0cb52ffa4..9ab724d81 100644
--- a/soh/soh/Enhancements/randomizer/Traps.cpp
+++ b/soh/soh/Enhancements/randomizer/Traps.cpp
@@ -5,6 +5,10 @@
#include "soh/ShipUtils.h"
#include "soh/Enhancements/randomizer/rng.h"
+#include "soh/Enhancements/randomizer/randomizerEnumStrings.h"
+
+#define NOGDI // avoid various windows defines that conflict with things in z64.h
+#include <spdlog/spdlog.h>
#include <vector>
@@ -1859,21 +1863,40 @@ static void InitTrickNames() {
*/
}
-// Generate a fake name for the ice trap based on the item it's displayed as
-Rando::Traps::TrickName Rando::Traps::GetTrapName(uint16_t id, uint64_t* state) {
- // If the trick names table has not been initialized, do so
- if (!initTrickNames) {
- InitTrickNames();
- initTrickNames = true;
+/// @brief Gets the "trick name" for an Ice Trap
+/// @param id The RandomizerGet of the item the Ice Trap is disguised as
+/// @param iceTrapNamesOption The current value of the RSK_ICE_TRAP_NAMES setting
+/// @param state The rng state
+/// @return A Text object with the selected trick name
+Rando::Traps::TrickName Rando::Traps::GetTrapName(RandomizerGet id, RandoIceTrapNames iceTrapNamesOption,
+ uint64_t* state) {
+ if (iceTrapNamesOption == RO_ICE_TRAP_NAMES_SIMILAR) {
+ // If the trick names table has not been initialized, do so
+ if (!initTrickNames) {
+ InitTrickNames();
+ initTrickNames = true;
+ }
+
+ if (!trickNameTable[id].empty()) {
+ // Randomly get the easy, medium, or hard name for the given item id
+ return ShipUtils::RandomElement(trickNameTable[id], state);
+ }
+
+ // No trick name for this item, so fall through to its real name
+ SPDLOG_ERROR("[Rando::Traps::GetTrapName] Couldn't find entry for RG {} in trickNameTable", id);
}
- if (trickNameTable[id].empty()) {
- assert(false);
- return { Text{ "not an Ice Trap" }, Text{ "", "", "" } };
+ const Rando::Item& item =
+ Rando::StaticData::RetrieveItem(iceTrapNamesOption == RO_ICE_TRAP_NAMES_REVEALED ? RG_ICE_TRAP : id);
+ Text name = item.GetName();
+
+ if (iceTrapNamesOption == RO_ICE_TRAP_NAMES_MISSPELLED_CHANGED_VOWEL) {
+ name.ReplaceRandomVowel(state);
+ } else if (iceTrapNamesOption == RO_ICE_TRAP_NAMES_MISSPELLED_DUPLICATED_LETTER) {
+ name.DuplicateRandomLetter(state);
}
- // Randomly get the easy, medium, or hard name for the given item id
- return ShipUtils::RandomElement(trickNameTable[id], state);
+ return { name, item.GetArticle() };
}
RandomizerGet Rando::Traps::GetTrapTrickModel(uint64_t* state) {
diff --git a/soh/soh/Enhancements/randomizer/Traps.h b/soh/soh/Enhancements/randomizer/Traps.h
index 342302022..3c5c1823f 100644
--- a/soh/soh/Enhancements/randomizer/Traps.h
+++ b/soh/soh/Enhancements/randomizer/Traps.h
@@ -4,8 +4,11 @@
#error This header should not be used in C files
#endif
-#include "soh/Enhancements/custom-message/CustomMessageManager.h"
#include "soh/Enhancements/custom-message/text.h"
+#include "soh/Enhancements/item-tables/ItemTableTypes.h"
+#include "soh/Enhancements/randomizer/randomizerTypes.h"
+
+class CustomMessage;
namespace Rando {
namespace Traps {
@@ -14,7 +17,7 @@ struct TrickName {
Text name;
Text article;
};
-TrickName GetTrapName(uint16_t id, uint64_t* state = nullptr);
+TrickName GetTrapName(RandomizerGet id, RandoIceTrapNames iceTrapNamesOption, uint64_t* state = nullptr);
RandomizerGet GetTrapTrickModel(uint64_t* state = nullptr);
bool ShouldJunkItemBeTrap();
void BuildIceTrapMessage(CustomMessage& msg, GetItemEntry getItemEntry);
diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h
index b84cd7c69..f49ce09bf 100644
--- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h
+++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h
@@ -359,6 +359,14 @@ RANDO_ENUM_ITEM(RO_ICE_TRAPS_COUNT)
RANDO_ENUM_ITEM(RO_ICE_TRAPS_PERCENT)
RANDO_ENUM_END(RandoOptionIceTraps)
+RANDO_ENUM_BEGIN(RandoIceTrapNames)
+RANDO_ENUM_ITEM(RO_ICE_TRAP_NAMES_IDENTICAL)
+RANDO_ENUM_ITEM(RO_ICE_TRAP_NAMES_SIMILAR)
+RANDO_ENUM_ITEM(RO_ICE_TRAP_NAMES_MISSPELLED_CHANGED_VOWEL)
+RANDO_ENUM_ITEM(RO_ICE_TRAP_NAMES_MISSPELLED_DUPLICATED_LETTER)
+RANDO_ENUM_ITEM(RO_ICE_TRAP_NAMES_REVEALED)
+RANDO_ENUM_END(RandoIceTrapNames)
+
// Gossip Stone Hint Settings (no hints, needs nothing,
// needs mask of truth, needs stone of agony)
RANDO_ENUM_BEGIN(RandoOptionGossipStones)
diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h
index 6d7d7e969..bd37db1e7 100644
--- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h
+++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h
@@ -105,6 +105,7 @@ RANDO_ENUM_ITEM(RSK_ITEM_POOL)
RANDO_ENUM_ITEM(RSK_BASE_ICE_TRAPS)
RANDO_ENUM_ITEM(RSK_ADDITIONAL_ICE_TRAPS)
RANDO_ENUM_ITEM(RSK_ICE_TRAP_PERCENT)
+RANDO_ENUM_ITEM(RSK_ICE_TRAP_NAMES)
RANDO_ENUM_ITEM(RSK_GOSSIP_STONE_HINTS)
RANDO_ENUM_ITEM(RSK_TOT_ALTAR_HINT)
RANDO_ENUM_ITEM(RSK_GANONDORF_HINT)
diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp
index af2fea8fe..7cb51e5b5 100644
--- a/soh/soh/Enhancements/randomizer/settings.cpp
+++ b/soh/soh/Enhancements/randomizer/settings.cpp
@@ -1406,6 +1406,7 @@ void Settings::CreateOptions() {
OPT_BOOL(RSK_BASE_ICE_TRAPS, CVAR_RANDOMIZER_SETTING("BaseIceTraps"), IMFLAG_NONE, WIDGET_CVAR_COMBOBOX, RO_GENERIC_ON);
OPT_U8(RSK_ADDITIONAL_ICE_TRAPS, {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AdditionalIceTraps"), WIDGET_CVAR_SLIDER_INT, 0);
OPT_U8(RSK_ICE_TRAP_PERCENT, {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTrapPercent"), WIDGET_CVAR_SLIDER_INT, 0);
+ OPT_U8(RSK_ICE_TRAP_NAMES, {"Identical", "Similar", "Misspelled (Vowel)", "Misspelled (Duplicate)", "Revealed"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTrapNames"), WIDGET_CVAR_COMBOBOX, RO_ICE_TRAP_NAMES_SIMILAR);
// TODO: Remove Double Defense
OPT_U8(RSK_STARTING_OCARINA, {"Off", "Fairy Ocarina", "Ocarina of Time"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingOcarina"), WIDGET_CVAR_COMBOBOX, RO_STARTING_OCARINA_OFF);
OPT_BOOL(RSK_STARTING_DEKU_SHIELD, CVAR_RANDOMIZER_SETTING("StartingDekuShield"));
@@ -2127,6 +2128,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_BASE_ICE_TRAPS],
&mOptions[RSK_ADDITIONAL_ICE_TRAPS],
&mOptions[RSK_ICE_TRAP_PERCENT],
+ &mOptions[RSK_ICE_TRAP_NAMES],
},
WidgetContainerType::SECTION);
mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS] =