Refactor Hint Creation and add support for hint Copies and fixed number of hints. (#3205)

* Initial implementation of no-duplicates of special hints

* stupid fixes

* Impa's song will no longer be hinted when you skip child zelda

* fix building

* Fix Loading spoiler logs causing corrupt hints, remove disabled warp song shuffle text from spoiler logs

* Remove Sheik and Saria hints from the spoiler log when are not enabled

* Prevent Magic hinted by Saria and Light Arrows hinted by Sheik from being hinted elsewhere unless they are locked by that item.

* Prevent the Final Frogs gossip stone hint from spawning when the special final frogs hint is enabled.

* Fix building after rebasing in deduplication

* redelete keys.hpp

* Remove Sheik and Saria hints from the spoiler log when are not enabled

* Prevent the Final Frogs gossip stone hint from spawning when the special final frogs hint is enabled.

* First part of copies implementation

* Refactor hint system (broken)

* fix building

* fix obvious errors

* fix fixed hints doubling after failing to place a hint

* Fix bugs with hint distrabution

* Split PlaceRandomHint and fix hint bugs

* Merge special hint functions, move special hint text to the HintTable, expand LightArrow hint category into OtherHint category.

* Fix remaining hint distribution errors

* Forgot to stage dampe diary update

* Restore building after conflict resolution

* fix SetAllInRegionAsHinted
This commit is contained in:
Pepper0ni 2023-11-02 16:42:33 +00:00 committed by GitHub
parent 15ebcd30fb
commit 051314e8b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 480 additions and 543 deletions

View File

@ -1098,20 +1098,13 @@ int Fill() {
printf("Done"); printf("Done");
ctx->CreateItemOverrides(); ctx->CreateItemOverrides();
CreateEntranceOverrides(); CreateEntranceOverrides();
//Always execute ganon hint generation for the funny line
CreateGanonAndSheikText(); //funny ganon line
CreateAltarText(); Text ganonText = RandomElement(GetHintCategory(HintCategory::GanonLine)).GetText();
CreateDampesDiaryText(); CreateMessageFromTextObject(0x70CB, 0, 2, 3, AddColorsAndFormat(ganonText));
CreateGregRupeeHint(); SetGanonText(ganonText);
CreateSariaText();
if (GossipStoneHints.IsNot(HINTS_NO_HINTS)) { CreateAllHints();
printf("\x1b[10;10HCreating Hints...");
CreateAllHints();
printf("Done");
}
if (ShuffleMerchants.Is(SHUFFLEMERCHANTS_HINTS)) {
CreateMerchantsHints();
}
CreateWarpSongTexts(); CreateWarpSongTexts();
return 1; return 1;
} }

View File

