Skip to content

Commit

Permalink
Rando: Master Sword Shuffle (HarbourMasters#2981)
Browse files Browse the repository at this point in the history
* The mother of all commits

* Removed `GI_SWORD_MASTER`;
"Master Sword" Items now actually give MS

* Removed dupe MS entries in item pool;
updated GIMESSAGE (should stop crashing on non-Windows);
re-added MS in item list

* Give Adult Link a freebie with shuffle MS on;
cihld -> adult no longer gives MS;
ToT Master Sword now gives correct item

* add master sword GI draw func based on ToT MS object

* Force `MasterSword` logic var to only update upon getting MS

* Dorf funny line now activates with LA and MS in inv

* Apply suggestions

* Updated RAND_INF;
Check Tracker changes;
Gave RAND_INF and ice trap logic to ToT MS check;
Fixed swordless behavior for HBA/fishing

* ToT MS Check now works in check tracker;
Visual bug where box hovers over non-existent MS gone;
Fixed RAND_INF check with ToT MS pedestal;
Ganon no longer gives free MS

* adult equips no longer reset in MS shuffle

* Apply (most) locacc review suggestions

Co-authored-by: inspectredc <78732756+inspectredc@users.noreply.github.com>

* Reorganized swordless check for interface to fit edge cases;
getting master sword no longer highlights box

* Edge case for BGS but no bow

* Fix implicit declaration error for GI hooks (#9)

* Adjusted `CanAdultAttack/Damage`; applied logic suggestions

* Fixed build errors (hopefully)

* Cleanup merge

* get shit working again

* Tidied up remaining uses of DD flag as rando indicator

* make master sword invisible and fix ms flag (#10)

* Add text to sheik if go mode is obtained but barrier is still up

* overhaul swordless behavior in `func_80083108`

* reworked ToT MS Check to have an actual GI

* suggestions

* Apply suggestions

* Better swordless handling with temp B (#11)

* better swordless handling with temp B

* prevent auto save in fishing pond

* prevent auto save during bombchu bowling

* enum fix

---------

Co-authored-by: Adam Bird <archez39@me.com>
Co-authored-by: inspectredc <78732756+inspectredc@users.noreply.github.com>
Co-authored-by: RaelCappra <rael.cappra@gmail.com>
Co-authored-by: Adam Bird <Archez@users.noreply.github.com>
  • Loading branch information
5 people authored Oct 22, 2023
1 parent e6445e0 commit 2eaed8d
Show file tree
Hide file tree
Showing 57 changed files with 558 additions and 310 deletions.
9 changes: 7 additions & 2 deletions soh/include/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -2449,13 +2449,18 @@ void Heaps_Free(void);

CollisionHeader* BgCheck_GetCollisionHeader(CollisionContext* colCtx, s32 bgId);

void Interface_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);

// Exposing these methods to leverage them from the file select screen to render messages
void Message_OpenText(PlayState* play, u16 textId);
void Message_Decode(PlayState* play);
void Message_DrawText(PlayState* play, Gfx** gfxP);

// #region SOH [General]

void Interface_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH);
void Interface_RandoRestoreSwordless(void);

// #endregion

#ifdef __cplusplus
#undef this
};
Expand Down
6 changes: 3 additions & 3 deletions soh/include/z64save.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ typedef struct {
/* */ u8 mqDungeonCount;
/* */ u8 pendingIceTrapCount;
/* */ SohStats sohStats;
/* */ u8 temporaryWeapon;
/* */ FaroresWindData backupFW;
/* */ RandomizerCheckTrackerData checkTrackerData[RC_MAX];
// #endregion
Expand All @@ -302,18 +301,19 @@ typedef struct {
/* */ char childAltarText[250];
/* */ char adultAltarText[750];
/* */ RandomizerCheck rewardCheck[9];
/* */ char ganonHintText[150];
/* */ char ganonHintText[300];
/* */ char gregHintText[250];
/* */ char ganonText[250];
/* */ char dampeText[150];
/* */ char sheikText[150];
/* */ char sheikText[200];
/* */ char sariaText[150];
/* */ char warpMinuetText[100];
/* */ char warpBoleroText[100];
/* */ char warpSerenadeText[100];
/* */ char warpRequiemText[100];
/* */ char warpNocturneText[100];
/* */ char warpPreludeText[100];
/* */ RandomizerCheck masterSwordHintCheck;
/* */ RandomizerCheck lightArrowHintCheck;
/* */ RandomizerCheck sariaCheck;
/* */ RandomizerCheck gregCheck;
Expand Down
2 changes: 2 additions & 0 deletions soh/soh/Enhancements/debugger/debugSaveEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ const std::vector<FlagTable> flagTables = {

{ RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD, "KAK_100_GOLD_SKULLTULA_REWARD" },
{ RAND_INF_GREG_FOUND, "RAND_INF_GREG_FOUND" },

{ RAND_INF_TOT_MASTER_SWORD, "RAND_INF_TOT_MASTER_SWORD"},
{ RAND_INF_CHILD_FISHING, "RAND_INF_CHILD_FISHING" },
{ RAND_INF_ADULT_FISHING, "RAND_INF_ADULT_FISHING" },
{ RAND_INF_10_BIG_POES, "RAND_INF_10_BIG_POES" },
Expand Down
7 changes: 5 additions & 2 deletions soh/soh/Enhancements/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ void AutoSave(GetItemEntry itemEntry) {
// Don't autosave immediately after buying items from shops to prevent getting them for free!
// Don't autosave in the Chamber of Sages since resuming from that map breaks the game
// Don't autosave during the Ganon fight when picking up the Master Sword
// Don't autosave in the fishing pond to prevent getting rod on B outside of the pond
// Don't autosave in the bombchu bowling alley to prevent having chus on B outside of the minigame
// Don't autosave in grottos since resuming from grottos breaks the game.
if ((CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) && (gPlayState != NULL) && (gSaveContext.pendingSale == ITEM_NONE) &&
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS)) {
Expand Down Expand Up @@ -311,8 +313,9 @@ void AutoSave(GetItemEntry itemEntry) {
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
performSave = true;
}
if ((gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN) || (gPlayState->sceneNum == SCENE_GROTTOS) ||
(gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES)) {
if (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN || gPlayState->sceneNum == SCENE_GROTTOS ||
gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_FISHING_POND ||
gPlayState->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) {
if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_MAJOR_ITEMS ||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS ||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
Expand Down
14 changes: 14 additions & 0 deletions soh/soh/Enhancements/randomizer/3drando/hint_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2958,6 +2958,20 @@ void HintTable_Init() {
"Ja, ja, ja... Nunca me derrotarás reflejando mis esferas de energía y desplegando la flecha de luz de " },
});


/*--------------------------
|MASTER SWORD LOCATION TEXT|
---------------------------*/

hintTable[MASTER_SWORD_LOCATION_HINT] = HintText::MasterSword({
// obscure text
Text{"And even if you do, you'll never find the legendary blade hidden in ",
/*french*/
"Et même si tu les trouves, tu ne touveras jamais l'épée de légende cachée dans ",
/*spanish*/
"E incluso si lo haces, nunca encontrarás la espada legendaria escondida en " },
});

hintTable[YOUR_POCKET] = HintText::Exclude({
// obscure text
Text{ "your pocket", /*french*/ "tes poches", /*spanish*/ "tu bolsillo" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ void HintTable_Init_Exclude_Overworld() {
Text{"a #hole in a volcano# holds", /*french*/"la #grotte dans le volcan# contient", /*spanish*/"bajo el #hoyo de un volcán# yace"},
});

hintTable[TOT_MASTER_SWORD] = HintText::Exclude({
//obscure text
Text{"a #pedestal in a temple# holds", /*french*/"un #piédestal dans un temple# contient", /*spanish*/"un #pedestal en un templo# sostiene"},
});

hintTable[TOT_LIGHT_ARROWS_CUTSCENE] = HintText::Exclude({
//obscure text
Expand Down
35 changes: 35 additions & 0 deletions soh/soh/Enhancements/randomizer/3drando/hints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "item_pool.hpp"
#include "logic.hpp"
#include "random.hpp"
#include "settings.hpp"
#include "spoiler_log.hpp"
#include "fill.hpp"
#include "hint_list.hpp"
Expand Down Expand Up @@ -143,7 +144,9 @@ Text warpRequiemText;
Text warpNocturneText;
Text warpPreludeText;


std::string lightArrowHintLoc;
std::string masterSwordHintLoc;
std::string sariaHintLoc;
std::string dampeHintLoc;

Expand Down Expand Up @@ -203,6 +206,10 @@ Text& GetWarpPreludeText() {
return warpPreludeText;
}

std::string GetMasterSwordHintLoc() {
return masterSwordHintLoc;
}

std::string GetLightArrowHintLoc() {
return lightArrowHintLoc;
}
Expand Down Expand Up @@ -607,6 +614,22 @@ void CreateGanonText() {
}
ganonHintText = ganonHintText + "!";

if (ShuffleMasterSword) {
//Get the location of the master sword
auto masterSwordLocation = FilterFromPool(allLocations, [](const LocationKey loc){return Location(loc)->GetPlacedItemKey() == MASTER_SWORD;});

// Add second text box
ganonHintText = ganonHintText + "^";
if (masterSwordLocation.empty()) {
ganonHintText = ganonHintText+Hint(MASTER_SWORD_LOCATION_HINT).GetText()+Hint(YOUR_POCKET).GetText();
masterSwordHintLoc = "Link's Pocket";
} else {
ganonHintText = ganonHintText+Hint(MASTER_SWORD_LOCATION_HINT).GetText()+GetHintRegion(Location(masterSwordLocation[0])->GetParentRegionKey())->GetHint().GetText();
masterSwordHintLoc = Location(masterSwordLocation[0])->GetName();
}
ganonHintText = ganonHintText + "!";
}

CreateMessageFromTextObject(0x70CC, 0, 2, 3, AddColorsAndFormat(ganonHintText));
}

Expand Down Expand Up @@ -865,6 +888,18 @@ void CreateSheikText() {
};
Text temp2 = Text{"%w.", "%w.", "%w."};
sheikText = temp1 + area + temp2;
if (ShuffleMasterSword) {
sheikText = sheikText + "^";
auto masterSwordLocation = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == MASTER_SWORD;});
masterSwordHintLoc = Location(masterSwordLocation[0])->GetName();
area = GetHintRegion(Location(masterSwordLocation[0])->GetParentRegionKey())->GetHint().GetText();
Text temp3 = Text{
"I also heard that he stole %rthe Master Sword%w and hid it somewhere within %g",
"Test",
"Test"
};
sheikText = sheikText + temp3 + area + temp2;
}
}

void CreateSariaText() {
Expand Down
6 changes: 6 additions & 0 deletions soh/soh/Enhancements/randomizer/3drando/hints.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum class HintCategory {
Altar,
Validation,
LightArrow,
MasterSword,
GanonLine,
MerchantsDialogs,
};
Expand Down Expand Up @@ -116,6 +117,10 @@ class HintText {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::LightArrow};
}

static auto MasterSword(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::MasterSword};
}

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};
}
Expand Down Expand Up @@ -228,5 +233,6 @@ Text& GetWarpNocturneText();
Text& GetWarpPreludeText();

std::string GetDampeHintLoc();
std::string GetMasterSwordHintLoc();
std::string GetLightArrowHintLoc();
std::string GetSariaHintLoc();
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/randomizer/3drando/item_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ static std::array<Item, KEY_ENUM_MAX> itemTable;
void ItemTable_Init() { // RandomizerGet English name French Spanish Item Type getItemID advancement logic hint key
itemTable[NONE] = Item(RG_NONE, Text{"No Item", "Rien", "Sin Objeto"}, ITEMTYPE_EVENT, GI_RUPEE_GREEN, false, &noVariable, NONE);
itemTable[KOKIRI_SWORD] = Item(RG_KOKIRI_SWORD, Text{"Kokiri Sword", "Épée Kokiri", "Espada Kokiri"}, ITEMTYPE_ITEM, GI_SWORD_KOKIRI, true, &KokiriSword, KOKIRI_SWORD);
//[MASTER_SWORD]
itemTable[MASTER_SWORD] = Item(RG_MASTER_SWORD, Text{"Master Sword", "Épée de Legende", "Espada Master"}, ITEMTYPE_ITEM, 0x73, true, &MasterSword, MASTER_SWORD);
itemTable[GIANTS_KNIFE] = Item(RG_GIANTS_KNIFE, Text{"Giant's Knife", "Lame des Géants", "Espada de Biggoron"}, ITEMTYPE_ITEM, GI_SWORD_KNIFE, false, &noVariable, GIANTS_KNIFE);
itemTable[BIGGORON_SWORD] = Item(RG_BIGGORON_SWORD, Text{"Biggoron's Sword", "Épée de Biggoron", "Espada de Biggoron"}, ITEMTYPE_ITEM, GI_SWORD_BGS, true, &BiggoronSword, BIGGORON_SWORD);
itemTable[DEKU_SHIELD] = Item(RG_DEKU_SHIELD, Text{"Deku Shield", "Bouclier Mojo", "Escudo deku"}, ITEMTYPE_ITEM, GI_SHIELD_DEKU, false, &noVariable, DEKU_SHIELD);
Expand Down
3 changes: 2 additions & 1 deletion soh/soh/Enhancements/randomizer/3drando/item_location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ void LocationTable_Init() {
/*-------------------------------
--- CUTSCENES ---
-------------------------------*/

locationTable[TOT_MASTER_SWORD] = ItemLocation::Delayed(RC_TOT_MASTER_SWORD, 0xFF, "ToT Master Sword", TOT_MASTER_SWORD, MASTER_SWORD, {}, SpoilerCollectionCheck::MasterSword(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[TOT_LIGHT_ARROWS_CUTSCENE] = ItemLocation::Delayed(RC_TOT_LIGHT_ARROWS_CUTSCENE, 0xFF, "ToT Light Arrow Cutscene", TOT_LIGHT_ARROWS_CUTSCENE, LIGHT_ARROWS, {}, SpoilerCollectionCheck::Chest(0x43, 0x1E), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
locationTable[LW_GIFT_FROM_SARIA] = ItemLocation::Delayed(RC_LW_GIFT_FROM_SARIA, 0xFF, "LW Gift From Saria", LW_GIFT_FROM_SARIA, PROGRESSIVE_OCARINA, {}, SpoilerCollectionCheck::EventChkInf(0xC1), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
locationTable[ZF_GREAT_FAIRY_REWARD] = ItemLocation::Delayed(RC_ZF_GREAT_FAIRY_REWARD, 0xFF, "ZF Great Fairy Reward", ZF_GREAT_FAIRY_REWARD, FARORES_WIND, {}, SpoilerCollectionCheck::Chest(0x3D, 0x01), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN);
Expand Down Expand Up @@ -1265,6 +1265,7 @@ std::vector<uint32_t> overworldLocations = {
OGC_GREAT_FAIRY_REWARD,

//Temple of Time
TOT_MASTER_SWORD,
SHEIK_AT_TEMPLE,
TOT_LIGHT_ARROWS_CUTSCENE,

Expand Down
5 changes: 5 additions & 0 deletions soh/soh/Enhancements/randomizer/3drando/item_location.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,14 @@ class SpoilerCollectionCheck {
static auto MagicBeans() {
return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS, 0x00, 0x00);
}

static auto MasterSword() {
return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD, 0x00, 0x00);
}

static auto Merchant() {
return SpoilerCollectionCheck(SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT, 0x00, 0x00);

}

static auto RandomizerInf() {
Expand Down
14 changes: 13 additions & 1 deletion soh/soh/Enhancements/randomizer/3drando/item_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ const std::array<uint32_t, 59> alwaysItems = {
ARROWS_10,
TREASURE_GAME_HEART,
};
const std::array<uint32_t, 43> easyItems = {
const std::array<uint32_t, 44> easyItems = {
BIGGORON_SWORD,
KOKIRI_SWORD,
MASTER_SWORD,
BOOMERANG,
LENS_OF_TRUTH,
MEGATON_HAMMER,
Expand Down Expand Up @@ -682,6 +683,13 @@ void GenerateItemPool() {
PlaceItemInLocation(KF_KOKIRI_SWORD_CHEST, KOKIRI_SWORD, false, true);
}

if (ShuffleMasterSword) {
AddItemToMainPool(MASTER_SWORD);
IceTrapModels.push_back(0xE0); //Master Sword without the GI enum
} else {
PlaceItemInLocation(TOT_MASTER_SWORD, MASTER_SWORD, false, true);
}

if (ShuffleWeirdEgg) {
AddItemToMainPool(WEIRD_EGG);
IceTrapModels.push_back(GI_WEIRD_EGG);
Expand Down Expand Up @@ -1161,6 +1169,10 @@ void GenerateItemPool() {
ReplaceMaxItem(KOKIRI_SWORD, 0);
}

if (!ShuffleMasterSword) {
ReplaceMaxItem(MASTER_SWORD, 0);
}

if (ProgressiveGoronSword) {
ReplaceMaxItem(BIGGORON_SWORD, 0);
AddItemToMainPool(PROGRESSIVE_GORONSWORD, 2);
Expand Down
2 changes: 2 additions & 0 deletions soh/soh/Enhancements/randomizer/3drando/keys.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ typedef enum {
MARKET_BOMBCHU_SHOP_ITEM_6,
MARKET_BOMBCHU_SHOP_ITEM_7,
MARKET_BOMBCHU_SHOP_ITEM_8,
TOT_MASTER_SWORD,
TOT_LIGHT_ARROWS_CUTSCENE,
//HYRULE_CASTLE
HC_MALON_EGG,
Expand Down Expand Up @@ -1804,6 +1805,7 @@ typedef enum {

VALIDATION_LINE,
LIGHT_ARROW_LOCATION_HINT,
MASTER_SWORD_LOCATION_HINT,
DAMPE_DIARY,
YOUR_POCKET,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ void AreaTable_Init_CastleTown() {

areaTable[TEMPLE_OF_TIME] = Area("Temple of Time", "Temple of Time", TEMPLE_OF_TIME, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LocationAccess(TOT_MASTER_SWORD, {[]{return IsAdult;}}),
LocationAccess(TOT_LIGHT_ARROWS_CUTSCENE, {[]{return IsAdult && CanTriggerLACS;}}),
}, {
//Exits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void AreaTable_Init_DeathMountain() {
LocationAccess(GC_ROLLING_GORON_AS_CHILD, {[]{return IsChild && (HasExplosives || (GoronBracelet && LogicChildRollingWithStrength));}}),
LocationAccess(GC_ROLLING_GORON_AS_ADULT, {[]{return StopGCRollingGoronAsAdult;}}),
LocationAccess(GC_GS_BOULDER_MAZE, {[]{return IsChild && CanBlastOrSmash;}}),
LocationAccess(GC_GS_CENTER_PLATFORM, {[]{return IsAdult;}}),
LocationAccess(GC_GS_CENTER_PLATFORM, {[]{return CanAdultAttack;}}),
LocationAccess(GC_MEDIGORON, {[]{return IsAdult && AdultsWallet && (CanBlastOrSmash || GoronBracelet);}}),
LocationAccess(GC_MAZE_GOSSIP_STONE, {[]{return CanBlastOrSmash || CanUse(SILVER_GAUNTLETS);}}),
LocationAccess(GC_MEDIGORON_GOSSIP_STONE, {[]{return CanBlastOrSmash || GoronBracelet;}}),
Expand Down
Loading

0 comments on commit 2eaed8d

Please sign in to comment.