Ice Trap Name Setting (#6588)

Co-authored-by: Philip Dubé <159546+serprex@users.noreply.github.com>
This commit is contained in:
Pepe20129
2026-09-06 20:52:08 +02:00
committed by GitHub
parent 61523391e2
commit 97f4fd5925
11 changed files with 139 additions and 16 deletions
+16
View File
@@ -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": {
@@ -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);
}
}
@@ -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 = "";
@@ -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();
}
@@ -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
@@ -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;
}
+34 -11
View File
@@ -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) {
+5 -2
View File
@@ -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);
@@ -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)
@@ -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)
@@ -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] =