@ -21,6 +21,8 @@ void ClearProgress();
void VanillaFill(); void VanillaFill();
int Fill(); int Fill();
std::vector<RandomizerCheck> GetEmptyLocations(std::vector<RandomizerCheck> allowedLocations);
std::vector<RandomizerCheck> GetAccessibleLocations(const std::vector<RandomizerCheck>& allowedLocations, std::vector<RandomizerCheck> GetAccessibleLocations(const std::vector<RandomizerCheck>& allowedLocations,
SearchMode mode = SearchMode::ReachabilitySearch, std::string ignore = "", SearchMode mode = SearchMode::ReachabilitySearch, std::string ignore = "",
bool checkPoeCollectorAccess = false, bool checkPoeCollectorAccess = false,

View File

@ -472,7 +472,7 @@ void HintTable_Init() {
Text{ "a #cow behind webs# in a grotto gifts", /*french*/ "la #vache derrière les toiles# d'une grotte donne", Text{ "a #cow behind webs# in a grotto gifts", /*french*/ "la #vache derrière les toiles# d'une grotte donne",
/*spanish*/ "una #vaca tras la telaraña# de una cueva brinda" }); /*spanish*/ "una #vaca tras la telaraña# de una cueva brinda" });
hintTable[RHT_ZR_FROGS_OCARINA_GAME] = HintText::Always( hintTable[RHT_ZR_FROGS_OCARINA_GAME] = HintText::Sometimes(
{ {
// obscure text // obscure text
Text{ "an #amphibian feast# yields", /*french*/ "un #festin d'amphibiens# donne", Text{ "an #amphibian feast# yields", /*french*/ "un #festin d'amphibiens# donne",
@ -2949,7 +2949,7 @@ void HintTable_Init() {
| LIGHT ARROW LOCATION TEXT| | LIGHT ARROW LOCATION TEXT|
---------------------------*/ ---------------------------*/
hintTable[RHT_LIGHT_ARROW_LOCATION_HINT] = HintText::LightArrow({ hintTable[RHT_LIGHT_ARROW_LOCATION_HINT] = HintText::OtherHint({
// obscure text // obscure text
Text{ Text{
"Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from ", "Ha ha ha... You'll never beat me by reflecting my lightning bolts and unleashing the arrows from ",
@ -2959,7 +2959,7 @@ void HintTable_Init() {
"Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de " }, "Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de " },
}); });
hintTable[RHT_SHEIK_LIGHT_ARROW_HINT] = HintText::LightArrow({ hintTable[RHT_SHEIK_LIGHT_ARROW_HINT] = HintText::OtherHint({
// obscure text // obscure text
Text{ Text{
"I overheard Ganondorf say that he misplaced the %yLight Arrows%w in&%r", "I overheard Ganondorf say that he misplaced the %yLight Arrows%w in&%r",
@ -2974,6 +2974,70 @@ void HintTable_Init() {
Text{ "your pocket", /*french*/ "tes poches", /*spanish*/ "tu bolsillo" }, Text{ "your pocket", /*french*/ "tes poches", /*spanish*/ "tu bolsillo" },
}); });
/*--------------------------
| OTHER HINT TEXT |
---------------------------*/
hintTable[RHT_DAMPE_DIARY01] = HintText::OtherHint({
// obscure text
Text{
"Whoever reads this, please enter %r",
/*french*/
"Toi qui lit ce journal, rends-toi dans %r",
/*german?*/
"Wer immer dies liest, der möge folgenden Ort aufsuchen: %r"},
});
hintTable[RHT_DAMPE_DIARY02] = HintText::OtherHint({
// obscure text
Text{
"%w. I will let you have my stretching, shrinking keepsake.^I'm waiting for you.&--Dampé",
/*french*/
"%w. Et peut-être auras-tu droit à mon précieux %rtrésor%w.^Je t'attends...&--Igor",
/*german?*/
"%w. Ihm gebe ich meinen langen, kurzen Schatz.^Ich warte!&Boris"},
});
hintTable[RHT_GREG_HINT01] = HintText::OtherHint({
// obscure text
Text{
"By the way, if you're interested, I saw the shiniest %gGreen Rupee%w somewhere in%g ",
/*french*/
"Au fait, si ça t'intéresse, j'ai aperçu le plus éclatant des %gRubis Verts%w quelque part à %g",
/*spanish*/
""},
});
hintTable[RHT_GREG_HINT02] = HintText::OtherHint({
// obscure text
Text{
"%w.^It's said to have %rmysterious powers%w...^But then, it could just be another regular rupee.&Oh well.",
/*french*/
"%w. On dit qu'il possède des pouvoirs mystérieux... Mais bon, ça pourrait juste être un autre rubis ordinaire.",
/*spanish*/
""},
});
hintTable[RHT_SARIA_TEXT01] = HintText::OtherHint({
// obscure text
Text{
"Did you feel the %gsurge of magic%w recently? A mysterious bird told me it came from %g",
/*french*/
"As-tu récemment ressenti une vague de %gpuissance magique%w? Un mystérieux hibou m'a dit qu'elle provenait du %g",
/*spanish*/
""},
});
hintTable[RHT_SARIA_TEXT02] = HintText::OtherHint({
// obscure text
Text{
"%w.^You should check that place out, @!$C",
/*french*/
"%w. Tu devrais aller y jeter un coup d'oeil, @!$C",
/*spanish*/
"%w.$C"},
});
/*-------------------------- /*--------------------------
| GANON LINE TEXT | | GANON LINE TEXT |
---------------------------*/ ---------------------------*/

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,6 @@
struct HintDistributionSetting { struct HintDistributionSetting {
HintType type; HintType type;
uint8_t order;
size_t weight; size_t weight;
uint8_t fixed; uint8_t fixed;
uint8_t copies; uint8_t copies;
@ -42,7 +41,7 @@ enum class HintCategory {
LACS, LACS,
Altar, Altar,
Validation, Validation,
LightArrow, OtherHint,
GanonLine, GanonLine,
MerchantsDialogs, MerchantsDialogs,
}; };
@ -112,9 +111,9 @@ public:
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Validation}; return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Validation};
} }
static auto LightArrow(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) { static auto OtherHint(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::LightArrow}; return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::OtherHint};
} //RANDOTODO Concert to generic special hints? }
static auto GanonLine(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) { static auto GanonLine(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::GanonLine}; return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::GanonLine};
@ -197,22 +196,17 @@ using ConditionalAlwaysHint = std::pair<RandomizerCheck, std::function<bool()>>;
//10 dungeons as GTG and GC are excluded //10 dungeons as GTG and GC are excluded
extern std::array<DungeonInfo, 10> dungeonInfoData; extern std::array<DungeonInfo, 10> dungeonInfoData;
extern std::array<ConditionalAlwaysHint, 10> conditionalAlwaysHints; extern std::array<ConditionalAlwaysHint, 10> conditionalAlwaysHints;
extern RandomizerHintTextKey GetHintRegionHintKey(const RandomizerRegion area); extern RandomizerHintTextKey GetHintRegionHintKey(const RandomizerRegion area);
extern void CreateAllHints(); extern void CreateAllHints();
extern void CreateMerchantsHints();
extern void CreateWarpSongTexts(); extern void CreateWarpSongTexts();
extern void CreateDampesDiaryText();
extern void CreateGregRupeeHint();
extern void CreateSariaText();
extern void CreateGanonAndSheikText();
extern void CreateAltarText();
Text& GetChildAltarText(); Text& GetChildAltarText();
Text& GetAdultAltarText(); Text& GetAdultAltarText();
Text& GetGanonText(); Text& GetGanonText();
void SetGanonText(Text text);
Text& GetGanonHintText(); Text& GetGanonHintText();
Text& GetDampeHintText(); Text& GetDampeHintText();
Text& GetGregHintText(); Text& GetGregHintText();
@ -226,6 +220,7 @@ Text& GetWarpRequiemText();
Text& GetWarpNocturneText(); Text& GetWarpNocturneText();
Text& GetWarpPreludeText(); Text& GetWarpPreludeText();
std::string GetDampeHintLoc();
std::string GetLightArrowHintLoc(); std::string GetLightArrowHintLoc();
std::string GetDampeHintLoc();
std::string GetGregHintLoc();
std::string GetSariaHintLoc(); std::string GetSariaHintLoc();

View File

@ -37,7 +37,7 @@ using json = nlohmann::ordered_json;
json jsonData; json jsonData;
std::map<RandomizerHintTextKey, Rando::ItemLocation*> hintedLocations; std::map<RandomizerHintTextKey, Rando::ItemLocation*> hintedLocations;
extern std::unordered_map<HintType, std::string> hintTypeNames; extern std::array<std::string, HINT_TYPE_MAX> hintTypeNames;
extern std::array<std::string, 17> hintCategoryNames; extern std::array<std::string, 17> hintCategoryNames;
extern Area* GetHintRegion(uint32_t); extern Area* GetHintRegion(uint32_t);
@ -775,7 +775,7 @@ static void WriteHints(int language) {
} }
if (Settings::GregHintText){ if (Settings::GregHintText){
jsonData["gregText"] = gregText; jsonData["gregText"] = gregText;
jsonData["gregLoc"] = Rando::StaticData::GetLocation(GetItemLocation(RG_GREG_RUPEE)->GetRandomizerCheck())->GetName(); jsonData["gregLoc"] = GetGregHintLoc();
} }
if (Settings::SariaHintText){ if (Settings::SariaHintText){
jsonData["sariaText"] = sariaText; jsonData["sariaText"] = sariaText;
@ -804,16 +804,16 @@ static void WriteHints(int language) {
std::string textStr = AutoFormatHintTextString(unformattedHintTextString); std::string textStr = AutoFormatHintTextString(unformattedHintTextString);
jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["hint"] = textStr; jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["hint"] = textStr;
jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["type"] = hintTypeNames.find(hintType)->second; jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["type"] = hintTypeNames[(int)hintType];
if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) { if ((hintType >= HINT_TYPE_ALWAYS && hintType < HINT_TYPE_JUNK) || hintType == HINT_TYPE_WOTH) {
jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["item"] = hintedLocation->GetPlacedItemName().GetEnglish(); jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["item"] = hintedLocation->GetPlacedItemName().GetEnglish();
if (hintType != HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) { if (hintType != HINT_TYPE_NAMED_ITEM) {
jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["location"] = jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["location"] =
Rando::StaticData::GetLocation(hintedLocation->GetRandomizerCheck())->GetName(); Rando::StaticData::GetLocation(hintedLocation->GetRandomizerCheck())->GetName();
} }
} }
if (hintType != HINT_TYPE_TRIAL && hintType != HINT_TYPE_JUNK) { if (hintType != HINT_TYPE_TRIAL && hintType != HINT_TYPE_JUNK) {
jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["area"] = hint->GetHintedRegion(); jsonData["hints"][Rando::StaticData::GetLocation(key)->GetName()]["area"] = hint->GetHintedRegion(); //RANDOTODO find elegent way to capitalise this
} }
} }
} }

View File

@ -36,7 +36,7 @@ extern "C" uint32_t ResourceMgr_IsGameMasterQuest();
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
extern std::map<RandomizerCheckArea, std::string> rcAreaNames; extern std::map<RandomizerCheckArea, std::string> rcAreaNames;
extern std::unordered_map<HintType, std::string> hintTypeNames; extern std::array<std::string, HINT_TYPE_MAX> hintTypeNames;
using json = nlohmann::json; using json = nlohmann::json;
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
@ -139,6 +139,18 @@ Randomizer::Randomizer() {
item.GetName().french, item.GetName().french,
}; };
} }
for (auto area : rcAreaNames) {
SpoilerfileAreaNameToEnum[area.second] = area.first;
}
SpoilerfileAreaNameToEnum["Inside Ganon's Castle"] = RCAREA_GANONS_CASTLE;
SpoilerfileAreaNameToEnum["the Lost Woods"] = RCAREA_LOST_WOODS;
SpoilerfileAreaNameToEnum["the Market"] = RCAREA_MARKET;
SpoilerfileAreaNameToEnum["the Graveyard"] = RCAREA_GRAVEYARD;
SpoilerfileAreaNameToEnum["Haunted Wasteland"] = RCAREA_WASTELAND;
SpoilerfileAreaNameToEnum["outside Ganon's Castle"] = RCAREA_HYRULE_CASTLE;
for (int c = 0; c < hintTypeNames.size(); c++) {
SpoilerfileHintTypeNameToEnum[hintTypeNames[c]] = (HintType)c;
}
} }
Sprite* Randomizer::GetSeedTexture(uint8_t index) { Sprite* Randomizer::GetSeedTexture(uint8_t index) {

View File

@ -31,23 +31,23 @@ typedef enum {
} RandomizerCheckStatus; } RandomizerCheckStatus;
typedef enum { typedef enum {
HINT_TYPE_STATIC,
HINT_TYPE_TRIAL, HINT_TYPE_TRIAL,
HINT_TYPE_ALWAYS,
HINT_TYPE_WOTH, // Way of the Hero HINT_TYPE_WOTH, // Way of the Hero
HINT_TYPE_BARREN, HINT_TYPE_BARREN,
HINT_TYPE_ENTRANCE, HINT_TYPE_ENTRANCE,
HINT_TYPE_ALWAYS,
HINT_TYPE_SOMETIMES, HINT_TYPE_SOMETIMES,
HINT_TYPE_RANDOM,
HINT_TYPE_ITEM,
HINT_TYPE_SONG, HINT_TYPE_SONG,
HINT_TYPE_OVERWORLD, HINT_TYPE_OVERWORLD,
HINT_TYPE_DUNGEON, HINT_TYPE_DUNGEON,
HINT_TYPE_JUNK,
HINT_TYPE_NAMED_ITEM, HINT_TYPE_NAMED_ITEM,
HINT_TYPE_STATIC, // For special hints, like Light Arrows or Hookshot Hints HINT_TYPE_RANDOM,
HINT_TYPE_JUNK,
HINT_TYPE_MAX HINT_TYPE_MAX
} HintType; } HintType;
// Check types based on main settings // Check types based on main settings
typedef enum { typedef enum {
RCTYPE_STANDARD, // Base set of rando checks RCTYPE_STANDARD, // Base set of rando checks
@ -3182,6 +3182,13 @@ typedef enum {
RHT_SHEIK_LIGHT_ARROW_HINT, RHT_SHEIK_LIGHT_ARROW_HINT,
// Your Pocket // Your Pocket
RHT_YOUR_POCKET, RHT_YOUR_POCKET,
// Other Hints
RHT_DAMPE_DIARY01,
RHT_DAMPE_DIARY02,
RHT_GREG_HINT01,
RHT_GREG_HINT02,
RHT_SARIA_TEXT01,
RHT_SARIA_TEXT02,
// Ganon Line // Ganon Line
RHT_GANON_LINE01, RHT_GANON_LINE01,
RHT_GANON_LINE02, RHT_GANON_LINE02,