Streamline hint generation (#3401)

* reimplement 3drando's hashtag color replacement system.

Also generates merchant text at seed gen time instead of runtime.
By merchants, I mean Bean Salesman, Medigoron, Granny, and Wasteland
Bombchu guy. Scrubs and shops are still dynamic at runtime.

* Improved auto-formatting and fixed altar text.

* Gets hint text for spoiler direct from context.

* Removal of now unused code.

* Change warp song hint generation/retrieval

Generates full warp location text instead of just location names and stores all six in the custom message tables for later retrieval as opposed to dynamically swapping in the location names every time the text is rendered.

* Change Frog Ocarina Game Hint generation/retrieval

Similar to previous changes, removes the on-the-fly generation aspect of it and just generates the full hint text once during seed generation.

* Update soh/soh/Enhancements/randomizer/3drando/text.hpp

Co-authored-by: Pepe20129 <72659707+Pepe20129@users.noreply.github.com>

* Fix submodules appearing as changed files.

* Fix WOTH/Foolish colors to match develop-macready.

* Fixes backwards colors for area and item in some hints.

---------

Co-authored-by: Pepe20129 <72659707+Pepe20129@users.noreply.github.com>
This commit is contained in:
Christopher Leggett 2024-01-08 14:41:33 -05:00 committed by GitHub
commit a196dd6b7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 646 additions and 732 deletions

View file

@ -42,7 +42,7 @@ Context::Context() {
{ "Item Location", HINT_TYPE_ITEM_LOCATION },
{ "Junk", HINT_TYPE_JUNK },
};
mSpoilerfileAreaNameToEnum = {
{"No Hint", RA_NONE},
{"Link's Pocket", RA_LINKS_POCKET},
@ -423,187 +423,97 @@ void Context::ParseItemLocationsJson(nlohmann::json spoilerFileJson) {
}
}
std::string AltarIconString(const char iconChar) {
std::string iconString;
iconString += '\x13';
switch (iconChar) {
case '0':
// Kokiri Emerald
iconString += '\x6C';
break;
case '1':
// Goron Ruby
iconString += '\x6D';
break;
case '2':
// Zora Sapphire
iconString += '\x6E';
break;
case '3':
// Forest Medallion
iconString += '\x66';
break;
case '4':
// Fire Medallion
iconString += '\x67';
break;
case '5':
// Water Medallion
iconString += '\x68';
break;
case '6':
// Spirit Medallion
iconString += '\x69';
break;
case '7':
// Shadow Medallion
iconString += '\x6A';
break;
case '8':
// Light Medallion
iconString += '\x6B';
break;
case 'o':
// Open DOT (master sword)
iconString += '\x3C';
break;
case 'c':
// Closed DOT (fairy ocarina)
iconString += '\x07';
break;
case 'i':
// Intended DOT (oot)
iconString += '\x08';
break;
case 'l':
// Light Arrow (for bridge reqs)
iconString += '\x12';
break;
case 'b':
// Boss Key (ganon boss key location)
iconString += '\x74';
break;
case 'L':
// Bow with Light Arrow
iconString += '\x3A';
break;
case 'k':
// Kokiri Tunic
iconString += '\x41';
break;
default:
break;
}
return iconString;
}
std::string FormatJsonHintText(const std::string& jsonHint) {
std::string formattedHintMessage = jsonHint;
// add icons to altar text
for (const char iconChar : { '0', '1', '2', '3', '4', '5', '6', '7', '8', 'o', 'c', 'i', 'l', 'b', 'L', 'k' }) {
std::string textToReplace = "$";
textToReplace += iconChar;
if (const size_t start_pos = formattedHintMessage.find(textToReplace); start_pos != std::string::npos) {
std::string iconString = AltarIconString(iconChar);
formattedHintMessage.replace(start_pos, textToReplace.length(), iconString);
}
}
return formattedHintMessage;
}
void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
// Child Altar
std::string childAltarJsonText = spoilerFileJson["childAltar"]["hintText"].get<std::string>();
std::string formattedChildAltarText = FormatJsonHintText(childAltarJsonText);
AddHint(RH_ALTAR_CHILD, Text(formattedChildAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
std::string childAltarText = spoilerFileJson["childAltar"]["hintText"].get<std::string>();
AddHint(RH_ALTAR_CHILD, Text(childAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", RA_NONE);
mEmeraldLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["emeraldLoc"]];
mRubyLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["rubyLoc"]];
mSapphireLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["sapphireLoc"]];
// Adult Altar
std::string adultAltarJsonText = spoilerFileJson["adultAltar"]["hintText"].get<std::string>();
std::string formattedAdultAltarText = FormatJsonHintText(adultAltarJsonText);
AddHint(RH_ALTAR_ADULT, Text(formattedAdultAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
std::string adultAltarText = spoilerFileJson["adultAltar"]["hintText"].get<std::string>();
AddHint(RH_ALTAR_ADULT, Text(adultAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", RA_NONE);
mForestMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["forestMedallionLoc"].get<std::string>()];
mFireMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["fireMedallionLoc"].get<std::string>()];
mWaterMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["waterMedallionLoc"].get<std::string>()];
mShadowMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["shadowMedallionLoc"].get<std::string>()];
mSpiritMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["spiritMedallionLoc"].get<std::string>()];
mLightMedallionLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["lightMedallionLoc"].get<std::string>()];
// Ganondorf and Sheik Light Arrow Hints
std::string ganonHintText = FormatJsonHintText(spoilerFileJson["ganonHintText"].get<std::string>());
std::string ganonHintText = spoilerFileJson["ganonHintText"].get<std::string>();
RandomizerCheck lightArrowLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["lightArrowHintLoc"].get<std::string>()];
std::string lightArrowRegion = spoilerFileJson["lightArrowArea"].get<std::string>();
AddHint(RH_GANONDORF_HINT, Text(ganonHintText), lightArrowLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[lightArrowRegion]);
if (spoilerFileJson.contains("sheikText")) {
std::string sheikText = FormatJsonHintText(spoilerFileJson["sheikText"].get<std::string>());
std::string sheikText = spoilerFileJson["sheikText"].get<std::string>();
AddHint(RH_SHEIK_LIGHT_ARROWS, Text(sheikText), lightArrowLoc, HINT_TYPE_STATIC, lightArrowRegion);
}
std::string ganonText = FormatJsonHintText(spoilerFileJson["ganonText"].get<std::string>());
std::string ganonText = spoilerFileJson["ganonText"].get<std::string>();
AddHint(RH_GANONDORF_NOHINT, Text(ganonText), RC_UNKNOWN_CHECK, HINT_TYPE_JUNK, "Static", RA_GANONS_CASTLE);
// Dampe Hookshot Hint
if (spoilerFileJson.contains("dampeText")) {
std::string dampeText = FormatJsonHintText(spoilerFileJson["dampeText"].get<std::string>());
std::string dampeText = spoilerFileJson["dampeText"].get<std::string>();
std::string dampeRegion = spoilerFileJson["dampeRegion"].get<std::string>();
RandomizerCheck dampeHintLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["dampeHintLoc"].get<std::string>()];
AddHint(RH_DAMPES_DIARY, Text(dampeText), dampeHintLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[dampeRegion]);
}
// Greg Hint
if (spoilerFileJson.contains("gregText")) {
std::string gregText = FormatJsonHintText(spoilerFileJson["gregText"].get<std::string>());
std::string gregText = spoilerFileJson["gregText"].get<std::string>();
std::string gregRegion = spoilerFileJson["gregRegion"].get<std::string>();
RandomizerCheck gregLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["gregLoc"].get<std::string>()];
AddHint(RH_GREG_RUPEE, Text(gregText), gregLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[gregRegion]);
}
// Saria Magic Hint
if (spoilerFileJson.contains("sariaText")) {
std::string sariaText = FormatJsonHintText(spoilerFileJson["sariaText"].get<std::string>());
std::string sariaText = spoilerFileJson["sariaText"].get<std::string>();
std::string sariaRegion = spoilerFileJson["sariaRegion"].get<std::string>();
RandomizerCheck sariaHintLoc = mSpoilerfileCheckNameToEnum[spoilerFileJson["sariaHintLoc"].get<std::string>()];
AddHint(RH_SARIA, Text(sariaText), sariaHintLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[sariaRegion]);
}
// Warp Songs
if (spoilerFileJson.contains("warpMinuetText")) {
std::string warpMinuetText = FormatJsonHintText(spoilerFileJson["warpMinuetText"].get<std::string>()); //RANDOTODO fall back for if location is used
std::string warpMinuetText = spoilerFileJson["warpMinuetText"].get<std::string>(); //RANDOTODO fall back for if location is used
AddHint(RH_MINUET_WARP_LOC, Text(warpMinuetText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpMinuetText]);
}
if (spoilerFileJson.contains("warpBoleroText")) {
std::string warpBoleroText = FormatJsonHintText(spoilerFileJson["warpBoleroText"].get<std::string>());
std::string warpBoleroText = spoilerFileJson["warpBoleroText"].get<std::string>();
AddHint(RH_BOLERO_WARP_LOC, Text(warpBoleroText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpBoleroText]);
}
if (spoilerFileJson.contains("warpSerenadeText")) {
std::string warpSerenadeText = FormatJsonHintText(spoilerFileJson["warpSerenadeText"].get<std::string>());
std::string warpSerenadeText = spoilerFileJson["warpSerenadeText"].get<std::string>();
AddHint(RH_SERENADE_WARP_LOC, Text(warpSerenadeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpSerenadeText]);
}
if (spoilerFileJson.contains("warpRequiemText")) {
std::string warpRequiemText = FormatJsonHintText(spoilerFileJson["warpRequiemText"].get<std::string>());
std::string warpRequiemText = spoilerFileJson["warpRequiemText"].get<std::string>();
AddHint(RH_REQUIEM_WARP_LOC, Text(warpRequiemText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpRequiemText]);
}
if (spoilerFileJson.contains("warpNocturneText")) {
std::string warpNocturneText = FormatJsonHintText(spoilerFileJson["warpNocturneText"].get<std::string>());
std::string warpNocturneText = spoilerFileJson["warpNocturneText"].get<std::string>();
AddHint(RH_NOCTURNE_WARP_LOC, Text(warpNocturneText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpNocturneText]);
}
if (spoilerFileJson.contains("warpPreludeText")) {
std::string warpPreludeText = FormatJsonHintText(spoilerFileJson["warpPreludeText"].get<std::string>());
std::string warpPreludeText = spoilerFileJson["warpPreludeText"].get<std::string>();
AddHint(RH_PRELUDE_WARP_LOC, Text(warpPreludeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpPreludeText]);
}
// Gossip Stones
nlohmann::json hintsJson = spoilerFileJson["hints"];
for (auto it = hintsJson.begin(); it != hintsJson.end(); ++it) {
RandomizerCheck gossipStoneLoc = mSpoilerfileCheckNameToEnum[it.key()];
nlohmann::json hintInfo = it.value();
std::string hintText = FormatJsonHintText(hintInfo["hint"].get<std::string>());
std::string hintText = hintInfo["hint"].get<std::string>();
HintType hintType = mSpoilerfileHintTypeNameToEnum[hintInfo["type"].get<std::string>()];
RandomizerCheck hintedLocation = hintInfo.contains("location") ? mSpoilerfileCheckNameToEnum[hintInfo["location"]] : RC_UNKNOWN_CHECK;
std::string hintedArea = hintInfo.contains("area") ? hintInfo["area"].get<std::string>() : "";
AddHint(static_cast<RandomizerHintKey>(gossipStoneLoc - RC_COLOSSUS_GOSSIP_STONE + 1), Text(hintText), hintedLocation, hintType, hintedArea);
RandomizerArea hintedArea = hintInfo.contains("area") ? mSpoilerfileAreaNameToEnum[hintInfo["area"].get<std::string>()] : RA_NONE;
std::string distribution = hintInfo["distribution"].get<std::string>();
AddHint(static_cast<RandomizerHintKey>(gossipStoneLoc - RC_COLOSSUS_GOSSIP_STONE + 1), Text(hintText), hintedLocation, hintType, distribution, hintedArea);
}
}