diff options
| author | Caladius <Caladius@users.noreply.github.com> | 2025-10-15 12:48:06 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-15 12:48:06 -0400 |
| commit | c95a7739f92465d3fc2e1a4eca2912ec47c2ed3a (patch) | |
| tree | 89193494f4c3b63a309b71fddfb7bede35c802f8 | |
| parent | da767c966d1102fa2dfe8f78627400ab23f4634c (diff) | |
[Rando] Implement Triforce Hunt (#1071)
* Implement Triforce Hunt (needs Glitchless Seed Gen help)
* Fix Credits Warping
* Clean up logging
* Fix Seed Gen
* Remove test code.
* Add Triforce Piece to item tracker
* Allow for multiple models, pushing up because I dislike it and can't figure out a better way.
* Majora Soul for Reward (needs work). Completed Triforce text
* Clang
* clean up comment
* Remove SPDLOG
* Updates based on Feedback
* Cleanup
* Cleanup again
* Fix Ship Utils
* Fix Tracker drawing
* Feedback updates
* clang because why not
* Update to use ENTRANCE mapping. Thanks BalloonDude
* Expand shuffle maximum and handle instances where a seed may not generate.
* Undo menu text.
* Fix Item Tracker... again and feedback changes.
* Update define location to StaticData.h and update majora syntax
* cleanup
* Remove TriforceHunt.cpp again...
* clean up
* FOR REAL THIS TIME
* Update ratio of required/max if the seed needs to adjust how many are in the pool due to generator issues.
* clang
| -rw-r--r-- | mm/2s2h/BenJsonConversions.hpp | 2 | ||||
| -rw-r--r-- | mm/2s2h/DeveloperTools/SaveEditor.cpp | 3 | ||||
| -rw-r--r-- | mm/2s2h/Enhancements/Trackers/ItemTracker.cpp | 25 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/Souls.cpp | 15 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ConvertItem.cpp | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/DrawItem.cpp | 33 | ||||
| -rw-r--r-- | mm/2s2h/Rando/GiveItem.cpp | 16 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Menu.cpp | 27 | ||||
| -rw-r--r-- | mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp | 8 | ||||
| -rw-r--r-- | mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp | 50 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Items.cpp | 2 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Options.cpp | 3 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/StaticData.h | 2 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Types.h | 5 | ||||
| -rw-r--r-- | mm/2s2h/ShipUtils.cpp | 3 | ||||
| -rw-r--r-- | mm/assets/2s2h_assets.h | 12 | ||||
| -rw-r--r-- | mm/include/z64save.h | 1 |
17 files changed, 203 insertions, 5 deletions
diff --git a/mm/2s2h/BenJsonConversions.hpp b/mm/2s2h/BenJsonConversions.hpp index 3bed702fa..8dc1fa7bf 100644 --- a/mm/2s2h/BenJsonConversions.hpp +++ b/mm/2s2h/BenJsonConversions.hpp @@ -56,6 +56,7 @@ void to_json(json& j, const RandoSaveInfo& rando) { { "finalSeed", rando.finalSeed }, { "randoSaveOptions", rando.randoSaveOptions }, { "foundDungeonKeys", rando.foundDungeonKeys }, + { "foundTriforcePieces", rando.foundTriforcePieces }, }; } @@ -66,6 +67,7 @@ void from_json(const json& j, RandoSaveInfo& rando) { j.at("finalSeed").get_to(rando.finalSeed); j.at("randoSaveOptions").get_to(rando.randoSaveOptions); j.at("foundDungeonKeys").get_to(rando.foundDungeonKeys); + j.at("foundTriforcePieces").get_to(rando.foundTriforcePieces); } void to_json(json& j, const ShipSaveInfo& shipSaveInfo) { diff --git a/mm/2s2h/DeveloperTools/SaveEditor.cpp b/mm/2s2h/DeveloperTools/SaveEditor.cpp index 7d8a808b7..945c0af3d 100644 --- a/mm/2s2h/DeveloperTools/SaveEditor.cpp +++ b/mm/2s2h/DeveloperTools/SaveEditor.cpp @@ -902,6 +902,9 @@ void DrawItemsAndMasksTab() { if (!riFilter.PassFilter(randoStaticItem.name)) { continue; } + if (randoItemId == RI_TRIFORCE_PIECE_PREVIOUS) { + continue; + } std::string buttonLabel = "Give "; buttonLabel += randoStaticItem.name; diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp b/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp index 84970bc38..e64be32c8 100644 --- a/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp @@ -3,6 +3,7 @@ #include "Context.h" #include "config/Config.h" #include <bit> +#include "Rando/Rando.h" extern "C" { #include "z64save.h" @@ -12,6 +13,7 @@ extern "C" { #include "assets/interface/icon_item_dungeon_static/icon_item_dungeon_static.h" #include "assets/interface/icon_item_field_static/icon_item_field_static.h" #include "assets/interface/parameter_static/parameter_static.h" +#include "2s2h_assets.h" } #define CFG_TRACKER_ITEM(var) ("ItemTracker." var) @@ -29,6 +31,7 @@ typedef enum { TRACKER_ITEM_KEY_SNOWHEAD, TRACKER_ITEM_KEY_GREAT_BAY, TRACKER_ITEM_KEY_STONE_TONER, + TRACKER_ITEM_TRIFORCE_PIECES, } ItemTrackerItems; using namespace Ship; @@ -351,6 +354,7 @@ bool ItemTrackerWindow::HasItemCount(int itemId) { case TRACKER_ITEM_KEY_SNOWHEAD: case TRACKER_ITEM_KEY_GREAT_BAY: case TRACKER_ITEM_KEY_STONE_TONER: + case TRACKER_ITEM_TRIFORCE_PIECES: return true; default: return false; @@ -416,6 +420,17 @@ ItemTrackerWindow::CountInfo ItemTrackerWindow::GetItemCountInfo(int itemId) { .maxCap = sSmallKeyCounts[itemId - TRACKER_ITEM_KEY_WOODFALL + DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE], }; break; + case TRACKER_ITEM_TRIFORCE_PIECES: { + u32 triforcePieceCount = gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces; + info = { + .cur = (uint16_t)triforcePieceCount, + .curCap = + (uint16_t)RANDO_SAVE_OPTIONS[Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].randoOptionId], + .maxCap = + (uint16_t)RANDO_SAVE_OPTIONS[Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].randoOptionId], + }; + break; + } default: info = { 0 }; } @@ -585,6 +600,14 @@ int ItemTrackerWindow::DrawMisc(int columns, int prevDrawnColumns) { DrawOwlFace(gSaveContext.save.saveInfo.playerData.owlActivationFlags == 0); DrawItemCount(TRACKER_ITEM_OWL_ACTIVATIONS, pos); ImGui::EndGroup(); + pos = ImVec2((0 * (mIconSize + mIconSpacing) + 8.0f), + ((prevDrawnColumns + 1) * (mIconSize + mIconSpacing)) + 8.0f + topPadding); + ImGui::SetCursorPos(pos); + ImGui::BeginGroup(); + DrawItem((char*)gTriforcePieceTex, + RANDO_SAVE_OPTIONS[Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].randoOptionId] == 0, mIconSize); + DrawItemCount(TRACKER_ITEM_TRIFORCE_PIECES, pos); + ImGui::EndGroup(); // TODO: Heart counts once we have extra save stats // pos = ImVec2((2 * (mIconSize + mIconSpacing) + 8.0f), @@ -597,7 +620,7 @@ int ItemTrackerWindow::DrawMisc(int columns, int prevDrawnColumns) { // ImGui::SetCursorPos(pos); // DrawItem(const_cast<char*>(gQuestIconPieceOfHeartTex), false, mIconSize); - return 1; + return 2; } static int RoundDown(int orig, int nearest) { diff --git a/mm/2s2h/Rando/ActorBehavior/Souls.cpp b/mm/2s2h/Rando/ActorBehavior/Souls.cpp index ff2604bd1..435a1cbe8 100644 --- a/mm/2s2h/Rando/ActorBehavior/Souls.cpp +++ b/mm/2s2h/Rando/ActorBehavior/Souls.cpp @@ -10,6 +10,17 @@ extern "C" { void BossHakugin_DrawIce(BossHakugin*, PlayState*); } +bool shouldMajoraRegister() { + bool registerStatus = false; + if (IS_RANDO) { + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_YES || + RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { + registerStatus = true; + } + } + return registerStatus; +} + void ShouldActorUpdate(Actor* actor, bool* should, RandoInf randoInf) { if (!Flags_GetRandoInf(randoInf)) { *should = false; @@ -44,7 +55,7 @@ void Rando::ActorBehavior::InitSoulsBehavior() { COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_03, shouldRegister, [](Actor* actor, bool* should) { ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_GYORG); }); - COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_07, shouldRegister, + COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_07, shouldMajoraRegister(), [](Actor* actor, bool* should) { ShouldActorDraw(actor, should, RANDO_INF_OBTAINED_SOUL_OF_MAJORA); }); COND_ID_HOOK(ShouldActorDraw, ACTOR_BOSS_01, shouldRegister, @@ -58,7 +69,7 @@ void Rando::ActorBehavior::InitSoulsBehavior() { ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_GYORG); }); - COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_07, shouldRegister, [](Actor* actor, bool* should) { + COND_ID_HOOK(ShouldActorUpdate, ACTOR_BOSS_07, shouldMajoraRegister(), [](Actor* actor, bool* should) { ShouldActorUpdate(actor, should, RANDO_INF_OBTAINED_SOUL_OF_MAJORA); }); diff --git a/mm/2s2h/Rando/ConvertItem.cpp b/mm/2s2h/Rando/ConvertItem.cpp index f2d7142fb..26ba83a94 100644 --- a/mm/2s2h/Rando/ConvertItem.cpp +++ b/mm/2s2h/Rando/ConvertItem.cpp @@ -239,6 +239,7 @@ bool Rando::IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId) case RI_FROG_PINK: case RI_FROG_WHITE: case RI_ABILITY_SWIM: + case RI_TRIFORCE_PIECE: if (hasObtainedCheck) { return false; } diff --git a/mm/2s2h/Rando/DrawItem.cpp b/mm/2s2h/Rando/DrawItem.cpp index 5fcfd13f5..54ccd568b 100644 --- a/mm/2s2h/Rando/DrawItem.cpp +++ b/mm/2s2h/Rando/DrawItem.cpp @@ -297,6 +297,35 @@ void DrawSkulltulaToken(RandoItemId randoItemId, Actor* actor) { CLOSE_DISPS(gPlayState->state.gfxCtx); } +void DrawTriforcePiece(RandoItemId randoItemId) { + Gfx* triforcePieceModels[3] = { + (Gfx*)gTriforcePiece0DL, + (Gfx*)gTriforcePiece1DL, + (Gfx*)gTriforcePiece2DL, + }; + + u8 currentTriforcePieces = gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces; + + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL25_Xlu(gPlayState->state.gfxCtx); + + Matrix_Scale(0.03f, 0.03f, 0.03f, MTXMODE_APPLY); + + MATRIX_FINALIZE_AND_LOAD(POLY_XLU_DISP++, gPlayState->state.gfxCtx); + if (currentTriforcePieces >= RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED]) { + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gTriforcePieceCompletedDL); + } else { + if (randoItemId == RI_TRIFORCE_PIECE_PREVIOUS) { + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)triforcePieceModels[(currentTriforcePieces - 1) % 3]); + } else { + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)triforcePieceModels[currentTriforcePieces % 3]); + } + } + + CLOSE_DISPS(gPlayState->state.gfxCtx); +} + void DrawAbilityItem(RandoItemId randoItemId, Actor* actor) { Gfx* abilityItemModel[1] = { (Gfx*)gGiFlippersDL, @@ -440,6 +469,10 @@ void Rando::DrawItem(RandoItemId randoItemId, Actor* actor) { case RI_ABILITY_SWIM: DrawAbilityItem(randoItemId, actor); break; + case RI_TRIFORCE_PIECE_PREVIOUS: + case RI_TRIFORCE_PIECE: + DrawTriforcePiece(randoItemId); + break; case RI_NONE: case RI_UNKNOWN: break; diff --git a/mm/2s2h/Rando/GiveItem.cpp b/mm/2s2h/Rando/GiveItem.cpp index 44863637d..28a9b1819 100644 --- a/mm/2s2h/Rando/GiveItem.cpp +++ b/mm/2s2h/Rando/GiveItem.cpp @@ -103,6 +103,22 @@ void Rando::GiveItem(RandoItemId randoItemId) { gSaveContext.save.shipSaveInfo.rando.foundDungeonKeys[DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE]++; } break; + case RI_TRIFORCE_PIECE: + case RI_TRIFORCE_PIECE_PREVIOUS: + gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces++; + if (gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces == + RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED]) { + // Blocks the ability to beat the game through killing Majora until all Triforce Pieces are found. + if (!Flags_GetRandoInf(RANDO_INF_OBTAINED_SOUL_OF_MAJORA)) { + Rando::GiveItem(RI_SOUL_MAJORA); + } + GameInteractor::Instance->events.emplace_back( + GIEventTransition{ .entrance = ENTRANCE(TERMINA_FIELD, 0), + .cutsceneIndex = 0xFFF7, + .transitionTrigger = TRANS_TRIGGER_START, + .transitionType = TRANS_TYPE_FADE_BLACK }); + } + break; // Technically these should never be used, but leaving them here just in case case RI_PROGRESSIVE_MAGIC: case RI_PROGRESSIVE_BOW: diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index e7fa06f78..09c946c00 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -262,7 +262,32 @@ static void DrawShufflesTab() { CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); ImGui::SameLine(); - ImGui::BeginChild("randoShufflesColumn3", ImVec2(columnWidth, halfHeight)); + ImGui::BeginChild("randoLocationsColumn3", ImVec2(columnWidth, halfHeight)); + CVarCheckbox("Triforce Hunt", Rando::StaticData::Options[RO_SHUFFLE_TRIFORCE_PIECES].cvar); + ImGui::BeginDisabled(!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRIFORCE_PIECES].cvar, RO_GENERIC_OFF)); + CVarSliderInt( + "Required Triforce Pieces", Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].cvar, + IntSliderOptions({}) + .Min(1) + .Max(CVarGetInteger(Rando::StaticData::Options[RO_TRIFORCE_PIECES_MAX].cvar, DEFAULT_TRIFORCE_PIECES_MAX)) + .DefaultValue(DEFAULT_TRIFORCE_PIECES_MAX)); + if (CVarSliderInt( + "Shuffled Triforce Pieces", Rando::StaticData::Options[RO_TRIFORCE_PIECES_MAX].cvar, + IntSliderOptions({}) + .Min(1) + .Max(1000) + .DefaultValue(DEFAULT_TRIFORCE_PIECES_MAX) + .Tooltip("If the maximum amount of placeable pieces exceeds what will allow the seed to generate, the " + "amount will be adjusted automatically."))) { + if (CVarGetInteger(Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].cvar, DEFAULT_TRIFORCE_PIECES_MAX) > + CVarGetInteger(Rando::StaticData::Options[RO_TRIFORCE_PIECES_MAX].cvar, DEFAULT_TRIFORCE_PIECES_MAX)) { + CVarGetInteger( + Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].cvar, + CVarGetInteger(Rando::StaticData::Options[RO_TRIFORCE_PIECES_MAX].cvar, DEFAULT_TRIFORCE_PIECES_MAX)); + } + } + + ImGui::EndDisabled(); ImGui::EndChild(); } diff --git a/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp b/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp index a9db0433f..bef7eb663 100644 --- a/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp +++ b/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp @@ -49,6 +49,14 @@ void Rando::MiscBehavior::CheckQueue() { if (randoItemId == RI_JUNK) { randoItemId = Rando::CurrentJunkItem(); } + if (randoItemId == RI_TRIFORCE_PIECE) { + if (gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces + 1 >= + RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED]) { + prefix = "You"; + message = "completed the Triforce"; + } + randoItemId = RI_TRIFORCE_PIECE_PREVIOUS; + } CustomMessage::Entry entry = { .textboxType = 2, diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index 93b9621bd..21dd965e6 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -64,6 +64,11 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { gSaveContext.save.saveInfo.playerData.health = RANDO_SAVE_OPTIONS[RO_STARTING_HEALTH] * 0x10; } + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] != RO_GENERIC_OFF) { + RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED] = CVarGetInteger( + Rando::StaticData::Options[RO_TRIFORCE_PIECES_REQUIRED].cvar, DEFAULT_TRIFORCE_PIECES_MAX); + } + if (RANDO_SAVE_OPTIONS[RO_STARTING_CONSUMABLES]) { GiveItem(RI_DEKU_STICK); GiveItem(RI_DEKU_NUT); @@ -241,6 +246,9 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { // Add other items that don't have a vanilla location like Sun's Song or Song of Double Time if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_BOSS_SOULS] == RO_GENERIC_YES) { for (int i = RI_SOUL_GOHT; i <= RI_SOUL_TWINMOLD; i++) { + if (i == RI_SOUL_MAJORA && RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { + continue; + } itemPool.push_back((RandoItemId)i); } } @@ -257,6 +265,20 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { } } + // Shuffle Triforce Pieces into the Pool + int piecesShuffled = 0; + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { + int piecesToShuffle = RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_MAX]; + for (auto& item : itemPool) { + if (piecesToShuffle == 0) { + break; + } + itemPool.push_back(RI_TRIFORCE_PIECE); + piecesToShuffle--; + piecesShuffled++; + } + } + if (RANDO_SAVE_OPTIONS[RO_PLENTIFUL_ITEMS] == RO_GENERIC_YES) { int replaceableItems = 0; std::vector<RandoItemId> plentifulItems; @@ -347,12 +369,40 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { continue; } + // If Triforce Hunt is enabled, removed pieces as a last resort + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { + bool removedTriforcePiece = false; + for (int i = 0; i < itemPool.size(); i++) { + if (Rando::StaticData::Items[itemPool[i]].randoItemId == RI_TRIFORCE_PIECE) { + itemPool.erase(itemPool.begin() + i); + removedTriforcePiece = true; + piecesShuffled--; + break; + } + } + + if (removedTriforcePiece) { + continue; + } + } + SPDLOG_ERROR("Could not match item pool size to check pool size {}/{}", itemPool.size(), checkPool.size()); throw std::runtime_error("Could not match item pool size to check pool size"); } } + // Update Required Triforce Pieces if piecesShuffled falls below max shuffled + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRIFORCE_PIECES] == RO_GENERIC_YES) { + if (piecesShuffled != RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_MAX]) { + float currentRatio = ((float)RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED] / + (float)RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_MAX]); + + RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_MAX] = piecesShuffled; + RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED] = (piecesShuffled * currentRatio) + 1; + } + } + // Grant the starting items for (RandoItemId startingItem : startingItems) { GiveItem(ConvertItem(startingItem)); diff --git a/mm/2s2h/Rando/StaticData/Items.cpp b/mm/2s2h/Rando/StaticData/Items.cpp index a2a127a97..b8a8064c0 100644 --- a/mm/2s2h/Rando/StaticData/Items.cpp +++ b/mm/2s2h/Rando/StaticData/Items.cpp @@ -186,6 +186,8 @@ std::map<RandoItemId, RandoStaticItem> Items = { RI(RI_TINGLE_MAP_SNOWHEAD, "", "Tingle's Snowhead Map", RITYPE_LESSER, ITEM_TINGLE_MAP, GI_TINGLE_MAP_SNOWHEAD, GID_TINGLE_MAP), RI(RI_TINGLE_MAP_STONE_TOWER, "", "Tingle's Stone Tower Map", RITYPE_LESSER, ITEM_TINGLE_MAP, GI_TINGLE_MAP_STONE_TOWER, GID_TINGLE_MAP), RI(RI_TINGLE_MAP_WOODFALL, "", "Tingle's Woodfall Map", RITYPE_LESSER, ITEM_TINGLE_MAP, GI_TINGLE_MAP_WOODFALL, GID_TINGLE_MAP), + RI(RI_TRIFORCE_PIECE, "a", "Piece of the Triforce", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), + RI(RI_TRIFORCE_PIECE_PREVIOUS, "a", "Piece of the Triforce", RITYPE_MAJOR, ITEM_NONE, GI_NONE, GID_NONE), // This only exists to aid in the drawing of unique models, it has no use outside of that. RI(RI_WALLET_ADULT, "the", "Adult's Wallet", RITYPE_MAJOR, ITEM_WALLET_ADULT, GI_WALLET_ADULT, GID_WALLET_ADULT), RI(RI_WALLET_GIANT, "the", "Giant's Wallet", RITYPE_LESSER, ITEM_WALLET_GIANT, GI_WALLET_GIANT, GID_WALLET_GIANT), RI(RI_WOODFALL_BOSS_KEY, "the", "Woodfall Boss Key", RITYPE_BOSS_KEY, ITEM_KEY_BOSS, GI_KEY_BOSS, GID_KEY_BOSS), diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index 38a35e459..b8e695653 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -41,6 +41,7 @@ std::map<RandoOptionId, RandoStaticOption> Options = { RO(RO_SHUFFLE_SNOWBALL_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_SWIM, RO_GENERIC_OFF), RO(RO_SHUFFLE_TINGLE_SHOPS, RO_GENERIC_OFF), + RO(RO_SHUFFLE_TRIFORCE_PIECES, RO_GENERIC_OFF), RO(RO_STARTING_CONSUMABLES, RO_GENERIC_OFF), RO(RO_STARTING_HEALTH, 3), RO(RO_STARTING_ITEMS_1, 0), @@ -48,6 +49,8 @@ std::map<RandoOptionId, RandoStaticOption> Options = { RO(RO_STARTING_ITEMS_3, 2048), RO(RO_STARTING_MAPS_AND_COMPASSES, RO_GENERIC_OFF), RO(RO_STARTING_RUPEES, RO_GENERIC_OFF), + RO(RO_TRIFORCE_PIECES_MAX, DEFAULT_TRIFORCE_PIECES_MAX), + RO(RO_TRIFORCE_PIECES_REQUIRED, DEFAULT_TRIFORCE_PIECES_MAX), }; // clang-format on diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index 9a5a0d2c0..392651b9c 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -10,6 +10,8 @@ extern "C" { #include "z64scene.h" } +#define DEFAULT_TRIFORCE_PIECES_MAX 15 + namespace Rando { namespace StaticData { diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index ef9725be7..8e1f9c064 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -2417,6 +2417,8 @@ typedef enum { RI_TINGLE_MAP_SNOWHEAD, RI_TINGLE_MAP_STONE_TOWER, RI_TINGLE_MAP_WOODFALL, + RI_TRIFORCE_PIECE, + RI_TRIFORCE_PIECE_PREVIOUS, RI_WALLET_ADULT, RI_WALLET_GIANT, RI_WOODFALL_BOSS_KEY, @@ -2762,6 +2764,7 @@ typedef enum { RO_SHUFFLE_SNOWBALL_DROPS, RO_SHUFFLE_SWIM, RO_SHUFFLE_TINGLE_SHOPS, + RO_SHUFFLE_TRIFORCE_PIECES, RO_STARTING_CONSUMABLES, RO_STARTING_HEALTH, RO_STARTING_ITEMS_1, @@ -2769,6 +2772,8 @@ typedef enum { RO_STARTING_ITEMS_3, RO_STARTING_MAPS_AND_COMPASSES, RO_STARTING_RUPEES, + RO_TRIFORCE_PIECES_MAX, + RO_TRIFORCE_PIECES_REQUIRED, RO_MAX, } RandoOptionId; diff --git a/mm/2s2h/ShipUtils.cpp b/mm/2s2h/ShipUtils.cpp index 2edb19927..3d5193cb4 100644 --- a/mm/2s2h/ShipUtils.cpp +++ b/mm/2s2h/ShipUtils.cpp @@ -45,7 +45,7 @@ std::unordered_map<s16, const char*> sceneNames = { #undef DEFINE_SCENE_UNSET // These textures are not in existing lists that we iterate over. -std::array<const char*, 21> miscellaneousTextures = { +std::array<const char*, 22> miscellaneousTextures = { gArcheryScoreIconTex, gBarrelTrackerIcon, gChestTrackerIcon, @@ -63,6 +63,7 @@ std::array<const char*, 21> miscellaneousTextures = { gStrayFairyStoneTowerIconTex, gStrayFairyWoodfallIconTex, gTimerClockIconTex, + gTriforcePieceTex, gWorldMapOwlFaceTex, gameplay_keep_Tex_053140, gDungeonMapSkullTex, diff --git a/mm/assets/2s2h_assets.h b/mm/assets/2s2h_assets.h index 5581dad87..0ddb4497e 100644 --- a/mm/assets/2s2h_assets.h +++ b/mm/assets/2s2h_assets.h @@ -373,3 +373,15 @@ static const ALIGN_ASSET(2) char gBarrelTrackerIcon[] = dgBarrelTrackerIcon; #define dgGiFlippersDL "__OTR__objects/object_ability_swim/gGiFlippersDL" static const ALIGN_ASSET(2) char gGiFlippersDL[] = dgGiFlippersDL; + +#define dgTriforcePiece0DL "__OTR__objects/object_triforce_piece_0/gTriforcePiece0DL" +static const ALIGN_ASSET(2) char gTriforcePiece0DL[] = dgTriforcePiece0DL; + +#define dgTriforcePiece1DL "__OTR__objects/object_triforce_piece_1/gTriforcePiece1DL" +static const ALIGN_ASSET(2) char gTriforcePiece1DL[] = dgTriforcePiece1DL; + +#define dgTriforcePiece2DL "__OTR__objects/object_triforce_piece_2/gTriforcePiece2DL" +static const ALIGN_ASSET(2) char gTriforcePiece2DL[] = dgTriforcePiece2DL; + +#define dgTriforcePieceCompletedDL "__OTR__objects/object_triforce_completed/gTriforcePieceCompletedDL" +static const ALIGN_ASSET(2) char gTriforcePieceCompletedDL[] = dgTriforcePieceCompletedDL; diff --git a/mm/include/z64save.h b/mm/include/z64save.h index debcfe010..b2c14f752 100644 --- a/mm/include/z64save.h +++ b/mm/include/z64save.h @@ -386,6 +386,7 @@ typedef struct RandoSaveInfo { u32 finalSeed; u32 randoSaveOptions[RO_MAX]; // Type here may change in the future s8 foundDungeonKeys[9]; // Tracks the number of dungeon keys found, opposed to the number of keys in the inventory + u8 foundTriforcePieces; } RandoSaveInfo; // These are values added by 2S2H that we need to be persisted to the save file |
