From bae6cf420374a2ab15059e359e57f5de43c9776c Mon Sep 17 00:00:00 2001 From: aMannus Date: Tue, 26 Sep 2023 15:45:37 +0200 Subject: [PATCH] Randomizer feature: Triforce Hunt (#3062) * Initial work to make triforce pieces their own rando item * Disable triforce greyscaling * Better triforce model, finish adding triforce pieces to logic * Triforce model is now a shard * Credits warp + start of item tracker * Initial item tracker stuff * Completed triangle on triforce completion * Completed triforce model on GI done * Multiple triforce piece models * Triforce pieces in save editor & fix build * Finish item tracker * Gameplaystats timestamp * Revert parts of logic * More reverting * Start of making Triforce Hunt the win condition * Bit of cleanup * Triforce pieces can show up as icetraps * Grant GBK to player after hunt is completed * Better text boxes * Disable GBK option in ImGui with Triforce Hunt on * Clean-up * Forced save on completion improvements * Update Item Tracker Settings initial size * Small ImGui adjustments * French translation and update defaults * Finish translations * Fix timer completion & 50+ triforce pieces * Remove GI_ and ITEM_ enum usage, add french ice trap names * Fix build & small fixes * Review comments * Comment clarification --- .../gTriforcePieceCompletedDL | 13 ++ .../gTriforcePieceCompletedDL_tri_0 | 58 ++++++++ .../gTriforcePieceCompletedDL_tri_1 | 7 + .../gTriforcePieceCompletedDL_vtx_0 | 54 ++++++++ .../gTriforcePieceCompletedDL_vtx_1 | 8 ++ ...rcePieceCompletedDL_f3dlite_triforce_edges | 21 +++ ...ePieceCompletedDL_f3dlite_triforce_surface | 21 +++ .../object_triforce_completed/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_0/gTriforcePiece0DL | 15 +++ .../gTriforcePiece0DL_tri_0 | 17 +++ .../gTriforcePiece0DL_tri_1 | 18 +++ .../gTriforcePiece0DL_tri_2 | 51 +++++++ .../gTriforcePiece0DL_vtx_0 | 18 +++ .../gTriforcePiece0DL_vtx_1 | 22 ++++ .../gTriforcePiece0DL_vtx_2 | 49 +++++++ .../mat_gTriforcePiece0DL_f3dlite_shard_edge | 21 +++ ...t_gTriforcePiece0DL_f3dlite_triforce_edges | 21 +++ ...gTriforcePiece0DL_f3dlite_triforce_surface | 21 +++ .../objects/object_triforce_piece_0/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_1/gTriforcePiece1DL | 13 ++ .../gTriforcePiece1DL_tri_0 | 20 +++ .../gTriforcePiece1DL_tri_1 | 25 ++++ .../gTriforcePiece1DL_vtx_0 | 23 ++++ .../gTriforcePiece1DL_vtx_1 | 34 +++++ .../mat_gTriforcePiece1DL_f3dlite_shard_edge | 21 +++ ...gTriforcePiece1DL_f3dlite_triforce_surface | 21 +++ .../objects/object_triforce_piece_1/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_2/gTriforcePiece2DL | 15 +++ .../gTriforcePiece2DL_tri_0 | 29 ++++ .../gTriforcePiece2DL_tri_1 | 11 ++ .../gTriforcePiece2DL_tri_2 | 19 +++ .../gTriforcePiece2DL_vtx_0 | 36 +++++ .../gTriforcePiece2DL_vtx_1 | 12 ++ .../gTriforcePiece2DL_vtx_2 | 18 +++ .../mat_gTriforcePiece2DL_f3dlite_shard_edge | 21 +++ ...t_gTriforcePiece2DL_f3dlite_triforce_edges | 21 +++ ...gTriforcePiece2DL_f3dlite_triforce_surface | 21 +++ .../objects/object_triforce_piece_2/noise_tex | Bin 0 -> 2140 bytes .../gTriforcePiece.rgba32.png | Bin 0 -> 1761 bytes soh/assets/soh_assets.h | 15 +++ soh/include/variables.h | 1 + soh/include/z64item.h | 1 + soh/include/z64player.h | 2 +- soh/include/z64save.h | 1 + .../Enhancements/debugger/debugSaveEditor.cpp | 13 ++ .../game-interactor/GameInteractor.h | 4 + .../game-interactor/GameInteractor_State.cpp | 12 ++ soh/soh/Enhancements/gameplaystats.cpp | 2 + soh/soh/Enhancements/gameplaystats.h | 1 + soh/soh/Enhancements/mods.cpp | 42 ++++++ .../Enhancements/randomizer/3drando/hints.cpp | 7 +- .../randomizer/3drando/item_list.cpp | 1 + .../randomizer/3drando/item_location.cpp | 4 + .../randomizer/3drando/item_pool.cpp | 12 +- .../Enhancements/randomizer/3drando/keys.hpp | 1 + .../randomizer/3drando/location_access.cpp | 3 +- .../Enhancements/randomizer/3drando/logic.cpp | 9 +- .../Enhancements/randomizer/3drando/logic.hpp | 4 + .../randomizer/3drando/settings.cpp | 25 +++- .../randomizer/3drando/settings.hpp | 9 ++ .../Enhancements/randomizer/3drando/shops.cpp | 6 +- .../randomizer/3drando/starting_inventory.cpp | 4 +- soh/soh/Enhancements/randomizer/draw.cpp | 93 ++++++++++++- soh/soh/Enhancements/randomizer/draw.h | 18 ++- .../Enhancements/randomizer/randomizer.cpp | 124 +++++++++++++++++- soh/soh/Enhancements/randomizer/randomizer.h | 3 + .../Enhancements/randomizer/randomizerTypes.h | 15 +++ .../randomizer/randomizer_item_tracker.cpp | 94 ++++++++++++- soh/soh/Enhancements/randomizer/savefile.cpp | 3 + soh/soh/OTRGlobals.cpp | 2 + soh/soh/SaveManager.cpp | 6 + soh/src/code/z_draw.c | 36 ++++- soh/src/code/z_parameter.c | 15 +++ soh/src/code/z_play.c | 5 +- soh/src/code/z_player_lib.c | 3 + 75 files changed, 1364 insertions(+), 27 deletions(-) create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_completed/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/noise_tex create mode 100644 OTRExporter/assets/textures/parameter_static/gTriforcePiece.rgba32.png diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL new file mode 100644 index 000000000..966bef206 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 new file mode 100644 index 000000000..dea47708c --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 new file mode 100644 index 000000000..36be4333f --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 @@ -0,0 +1,7 @@ + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 new file mode 100644 index 000000000..6ca96db30 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 new file mode 100644 index 000000000..3a653966d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges new file mode 100644 index 000000000..52591dfc8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface new file mode 100644 index 000000000..06193ae61 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/noise_tex b/OTRExporter/assets/objects/object_triforce_completed/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 new file mode 100644 index 000000000..09e44f1b7 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 new file mode 100644 index 000000000..48001e3c3 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 new file mode 100644 index 000000000..e35e34492 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 new file mode 100644 index 000000000..a86fa98bf --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 new file mode 100644 index 000000000..230fbb7f8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 new file mode 100644 index 000000000..86d123825 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge new file mode 100644 index 000000000..f62631793 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges new file mode 100644 index 000000000..9355e7094 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface new file mode 100644 index 000000000..e863b31c5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_0/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 new file mode 100644 index 000000000..5f33f7347 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 new file mode 100644 index 000000000..43df6492b --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 new file mode 100644 index 000000000..e078b8246 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 new file mode 100644 index 000000000..e0460194d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge new file mode 100644 index 000000000..b9e61293d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface new file mode 100644 index 000000000..5f8dc51f9 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_1/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 new file mode 100644 index 000000000..b54e182d5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 new file mode 100644 index 000000000..00a32bfd8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 new file mode 100644 index 000000000..0993c1c1e --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 new file mode 100644 index 000000000..bf7dfcac6 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 new file mode 100644 index 000000000..e3237ab21 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 new file mode 100644 index 000000000..ec4e73700 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge new file mode 100644 index 000000000..c222fe68d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges new file mode 100644 index 000000000..5968068f5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface new file mode 100644 index 000000000..d903f00bb --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_2/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW-EX>4Tx04R}tkvm8OK@>$#q7o55g(*ZVwooh6_`@m6%w^@cVnWE-Gto) z8%x1fu<+CPD{QQ_wXhWg!4HV7jg_K}$oeJ;B#3cfn8Tg9kN56?|1e`(ZhaUmJMY9M zB3e8#uLWKR;0K0i)lD}y8jVg-bA8_7Q|E0+)vEpO&()G#c1`eU!eKM#&WxIkD=}fF71jxdk7;RqHk5S z?gMDsfb*-0X6(SlCUiXJbVn~rb+Y}b6uh0vHzlC`2AUR1xs~rZxrbTFYU&#H_MtZ| zwHIs5-B_L5zi-O<{Qy|*a-2@$NxJ|500v@9M??Vs0RI60puMM)00009a7bBm001r{ z001r{0eGc9b^rhX2XskIMF-~x0suH2t0hGv0000PbVXQnLvL+uWo~o;Lvm$dbY)~9 zcWHEJAV*0}P*;Ht7XSbRp-DtRR9M69mrH0|RT#&A=iK+aCz(u2U(#Y}wJxN%5yh=2 zxRfFybR%wrBHF5?)kSM*ZD~qsun$V2_$Y-I5u{RZ(WSW2y3*RlD$-;!nM~%DnYs6# z>*CyLqk)>Ht*d-+;Lbhgp6~blzwbN-R_q>Z^quU}_d17I?uTC<&OICW)o&{maWz65 z4T9olZylR#{4W5X9$1yIt<(%WV~fy%0=wNuK*H%H#s>^(4mPxj}w3enwVNRR^3S;*%=Na%Jzg~7IB;l_s# z3?K}fCo>skg$i0N=qrHjL7@O)2%Qc<`}Ohti5KrXz^T27SNr_qWEjV_Z3Z;@l1Q$-f zH|X6LfK0YFoXG|m*MnpV+@uCE3@HBzDugu~A)N-L!1p1F!p9AxHgd;;UW)V259OZA zW}637X-K5NiXDfcj<(Z=Mpf5=W5fKsZl(!B1lwMCCi&afm;IlYrNG#}Vj`EXk61P& z(qOtE91xbS0n-FansnMQJqfl0tJi?zXcd$KDO;yL9x6P#EP&!*ZI24Whx7el*nk=9 zCbbHN0n!RE4XDg$r->x2T@Mi&K&dEUnset*?sXG)1#o^Ow^ga`u1roxjv0Ru#oMR> zhN+dXY%nBDPl0ViE)VP0>oh3^ME({cPTduNZ8uL#q0Cf%!AJ#!8bS!IfVm(Yoeos1 z8mPYj1I3;SAVmA+6GPsPB>{Xj>b({Q{*!%!+7VIg*oUYKkq?p5eP*nJX@O}#c}jyw z2?K+WOfH0&A02%+_1GN&&K@ZYM#?{&$?B?(LhVp>Ac$M&Yk7r;9WQ{85+?tOm!i@u zr{2&|oh}KY*7@Ua+ezZAE;7fN8+P3woytHMKon{ED2@VRRk)poC;}tY&r0c~S*_^J z>BbFccR(6YuR=DX+;Sxw!t`Q*bHmx^Eo<(jbXIRJQKTJ5kBwb# zNCC{uXuzowtSZ7>4bmwv49*-FpKdQwU~FF@;kmQNQyD!`7~ZaUgr2w;c@*g;;&)Q$ zF}?mUgi;AAvye_gquDE5adE0-9fK~50Q&kGdy}bfqh-bsqL1006Cv~o*ej}q5DTGv zA8t5Zn< zgoif3mPd3%J*S$S3J4)kYI%|>~q+0NyVhT$}J9`5|QaQx-7#m$a0d#POV z*I&6p&G*4`aqA6g>6B~&+jozs?;psm_*UWWU!A{pEhscwrHORP6QLRiKN$N%t^8?O zu@zgY{SCvZ?~TMo1l|Au01jnXNoGw=04e|g00;m8000000Mb*F00000NkvXXu0mjf D?WQyR literal 0 HcmV?d00001 diff --git a/soh/assets/soh_assets.h b/soh/assets/soh_assets.h index b1feac469..2b193053c 100644 --- a/soh/assets/soh_assets.h +++ b/soh/assets/soh_assets.h @@ -44,6 +44,18 @@ static const ALIGN_ASSET(2) char gTitleRandomizerSubtitleTex[] = dgTitleRandomiz #define dgTitleBossRushSubtitleTex "__OTR__objects/object_mag/gTitleBossRushSubtitleTex" static const ALIGN_ASSET(2) char gTitleBossRushSubtitleTex[] = dgTitleBossRushSubtitleTex; +#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; + // overlays #define dgOptionsDividerChangeLangVtx "__OTR__overlays/ovl_file_choose/gOptionsDividerChangeLangVtx" static const ALIGN_ASSET(2) char gOptionsDividerChangeLangVtx[] = dgOptionsDividerChangeLangVtx; @@ -58,6 +70,9 @@ static const ALIGN_ASSET(2) char gArrowUpTex[] = dgArrowUp; #define dgArrowDown "__OTR__textures/parameter_static/gArrowDown" static const ALIGN_ASSET(2) char gArrowDownTex[] = dgArrowDown; +#define dgTriforcePiece "__OTR__textures/parameter_static/gTriforcePiece" +static const ALIGN_ASSET(2) char gTriforcePieceTex[] = dgTriforcePiece; + #define dgFileSelMQButtonTex "__OTR__textures/title_static/gFileSelMQButtonTex" static const ALIGN_ASSET(2) char gFileSelMQButtonTex[] = dgFileSelMQButtonTex; diff --git a/soh/include/variables.h b/soh/include/variables.h index 8fd457d18..ee73d003b 100644 --- a/soh/include/variables.h +++ b/soh/include/variables.h @@ -171,6 +171,7 @@ extern "C" extern u8 gWalkSpeedToggle1; extern u8 gWalkSpeedToggle2; extern f32 iceTrapScale; + extern f32 triforcePieceScale; extern const s16 D_8014A6C0[]; #define gTatumsPerBeat (D_8014A6C0[1]) diff --git a/soh/include/z64item.h b/soh/include/z64item.h index 65f6e93d9..2f0038872 100644 --- a/soh/include/z64item.h +++ b/soh/include/z64item.h @@ -511,6 +511,7 @@ typedef enum { /* 0x79 */ GID_SONG_SUN, /* 0x7A */ GID_SONG_TIME, /* 0x7B */ GID_SONG_STORM, + /* 0x7C */ GID_TRIFORCE_PIECE, /* 0x7C */ GID_MAXIMUM } GetItemDrawID; diff --git a/soh/include/z64player.h b/soh/include/z64player.h index 99445f1d7..094e28eb2 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -620,7 +620,7 @@ typedef struct Player { /* 0x0858 */ f32 unk_858; /* 0x085C */ f32 unk_85C; // stick length among other things /* 0x0860 */ s16 unk_860; // stick flame timer among other things - /* 0x0862 */ s8 unk_862; // get item draw ID + 1 + /* 0x0862 */ s16 unk_862; // get item draw ID + 1 /* 0x0864 */ f32 unk_864; /* 0x0868 */ f32 unk_868; /* 0x086C */ f32 unk_86C; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index e4aeb4e31..50f9ba44b 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -324,6 +324,7 @@ typedef struct { /* */ u8 seedIcons[5]; /* */ u16 randomizerInf[10]; /* */ u16 adultTradeItems; + /* */ u8 triforcePiecesCollected; // #endregion } SaveContext; // size = 0x1428 diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 0f9c56669..cf60e61da 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -10,6 +10,7 @@ #include #include #include +#include "soh_assets.h" extern "C" { #include @@ -144,6 +145,10 @@ std::map gregMapping = { {ITEM_RUPEE_GREEN, {ITEM_RUPEE_GREEN, "ITEM_RUPEE_GREEN", "ITEM_RUPEE_GREEN_Faded", gRupeeCounterIconTex}} }; +std::map triforcePieceMapping = { + {RG_TRIFORCE_PIECE, {RG_TRIFORCE_PIECE, "RG_TRIFORCE_PIECE", "RG_TRIFORCE_PIECE_Faded", gTriforcePieceTex}} +}; + // Maps entries in the GS flag array to the area name it represents std::vector gsMapping = { "Deku Tree", @@ -509,6 +514,10 @@ void DrawInfoTab() { } UIWidgets::InsertHelpHoverText("Z-Targeting behavior"); + if (gSaveContext.n64ddFlag && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { + ImGui::InputScalar("Triforce Pieces", ImGuiDataType_U16, &gSaveContext.triforcePiecesCollected); + UIWidgets::InsertHelpHoverText("Currently obtained Triforce Pieces. For Triforce Hunt."); + } ImGui::PushItemWidth(ImGui::GetFontSize() * 10); static std::array minigameHS = { "Horseback Archery", @@ -1790,6 +1799,10 @@ void SaveEditorWindow::InitElement() { LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.name, entry.second.texturePath, gregGreen); LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.nameFaded, entry.second.texturePath, gregFadedGreen); } + for (const auto& entry : triforcePieceMapping) { + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.name, entry.second.texturePath, ImVec4(1, 1, 1, 1)); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.nameFaded, entry.second.texturePath, ImVec4(1, 1, 1, 0.3f)); + } for (const auto& entry : questMapping) { LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.name, entry.second.texturePath, ImVec4(1, 1, 1, 1)); LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.nameFaded, entry.second.texturePath, ImVec4(1, 1, 1, 0.3f)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 036483f2e..008cfb53c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -83,6 +83,8 @@ uint8_t GameInteractor_GetRandomWindActive(); uint8_t GameInteractor_GetRandomBonksActive(); uint8_t GameInteractor_GetSlipperyFloorActive(); uint8_t GameInteractor_SecondCollisionUpdate(); +void GameInteractor_SetTriforceHuntPieceGiven(uint8_t state); +void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state); #ifdef __cplusplus } #endif @@ -123,6 +125,8 @@ public: static uint8_t RandomBonksActive; static uint8_t SlipperyFloorActive; static uint8_t SecondCollisionUpdate; + static uint8_t TriforceHuntPieceGiven; + static uint8_t TriforceHuntCreditsWarpActive; static void SetPacifistMode(bool active); }; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp index 881fb7ff5..21642dded 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp @@ -20,6 +20,8 @@ uint8_t GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0; uint8_t GameInteractor::State::RandomBonksActive = 0; uint8_t GameInteractor::State::SlipperyFloorActive = 0; uint8_t GameInteractor::State::SecondCollisionUpdate = 0; +uint8_t GameInteractor::State::TriforceHuntPieceGiven = 0; +uint8_t GameInteractor::State::TriforceHuntCreditsWarpActive = 0; void GameInteractor::State::SetPacifistMode(bool active) { PacifistModeActive = active; @@ -127,3 +129,13 @@ uint8_t GameInteractor_GetSlipperyFloorActive() { uint8_t GameInteractor_SecondCollisionUpdate() { return GameInteractor::State::SecondCollisionUpdate; } + +// MARK: - GameInteractor::State::TriforceHuntPieceGiven +void GameInteractor_SetTriforceHuntPieceGiven(uint8_t state) { + GameInteractor::State::TriforceHuntPieceGiven = state; +} + +// MARK: - GameInteractor::State::TriforceHuntCreditsWarpActive +void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state) { + GameInteractor::State::TriforceHuntCreditsWarpActive = state; +} diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 6276da8b2..ea95afe8e 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -793,6 +793,7 @@ void SetupDisplayNames() { strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: "); strcpy(itemTimestampDisplayName[TIMESTAMP_BOSSRUSH_FINISH], "Boss Rush Finished: "); strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_TRIFORCE_COMPLETED], "Triforce Completed: "); } void SetupDisplayColors() { @@ -839,6 +840,7 @@ void SetupDisplayColors() { case ITEM_ARROW_LIGHT: case TIMESTAMP_DEFEAT_GANONDORF: case TIMESTAMP_DEFEAT_GANON: + case TIMESTAMP_TRIFORCE_COMPLETED: itemTimestampDisplayColor[i] = COLOR_YELLOW; break; case ITEM_SONG_STORMS: diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 8bcf524d8..8772dd37f 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -35,6 +35,7 @@ typedef enum { /* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c /* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c /* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c + /* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c /* 0xAB */ TIMESTAMP_MAX }GameplayStatTimestamp; diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 6fc3b35b2..927ae1870 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -2,10 +2,12 @@ #include #include "game-interactor/GameInteractor.h" #include "tts/tts.h" +#include "soh/OTRGlobals.h" #include "soh/Enhancements/boss-rush/BossRushTypes.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/cosmetics/authenticGfxPatches.h" +#include #include "soh/Enhancements/nametag.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" @@ -613,6 +615,45 @@ void RegisterMirrorModeHandler() { }); } +f32 triforcePieceScale; + +void RegisterTriforceHunt() { + GameInteractor::Instance->RegisterGameHook([]() { + if (!GameInteractor::IsGameplayPaused() && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT)) { + + // Warp to credits + if (GameInteractor::State::TriforceHuntCreditsWarpActive) { + gPlayState->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0xFFF2; + gPlayState->sceneLoadFlag = 0x14; + gPlayState->fadeTransition = 3; + GameInteractor::State::TriforceHuntCreditsWarpActive = 0; + } + + // Reset Triforce Piece scale for GI animation. Triforce Hunt allows for multiple triforce models, + // and cycles through them based on the amount of triforce pieces collected. It takes a little while + // for the count to increase during the GI animation, so the model is entirely hidden until that piece + // has been added. That scale has to be reset after the textbox is closed, and this is the best way + // to ensure it's done at that point in time specifically. + if (GameInteractor::State::TriforceHuntPieceGiven) { + triforcePieceScale = 0.0f; + GameInteractor::State::TriforceHuntPieceGiven = 0; + } + + uint8_t currentPieces = gSaveContext.triforcePiecesCollected; + uint8_t requiredPieces = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); + + // Give Boss Key when player loads back into the savefile. + if (currentPieces >= requiredPieces && gPlayState->sceneLoadFlag != 0x14 && + (1 << 0 & gSaveContext.inventory.dungeonItems[SCENE_GANONS_TOWER]) == 0) { + GetItemEntry getItemEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY); + GiveItemEntryWithoutActor(gPlayState, getItemEntry); + } + } + }); +} + //this map is used for enemies that can be uniquely identified by their id //and that are always counted //enemies that can't be uniquely identified by their id @@ -996,6 +1037,7 @@ void InitMods() { RegisterBonkDamage(); RegisterMenuPathFix(); RegisterMirrorModeHandler(); + RegisterTriforceHunt(); RegisterEnemyDefeatCounts(); RegisterAltTrapTypes(); RegisterRandomizerSheikSpawn(); diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index 0bae15676..c1610ce07 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -503,7 +503,9 @@ static std::vector CalculateBarrenRegions() { if (Location(loc)->GetPlacedItem().IsMajorItem() || ElementInContainer(loc, wothLocations)) { AddElementsToPool(potentiallyUsefulLocations, std::vector{loc}); } else { - if (loc != LINKS_POCKET) { //Nobody cares to know if Link's Pocket is barren + // Link's Pocket & Triforce Hunt "reward" shouldn't be considered for barren areas because it's clear what + // they have to a player. + if (loc != LINKS_POCKET && loc != TRIFORCE_COMPLETED) { AddElementsToPool(barrenLocations, std::vector{loc}); } } @@ -723,6 +725,9 @@ static Text BuildGanonBossKeyText() { } else if (GanonsBossKey.Is(GANONSBOSSKEY_LACS_TOKENS)) { ganonBossKeyText = BuildCountReq(LACS_TOKENS_HINT, LACSTokenCount); + + } else if (GanonsBossKey.Is(GANONSBOSSKEY_TRIFORCE_HUNT)) { + ganonBossKeyText = Hint(GANON_BK_TRIFORCE_HINT).GetText(); } return Text()+"$b"+ganonBossKeyText+"^"; diff --git a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp index dd7533b98..417203a5b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp @@ -242,6 +242,7 @@ void ItemTable_Init() { // RandomizerGet itemTable[BUY_RED_POTION_40] = Item(RG_BUY_RED_POTION_40, Text{"Buy Red Potion [40]", "Acheter: Potion Rouge [40]", "Comprar poción roja [40]"}, ITEMTYPE_SHOP, 0x30, false, &noVariable, BOTTLE_WITH_RED_POTION, 40); itemTable[BUY_RED_POTION_50] = Item(RG_BUY_RED_POTION_50, Text{"Buy Red Potion [50]", "Acheter: Potion Rouge [50]", "Comprar poción roja [50]"}, ITEMTYPE_SHOP, 0x31, false, &noVariable, BOTTLE_WITH_RED_POTION, 50); + itemTable[TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{"Triforce Piece", "Triforce Piece", "Triforce Piece"}, ITEMTYPE_ITEM, 0xDF, true, &TriforcePieces, TRIFORCE_PIECE); itemTable[TRIFORCE] = Item(RG_TRIFORCE, Text{"Triforce", "Triforce", "Trifuerza"}, ITEMTYPE_EVENT, GI_RUPEE_RED_LOSE, false, &noVariable, NONE); itemTable[HINT] = Item(RG_HINT, Text{"Hint", "Indice", "Pista"}, ITEMTYPE_EVENT, GI_RUPEE_BLUE_LOSE, false, &noVariable, NONE); diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp index 2327a4a4d..1c085021c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp @@ -918,6 +918,7 @@ void LocationTable_Init() { locationTable[DMC_UPPER_GROTTO_GOSSIP_STONE] = ItemLocation::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, "DMC Upper Grotto Gossip Stone"); locationTable[GANONDORF_HINT] = ItemLocation::OtherHint(RC_GANONDORF_HINT, "Ganondorf Hint"); + locationTable[TRIFORCE_COMPLETED] = ItemLocation::Reward (RC_TRIFORCE_COMPLETED, 0xFF, "Completed Triforce", NONE, TRIFORCE_COMPLETED, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_NO_GROUP); for (int i = NONE; i != KEY_ENUM_MAX; i++) locationLookupTable.insert(std::make_pair(locationTable[i].GetRandomizerCheck(), static_cast(i))); @@ -1523,6 +1524,9 @@ void GenerateLocationPool() { allLocations.clear(); AddLocation(LINKS_POCKET); + if (Settings::TriforceHunt.Is(TRIFORCE_HUNT_ON)) { + AddLocation(TRIFORCE_COMPLETED); + } AddLocations(overworldLocations); for (auto dungeon : Dungeon::dungeonList) { diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 9b55eb0b4..485fc4154 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -662,9 +662,17 @@ void GenerateItemPool() { IceTrapModels.push_back(0xD3); } + if (TriforceHunt.Is(TRIFORCE_HUNT_ON)) { + IceTrapModels.push_back(0xDF); + AddItemToMainPool(TRIFORCE_PIECE, Settings::TriforceHuntTotal.Value()); + PlaceItemInLocation(TRIFORCE_COMPLETED, TRIFORCE); // Win condition + PlaceItemInLocation(GANON, GetJunkItem(), false, true); + } else { + PlaceItemInLocation(GANON, TRIFORCE); // Win condition + } + //Fixed item locations PlaceItemInLocation(HC_ZELDAS_LETTER, ZELDAS_LETTER); - PlaceItemInLocation(GANON, TRIFORCE); //The Triforce is only used to make sure Ganon is accessible PlaceItemInLocation(MARKET_BOMBCHU_BOWLING_BOMBCHUS, BOMBCHU_DROP); if (ShuffleKokiriSword) { @@ -1135,7 +1143,7 @@ void GenerateItemPool() { if (GanonsBossKey.Is(GANONSBOSSKEY_FINAL_GS_REWARD)) { PlaceItemInLocation(KAK_100_GOLD_SKULLTULA_REWARD, GANONS_CASTLE_BOSS_KEY); - } else if (GanonsBossKey.Value() >= GANONSBOSSKEY_LACS_VANILLA) { + } else if (GanonsBossKey.Value() >= GANONSBOSSKEY_LACS_VANILLA && GanonsBossKey.IsNot(GANONSBOSSKEY_TRIFORCE_HUNT)) { PlaceItemInLocation(TOT_LIGHT_ARROWS_CUTSCENE, GANONS_CASTLE_BOSS_KEY); } else if (GanonsBossKey.Is(GANONSBOSSKEY_VANILLA)) { PlaceItemInLocation(GANONS_TOWER_BOSS_KEY_CHEST, GANONS_CASTLE_BOSS_KEY); diff --git a/soh/soh/Enhancements/randomizer/3drando/keys.hpp b/soh/soh/Enhancements/randomizer/3drando/keys.hpp index 38011b8b8..6e9e87f4e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/keys.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/keys.hpp @@ -202,6 +202,7 @@ typedef enum { TRIFORCE, TRIFORCE_PIECE, + TRIFORCE_COMPLETED, EPONA, HINT, diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access.cpp index a78d00a30..6be67ea84 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access.cpp @@ -256,7 +256,8 @@ void AreaTable_Init() { //name, scene, hint text, events, locations, exits areaTable[ROOT] = Area("Root", "", LINKS_POCKET, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LocationAccess(LINKS_POCKET, {[]{return true;}}) + LocationAccess(LINKS_POCKET, {[]{return true;}}), + LocationAccess(TRIFORCE_COMPLETED, { [] { return CanCompleteTriforce;}}), }, { //Exits Entrance(ROOT_EXITS, {[]{return true;}}) diff --git a/soh/soh/Enhancements/randomizer/3drando/logic.cpp b/soh/soh/Enhancements/randomizer/3drando/logic.cpp index 6fe26a417..ddf53824e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/logic.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/logic.cpp @@ -150,6 +150,9 @@ namespace Logic { uint8_t BottomOfTheWellKeys = 0; uint8_t TreasureGameKeys = 0; + //Triforce Pieces + uint8_t TriforcePieces = 0; + //Boss Keys bool BossKeyForestTemple = false; bool BossKeyFireTemple = false; @@ -309,6 +312,7 @@ namespace Logic { bool AtDay = false; bool AtNight = false; uint8_t Age = 0; + bool CanCompleteTriforce = false; //Events bool ShowedMidoSwordAndShield = false; @@ -620,7 +624,7 @@ namespace Logic { (LACSCondition == LACSCONDITION_REWARDS && StoneCount + MedallionCount + (Greg && GregInLogic ? 1 : 0) >= LACSRewardCount.Value()) || (LACSCondition == LACSCONDITION_DUNGEONS && DungeonCount + (Greg && GregInLogic ? 1 : 0) >= LACSDungeonCount.Value()) || (LACSCondition == LACSCONDITION_TOKENS && GoldSkulltulaTokens >= LACSTokenCount.Value()); - + CanCompleteTriforce = TriforcePieces >= TriforceHuntRequired.Value(); } bool SmallKeys(Key dungeon, uint8_t requiredAmount) { @@ -873,7 +877,8 @@ namespace Logic { NumBottles = 0; NoBottles = false; - + //Triforce Pieces + TriforcePieces = 0; //Drops and Bottle Contents Access DekuNutDrop = false; diff --git a/soh/soh/Enhancements/randomizer/3drando/logic.hpp b/soh/soh/Enhancements/randomizer/3drando/logic.hpp index 77f942168..4d24fd3ff 100644 --- a/soh/soh/Enhancements/randomizer/3drando/logic.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/logic.hpp @@ -143,6 +143,9 @@ extern uint8_t GerudoFortressKeys; extern uint8_t GanonsCastleKeys; extern uint8_t TreasureGameKeys; +// Triforce Pieces +extern uint8_t TriforcePieces; + // Boss Keys extern bool BossKeyForestTemple; extern bool BossKeyFireTemple; @@ -298,6 +301,7 @@ extern bool AtDay; extern bool AtNight; extern bool LinksCow; extern uint8_t Age; +extern bool CanCompleteTriforce; // Events extern bool ShowedMidoSwordAndShield; diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index 286f249fc..42fdae5c1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -101,6 +101,9 @@ namespace Settings { Option BombchusInLogic = Option::Bool("Bombchus in Logic", {"Off", "On"}); Option AmmoDrops = Option::U8 ("Ammo Drops", {"On", "On + Bombchu", "Off"}, OptionCategory::Setting, AMMODROPS_BOMBCHU); Option HeartDropRefill = Option::U8 ("Heart Drops and Refills",{"On", "No Drop", "No Refill", "Off"}, OptionCategory::Setting, HEARTDROPREFILL_VANILLA); + Option TriforceHunt = Option::U8 ("Triforce Hunt", {"Off", "On"}); + Option TriforceHuntTotal = Option::U8 ("Triforce Hunt Total Pieces", {NumOpts(0, 100)}); + Option TriforceHuntRequired = Option::U8 ("Triforce Hunt Required Pieces", {NumOpts(0, 100)}); Option MQDungeonCount = Option::U8( "MQ Dungeon Count", { MultiVecOpts({ NumOpts(0, 12), { "Random" }, { "Selection" } }) }); uint8_t MQSet; @@ -139,6 +142,9 @@ namespace Settings { &BombchusInLogic, &AmmoDrops, &HeartDropRefill, + &TriforceHunt, + &TriforceHuntTotal, + &TriforceHuntRequired, &MQDungeonCount, &SetDungeonTypes, &MQDeku, @@ -219,7 +225,7 @@ namespace Settings { Option Keysanity = Option::U8 ("Small Keys", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, KEYSANITY_OWN_DUNGEON); Option GerudoKeys = Option::U8 ("Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}); Option BossKeysanity = Option::U8 ("Boss Keys", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, BOSSKEYSANITY_OWN_DUNGEON); - Option GanonsBossKey = Option::U8 ("Ganon's Boss Key", {"Vanilla", "Own dungeon", "Start with", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward"}, OptionCategory::Setting, GANONSBOSSKEY_VANILLA); + Option GanonsBossKey = Option::U8 ("Ganon's Boss Key", {"Vanilla", "Own dungeon", "Start with", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward", "Triforce Hunt"}, OptionCategory::Setting, GANONSBOSSKEY_VANILLA); uint8_t LACSCondition = 0; Option LACSStoneCount = Option::U8 ("Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, 1, true); Option LACSMedallionCount = Option::U8 ("Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, 1, true); @@ -1685,6 +1691,13 @@ namespace Settings { IncludeAndHide({KAK_100_GOLD_SKULLTULA_REWARD}); } + //Force include Triforce Hunt if it's off + if (TriforceHunt) { + Unhide({ TRIFORCE_COMPLETED }); + } else { + IncludeAndHide({ TRIFORCE_COMPLETED }); + } + //Force include Map and Compass Chests when Vanilla std::vector mapChests = GetLocations(everyPossibleLocation, Category::cVanillaMap); std::vector compassChests = GetLocations(everyPossibleLocation, Category::cVanillaCompass); @@ -2365,7 +2378,11 @@ namespace Settings { Keysanity.SetSelectedIndex(cvarSettings[RSK_KEYSANITY]); GerudoKeys.SetSelectedIndex(cvarSettings[RSK_GERUDO_KEYS]); BossKeysanity.SetSelectedIndex(cvarSettings[RSK_BOSS_KEYSANITY]); - GanonsBossKey.SetSelectedIndex(cvarSettings[RSK_GANONS_BOSS_KEY]); + if (cvarSettings[RSK_TRIFORCE_HUNT]) { + GanonsBossKey.SetSelectedIndex(RO_GANON_BOSS_KEY_TRIFORCE_HUNT); + } else { + GanonsBossKey.SetSelectedIndex(cvarSettings[RSK_GANONS_BOSS_KEY]); + } LACSStoneCount.SetSelectedIndex(cvarSettings[RSK_LACS_STONE_COUNT]); LACSMedallionCount.SetSelectedIndex(cvarSettings[RSK_LACS_MEDALLION_COUNT]); LACSRewardCount.SetSelectedIndex(cvarSettings[RSK_LACS_REWARD_COUNT]); @@ -2470,6 +2487,10 @@ namespace Settings { } } + TriforceHunt.SetSelectedIndex(cvarSettings[RSK_TRIFORCE_HUNT]); + TriforceHuntTotal.SetSelectedIndex(cvarSettings[RSK_TRIFORCE_HUNT_PIECES_TOTAL]); + TriforceHuntRequired.SetSelectedIndex(cvarSettings[RSK_TRIFORCE_HUNT_PIECES_REQUIRED]); + //Set key ring for each dungeon for (size_t i = 0; i < dungeons.size(); i++) { dungeons[i]->ClearKeyRing(); diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.hpp b/soh/soh/Enhancements/randomizer/3drando/settings.hpp index dcb9f4e2c..0d3d3c898 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.hpp @@ -91,6 +91,11 @@ typedef enum { LACS_OPTION_WILDCARD, } LACSRewardOptionsSetting; +typedef enum { + TRIFORCE_HUNT_OFF, + TRIFORCE_HUNT_ON, +} TriforceHuntSetting; + typedef enum { AGE_CHILD, AGE_ADULT, @@ -249,6 +254,7 @@ typedef enum { GANONSBOSSKEY_LACS_DUNGEONS, GANONSBOSSKEY_LACS_TOKENS, GANONSBOSSKEY_FINAL_GS_REWARD, + GANONSBOSSKEY_TRIFORCE_HUNT, } GanonsBossKeySetting; typedef enum { @@ -780,6 +786,9 @@ void UpdateSettings(std::unordered_map cvarSettin extern Option BombchusInLogic; extern Option AmmoDrops; extern Option HeartDropRefill; + extern Option TriforceHunt; + extern Option TriforceHuntTotal; + extern Option TriforceHuntRequired; extern Option MQDungeonCount; extern Option SetDungeonTypes; diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index ac6755da3..8c47ac976 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -13,7 +13,7 @@ using namespace Settings; std::vector NonShopItems = {}; -static std::array, 0xD5> trickNameTable; //Table of trick names for ice traps +static std::array, 0xE0> trickNameTable; // Table of trick names for ice traps bool initTrickNames = false; //Indicates if trick ice trap names have been initialized yet //Set vanilla shop item locations before potentially shuffling @@ -698,6 +698,10 @@ void InitTrickNames() { Text{"Crystal Heart", "Cœur de cristal", "Corazón de cristal"}, Text{"Life Heart", "Cœur de vie", "Vida Corazón"}, Text{"Lots of Love", "Beaucoup d'amour", "Mucho amor"}}; + trickNameTable[0xDF] = { + Text{"Piece of Cheese", "Morceau de Fromage", "Piece of Cheese"}, + Text{"Triforce Shard", "Éclat de Triforce", "Triforce Shard"}, + Text{"Shiny Rock", "Caiiloux Brillant", "Shiny Rock"}}; /* //Names for individual upgrades, in case progressive names are replaced diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index 26ea673bc..77b11203e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -54,7 +54,9 @@ void GenerateStartingInventory() { AddItemToInventory(SHADOW_TEMPLE_BOSS_KEY); } - if (GanonsBossKey.Is(GANONSBOSSKEY_START_WITH)) { + // Add Ganon's Boss key with Triforce Hunt so the game thinks it's obtainable from the start. + // During save init, the boss key isn't actually given and it's instead given when completing the triforce. + if (GanonsBossKey.Is(GANONSBOSSKEY_START_WITH) || GanonsBossKey.Is(GANONSBOSSKEY_TRIFORCE_HUNT)) { AddItemToInventory(GANONS_CASTLE_BOSS_KEY); } diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index 34c17356c..c2895dd62 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -3,17 +3,23 @@ #include "z64.h" #include "macros.h" #include "functions.h" +#include "variables.h" +#include "soh/OTRGlobals.h" #include "randomizerTypes.h" #include #include "objects/object_gi_key/object_gi_key.h" #include "objects/object_gi_bosskey/object_gi_bosskey.h" #include "objects/object_gi_hearts/object_gi_hearts.h" #include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "soh_assets.h" + +extern "C" { +extern SaveContext gSaveContext; +} extern "C" u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); extern "C" void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEntry) { - s32 pad; s8 keysCanBeOutsideDungeon = getItemEntry->getItemId == RG_GERUDO_FORTRESS_SMALL_KEY ? Randomizer_GetSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA : DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_KEYSANITY); @@ -53,7 +59,6 @@ extern "C" void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEn } extern "C" void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEntry) { - s32 pad; s8 keysCanBeOutsideDungeon = getItemEntry->getItemId == RG_GANONS_CASTLE_BOSS_KEY ? DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_GANONS_BOSS_KEY) : DUNGEON_ITEMS_CAN_BE_OUTSIDE_DUNGEON(RSK_BOSS_KEYSANITY); @@ -107,8 +112,6 @@ extern "C" void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEnt } extern "C" void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry) { - s32 pad; - s16 color_slot = getItemEntry->getItemId - RG_FOREST_TEMPLE_KEY_RING; s16 colors[9][3] = { { 4, 195, 46 }, // Forest Temple @@ -155,7 +158,6 @@ extern "C" void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEnt } extern "C" void Randomizer_DrawDoubleDefense(PlayState* play, GetItemEntry getItemEntry) { - s32 pad; OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx); @@ -173,3 +175,84 @@ extern "C" void Randomizer_DrawDoubleDefense(PlayState* play, GetItemEntry getIt CLOSE_DISPS(play->state.gfxCtx); } + +Gfx* Randomizer_GetTriforcePieceDL(uint8_t index) { + switch (index) { + case 1: + return (Gfx*)gTriforcePiece1DL; + case 2: + return (Gfx*)gTriforcePiece2DL; + default: + return (Gfx*)gTriforcePiece0DL; + } +} + +extern "C" void Randomizer_DrawTriforcePiece(PlayState* play, GetItemEntry getItemEntry) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Xlu(play->state.gfxCtx); + + uint16_t current = gSaveContext.triforcePiecesCollected; + + Matrix_Scale(0.035f, 0.035f, 0.035f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), + G_MTX_MODELVIEW | G_MTX_LOAD); + + Gfx* triforcePieceDL = Randomizer_GetTriforcePieceDL(current % 3); + + gSPDisplayList(POLY_XLU_DISP++, triforcePieceDL); + + CLOSE_DISPS(play->state.gfxCtx); +} + +// Seperate draw function for drawing the Triforce piece when in the GI state. +// Needed for delaying showing the triforce piece slightly so the triforce shard doesn't +// suddenly snap to the new piece model or completed triforce because the piece is +// given mid textbox. Also makes it so the overworld models don't turn into the completed +// model when the player has exactly the required amount of pieces. +extern "C" void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry getItemEntry) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Xlu(play->state.gfxCtx); + + uint16_t current = gSaveContext.triforcePiecesCollected; + uint16_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); + + Matrix_Scale(triforcePieceScale, triforcePieceScale, triforcePieceScale, MTXMODE_APPLY); + + // For creating a delay before showing the model so the model doesn't swap visually when the triforce piece + // is given when the textbox just appears. + if (triforcePieceScale < 0.0001f) { + triforcePieceScale += 0.00003f; + } + + // Animation. When not the completed triforce, create delay before showing the piece to bypass interpolation. + // If the completed triforce, make it grow slowly. + if (current != required) { + if (triforcePieceScale > 0.00008f && triforcePieceScale < 0.034f) { + triforcePieceScale = 0.034f; + } else if (triforcePieceScale < 0.035f) { + triforcePieceScale += 0.0005f; + } + } else if (triforcePieceScale > 0.00008f && triforcePieceScale < 0.035f) { + triforcePieceScale += 0.0005f; + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), + G_MTX_MODELVIEW | G_MTX_LOAD); + + // Show piece when not currently completing the triforce. Use the scale to create a delay so interpolation doesn't + // make the triforce twitch when the size is set to a higher value. + if (current != required && triforcePieceScale > 0.035f) { + // Get shard DL. Remove one before division to account for triforce piece given in the textbox + // to match up the shard from the overworld model. + Gfx* triforcePieceDL = Randomizer_GetTriforcePieceDL((current - 1) % 3); + + gSPDisplayList(POLY_XLU_DISP++, triforcePieceDL); + } else if (current == required && triforcePieceScale > 0.00008f) { + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gTriforcePieceCompletedDL); + } + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h index d9f0ed36b..19510c2f4 100644 --- a/soh/soh/Enhancements/randomizer/draw.h +++ b/soh/soh/Enhancements/randomizer/draw.h @@ -6,10 +6,18 @@ typedef struct PlayState PlayState; -extern "C" void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEntry); -extern "C" void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry); -extern "C" void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEntry); -extern "C" void Randomizer_DrawDoubleDefense(PlayState* play, GetItemEntry getItemEntry); -extern "C" void Randomizer_DrawIceTrap(PlayState* play, GetItemEntry getItemEntry); +#ifdef __cplusplus +extern "C" { +#endif +void Randomizer_DrawSmallKey(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawDoubleDefense(PlayState* play, GetItemEntry getItemEntry); +void Randomizer_DrawTriforcePiece(PlayState* play, GetItemEntry getItemEntry); +void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry getItemEntry); +#ifdef __cplusplus +}; +#endif + #endif diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index d50c23335..78a9d0525 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -55,6 +55,7 @@ const std::string Randomizer::getItemMessageTableID = "Randomizer"; const std::string Randomizer::hintMessageTableID = "RandomizerHints"; const std::string Randomizer::merchantMessageTableID = "RandomizerMerchants"; const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees"; +const std::string Randomizer::triforcePieceMessageTableID = "RandomizerTriforcePiece"; const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; @@ -320,6 +321,9 @@ std::unordered_map SpoilerfileSettingNameToEn { "World Settings:Mix Interiors", RSK_MIX_INTERIOR_ENTRANCES }, { "World Settings:Mix Grottos", RSK_MIX_GROTTO_ENTRANCES }, { "World Settings:Decouple Entrances", RSK_DECOUPLED_ENTRANCES }, + { "World Settings:Triforce Hunt", RSK_TRIFORCE_HUNT }, + { "World Settings:Triforce Hunt Total Pieces", RSK_TRIFORCE_HUNT_PIECES_TOTAL }, + { "World Settings:Triforce Hunt Required Pieces", RSK_TRIFORCE_HUNT_PIECES_REQUIRED }, { "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS }, { "Misc Settings:Hint Clarity", RSK_HINT_CLARITY }, { "Misc Settings:ToT Altar Hint", RSK_TOT_ALTAR_HINT }, @@ -799,6 +803,8 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { case RSK_BIG_POE_COUNT: case RSK_CUCCO_COUNT: case RSK_STARTING_SKULLTULA_TOKEN: + case RSK_TRIFORCE_HUNT_PIECES_TOTAL: + case RSK_TRIFORCE_HUNT_PIECES_REQUIRED: numericValueString = it.value(); gSaveContext.randoSettings[index].value = std::stoi(numericValueString); break; @@ -908,6 +914,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { case RSK_DECOUPLED_ENTRANCES: case RSK_SHOPSANITY_PRICES_AFFORDABLE: case RSK_ALL_LOCATIONS_REACHABLE: + case RSK_TRIFORCE_HUNT: if(it.value() == "Off") { gSaveContext.randoSettings[index].value = RO_GENERIC_OFF; } else if(it.value() == "On") { @@ -1061,6 +1068,8 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_TOKENS; } else if(it.value() == "100 GS Reward") { gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_KAK_TOKENS; + } else if(it.value() == "Triforce Hunt") { + gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_TRIFORCE_HUNT; } break; case RSK_RANDOM_MQ_DUNGEONS: @@ -1980,6 +1989,7 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe case RG_BUY_DEKU_NUT_10: case RG_BUY_DEKU_STICK_1: case RG_BUY_HEART: + case RG_TRIFORCE_PIECE: default: return CAN_OBTAIN; } @@ -2990,6 +3000,10 @@ void GenerateRandomizerImgui(std::string seed = "") { cvarSettings[RSK_MQ_DUNGEON_COUNT] = 0; } + cvarSettings[RSK_TRIFORCE_HUNT] = CVarGetInteger("gRandomizeTriforceHunt", 0); + cvarSettings[RSK_TRIFORCE_HUNT_PIECES_TOTAL] = CVarGetInteger("gRandomizeTriforceHuntTotalPieces", 30); + cvarSettings[RSK_TRIFORCE_HUNT_PIECES_REQUIRED] = CVarGetInteger("gRandomizeTriforceHuntRequiredPieces", 20); + cvarSettings[RSK_MQ_DEKU_TREE] = CVarGetInteger("gRandomizeMqDungeonsDekuTree", 0); cvarSettings[RSK_MQ_DODONGOS_CAVERN] = CVarGetInteger("gRandomizeMqDungeonsDodongosCavern", 0); cvarSettings[RSK_MQ_JABU_JABU] = CVarGetInteger("gRandomizeMqDungeonsJabuJabu", 0); @@ -3502,6 +3516,7 @@ void RandomizerSettingsWindow::DrawElement() { UIWidgets::PaddedSeparator(); + // Master Quest Dungeons if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) { ImGui::PushItemWidth(-FLT_MIN); ImGui::Text("Master Quest Dungeons"); @@ -3549,8 +3564,43 @@ void RandomizerSettingsWindow::DrawElement() { UIWidgets::EnhancementCheckbox("Ganon's Castle##RandomizeMqDungeons", "gRandomizeMqDungeonsGanonsCastle"); } + + UIWidgets::PaddedSeparator(); } + // Triforce Hunt + UIWidgets::EnhancementCheckbox("Triforce Hunt", "gRandomizeTriforceHunt"); + UIWidgets::InsertHelpHoverText( + "Pieces of the Triforce of Courage have been scattered across the world. Find them all to finish the game!\n\n" + "When the required amount of pieces have been found, the game is saved and Ganon's Boss key is given " + "to you when you load back into the game if you desire to beat Ganon afterwards.\n\n" + "Keep in mind Ganon might not be logically beatable when \"All Locations Reachable\" is turned off." + ); + + if (CVarGetInteger("gRandomizeTriforceHunt", 0)) { + // Triforce Hunt (total pieces) + UIWidgets::Spacer(0); + int totalPieces = CVarGetInteger("gRandomizeTriforceHuntTotalPieces", 30); + ImGui::Text("Triforce Pieces in the world: %d", totalPieces); + UIWidgets::InsertHelpHoverText( + "The amount of Triforce pieces that will be placed in the world. " + "Keep in mind seed generation can fail if more pieces are placed than there are junk items in the item pool." + ); + ImGui::SameLine(); + UIWidgets::EnhancementSliderInt("", "##TriforceHuntTotalPieces", "gRandomizeTriforceHuntTotalPieces", 1, 100, "", 30); + + // Triforce Hunt (required pieces) + int requiredPieces = CVarGetInteger("gRandomizeTriforceHuntRequiredPieces", 20); + ImGui::Text("Triforce Pieces to win: %d", requiredPieces); + UIWidgets::InsertHelpHoverText( + "The amount of Triforce pieces required to win the game." + ); + ImGui::SameLine(); + UIWidgets::EnhancementSliderInt("", "##TriforceHuntRequiredPieces", "gRandomizeTriforceHuntRequiredPieces", 1, totalPieces, "", 20); + } + + UIWidgets::PaddedSeparator(); + ImGui::EndChild(); // COLUMN 3 - Shuffle Entrances @@ -4144,7 +4194,11 @@ void RandomizerSettingsWindow::DrawElement() { "\n" "100 GS Reward - Ganon's Boss Key will be awarded by the cursed rich man after you collect 100 Gold Skulltula Tokens." ); - UIWidgets::EnhancementCombobox("gRandomizeShuffleGanonBossKey", randoShuffleGanonsBossKey, RO_GANON_BOSS_KEY_VANILLA); + bool disableGBK = CVarGetInteger("gRandomizeTriforceHunt", 0); + static const char* disableGBKText = "This option is disabled because Triforce Hunt is enabled. Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion."; + UIWidgets::EnhancementCombobox("gRandomizeShuffleGanonBossKey", randoShuffleGanonsBossKey, + RO_GANON_BOSS_KEY_VANILLA, disableGBK, disableGBKText, + RO_GANON_BOSS_KEY_VANILLA); ImGui::PopItemWidth(); switch (CVarGetInteger("gRandomizeShuffleGanonBossKey", RO_GANON_BOSS_KEY_VANILLA)) { case RO_GANON_BOSS_KEY_LACS_STONES: @@ -5461,6 +5515,70 @@ CustomMessage Randomizer::GetRupeeMessage(u16 rupeeTextId) { return messageEntry; } +void CreateTriforcePieceMessages() { + CustomMessage TriforcePieceMessages[NUM_TRIFORCE_PIECE_MESSAGES] = { + + { "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. It's a start!", + "Ein %yTriforce-Splitter%w! Du hast&%g{{current}}%w von %c{{required}}%w gefunden. Es ist ein&Anfang!", + "Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. C'est un début!" }, + + { "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Progress!", + "Ein %yTriforce-Splitter%w! Du hast&%g{{current}}%w von %c{{required}}%w gefunden. Es geht voran!", + "Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. Ça avance!" }, + + { "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Over half-way&there!", + "Ein %yTriforce-Splitter%w! Du hast&schon %g{{current}}%w von %c{{required}}%w gefunden. Schon&über die Hälfte!", + "Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. Il en reste un&peu moins que la moitié!" }, + + { "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Almost done!", + "Ein %yTriforce-Splitter%w! Du hast&schon %g{{current}}%w von %c{{required}}%w gefunden. Fast&geschafft!", + "Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. C'est presque&terminé!" }, + + { "You completed the %yTriforce of&Courage%w! %gGG%w!", + "Das %yTriforce des Mutes%w! Du hast&alle Splitter gefunden. %gGut gemacht%w!", + "Vous avez complété la %yTriforce&du Courage%w! %gFélicitations%w!" }, + + { "You found a spare %yTriforce Piece%w!&You only needed %c{{required}}%w, but you have %g{{current}}%w!", + "Ein übriger %yTriforce-Splitter%w! Du&hast nun %g{{current}}%w von %c{{required}}%w nötigen gefunden.", + "Vous avez trouvé un %yFragment de&Triforce%w en plus! Vous n'aviez besoin&que de %c{{required}}%w, mais vous en avez %g{{current}}%w en&tout!" }, + }; + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; + customMessageManager->AddCustomMessageTable(Randomizer::triforcePieceMessageTableID); + for (unsigned int i = 0; i <= (NUM_TRIFORCE_PIECE_MESSAGES - 1); i++) { + customMessageManager->CreateMessage(Randomizer::triforcePieceMessageTableID, i, TriforcePieceMessages[i]); + } +} + +CustomMessage Randomizer::GetTriforcePieceMessage() { + // Item is only given after the textbox, so reflect that inside the textbox. + uint16_t current = gSaveContext.triforcePiecesCollected + 1; + uint16_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); + uint16_t remaining = required - current; + float percentageCollected = (float)current / (float)required; + uint8_t messageIndex; + + if (percentageCollected <= 0.25) { + messageIndex = TH_MESSAGE_START; + } else if (percentageCollected <= 0.5) { + messageIndex = TH_MESSAGE_PROGRESS; + } else if (percentageCollected <= 0.75) { + messageIndex = TH_MESSAGE_HALFWAY; + } else if (percentageCollected < 1) { + messageIndex = TH_MESSAGE_ALMOSTDONE; + } else if (current == required) { + messageIndex = TH_MESSAGE_FINISHED; + } else { + messageIndex = TH_MESSAGE_SURPLUS; + } + + CustomMessage messageEntry = + CustomMessageManager::Instance->RetrieveMessage(Randomizer::triforcePieceMessageTableID, messageIndex); + messageEntry.Replace("{{current}}", std::to_string(current), std::to_string(current), std::to_string(current)); + messageEntry.Replace("{{remaining}}", std::to_string(remaining), std::to_string(remaining), std::to_string(remaining)); + messageEntry.Replace("{{required}}", std::to_string(required), std::to_string(required), std::to_string(required)); + return messageEntry; +} + void CreateNaviRandoMessages() { CustomMessage NaviMessages[NUM_NAVI_MESSAGES] = { @@ -5988,6 +6106,7 @@ void Randomizer::CreateCustomMessages() { }}; CreateGetItemMessages(&getItemMessages); CreateRupeeMessages(); + CreateTriforcePieceMessages(); CreateNaviRandoMessages(); CreateIceTrapRandoMessages(); CreateFireTempleGoronMessages(); @@ -6098,6 +6217,7 @@ void InitRandoItemTable() { GET_ITEM(RG_MAGIC_BEAN_PACK, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_MAGIC_BEAN_PACK), GET_ITEM(RG_TYCOON_WALLET, OBJECT_GI_PURSE, GID_WALLET_GIANT, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_TYCOON_WALLET), GET_ITEM(RG_PROGRESSIVE_BOMBCHUS, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_PROGRESSIVE_BOMBCHUS), + GET_ITEM(RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_TRIFORCE_PIECE), }; ItemTableManager::Instance->AddItemTable(MOD_RANDOMIZER); for (int i = 0; i < ARRAY_COUNT(extendedVanillaGetItemTable); i++) { @@ -6113,6 +6233,8 @@ void InitRandoItemTable() { randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawBossKey; } else if (randoGetItemTable[i].itemId == RG_DOUBLE_DEFENSE) { randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawDoubleDefense; + } else if (randoGetItemTable[i].itemId == RG_TRIFORCE_PIECE) { + randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawTriforcePiece; } ItemTableManager::Instance->AddItemEntry(MOD_RANDOMIZER, randoGetItemTable[i].itemId, randoGetItemTable[i]); } diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index c8a333593..82347d994 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -14,6 +14,7 @@ #include "soh/Enhancements/item-tables/ItemTableTypes.h" #define MAX_SEED_STRING_SIZE 1024 +#define NUM_TRIFORCE_PIECE_MESSAGES 6 #define NUM_NAVI_MESSAGES 19 #define NUM_ICE_TRAP_MESSAGES 23 #define NUM_GORON_MESSAGES 9 @@ -48,6 +49,7 @@ class Randomizer { static const std::string hintMessageTableID; static const std::string merchantMessageTableID; static const std::string rupeeMessageTableID; + static const std::string triforcePieceMessageTableID; static const std::string NaviRandoMessageTableID; static const std::string IceTrapRandoMessageTableID; static const std::string randoMiscHintsTableID; @@ -102,6 +104,7 @@ class Randomizer { CustomMessage GetMapGetItemMessageWithHint(GetItemEntry itemEntry); static void CreateCustomMessages(); static CustomMessage GetRupeeMessage(u16 rupeeTextId); + static CustomMessage GetTriforcePieceMessage(); bool CheckContainsVanillaItem(RandomizerCheck randoCheck); }; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index f80ef7595..9d77a9a29 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -847,6 +847,7 @@ typedef enum { RC_ZR_NEAR_GROTTOS_GOSSIP_STONE, RC_ZR_OPEN_GROTTO_GOSSIP_STONE, RC_GANONDORF_HINT, + RC_TRIFORCE_COMPLETED, RC_MAX } RandomizerCheck; @@ -1302,6 +1303,7 @@ typedef enum { RG_BUY_RED_POTION_40, RG_BUY_RED_POTION_50, RG_TRIFORCE, + RG_TRIFORCE_PIECE, RG_HINT, RG_TYCOON_WALLET, RG_MAX @@ -1453,6 +1455,9 @@ typedef enum { RSK_ALL_LOCATIONS_REACHABLE, RSK_SHUFFLE_BOSS_ENTRANCES, RSK_SHUFFLE_100_GS_REWARD, + RSK_TRIFORCE_HUNT, + RSK_TRIFORCE_HUNT_PIECES_TOTAL, + RSK_TRIFORCE_HUNT_PIECES_REQUIRED, RSK_MAX } RandomizerSettingKey; @@ -1613,6 +1618,7 @@ typedef enum { RO_GANON_BOSS_KEY_LACS_DUNGEONS, RO_GANON_BOSS_KEY_LACS_TOKENS, RO_GANON_BOSS_KEY_KAK_TOKENS, + RO_GANON_BOSS_KEY_TRIFORCE_HUNT, } RandoOptionGanonsBossKey; // LACS Reward Options settings (Standard rewards, Greg as reward, Greg as wildcard) @@ -1806,3 +1812,12 @@ typedef enum { TRACKER_COMBO_BUTTON_D_LEFT, TRACKER_COMBO_BUTTON_D_RIGHT, } TrackerComboButton; + +typedef enum { + TH_MESSAGE_START, + TH_MESSAGE_PROGRESS, + TH_MESSAGE_HALFWAY, + TH_MESSAGE_ALMOSTDONE, + TH_MESSAGE_FINISHED, + TH_MESSAGE_SURPLUS, +} TriforceHuntMessages; diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 884f70071..bfb44c6c7 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -80,6 +80,10 @@ std::vector gregItems = { ITEM_TRACKER_ITEM(ITEM_RUPEE_GREEN, 0, DrawItem), }; +std::vector triforcePieces = { + ITEM_TRACKER_ITEM(RG_TRIFORCE_PIECE, 0, DrawItem), +}; + std::vector itemTrackerDungeonsWithMapsHorizontal = { { SCENE_DEKU_TREE, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, { SCENE_DODONGOS_CAVERN, { ITEM_DUNGEON_MAP, ITEM_COMPASS } }, @@ -243,6 +247,11 @@ typedef enum { KEYS_CURRENT_MAX } ItemTrackerKeysNumberOption; +typedef enum { + TRIFORCE_PIECE_COLLECTED_REQUIRED, + TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX +} ItemTrackerTriforcePieceNumberOption; + typedef enum { SECTION_DISPLAY_HIDDEN, SECTION_DISPLAY_MAIN_WINDOW, @@ -488,6 +497,37 @@ void DrawItemCount(ItemTrackerItem item) { ImGui::PushStyleColor(ImGuiCol_Text, maxColor); ImGui::Text("%s", maxString.c_str()); ImGui::PopStyleColor(); + } else if (item.id == RG_TRIFORCE_PIECE && gSaveContext.n64ddFlag && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) && IsValidSaveFile()) { + std::string currentString = ""; + std::string requiredString = ""; + std::string maxString = ""; + uint8_t piecesRequired = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED); + uint8_t piecesTotal = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_TOTAL); + ImU32 currentColor = gSaveContext.triforcePiecesCollected >= piecesRequired ? IM_COL_GREEN : IM_COL_WHITE; + ImU32 maxColor = IM_COL_GREEN; + int32_t trackerTriforcePieceNumberDisplayMode = CVarGetInteger("gItemTrackerTriforcePieceTrack", TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX); + + currentString += std::to_string(gSaveContext.triforcePiecesCollected); + currentString += "/"; + // gItemTrackerTriforcePieceTrack + if (trackerTriforcePieceNumberDisplayMode == TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX) { + currentString += std::to_string(piecesRequired); + currentString += "/"; + maxString += std::to_string(piecesTotal); + } else if (trackerTriforcePieceNumberDisplayMode == TRIFORCE_PIECE_COLLECTED_REQUIRED) { + maxString += std::to_string(piecesRequired); + } + + ImGui::SetCursorScreenPos( + ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize((currentString + maxString).c_str()).x / 2), p.y - 14)); + ImGui::PushStyleColor(ImGuiCol_Text, currentColor); + ImGui::Text(currentString.c_str()); + ImGui::PopStyleColor(); + ImGui::SameLine(0, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, maxColor); + ImGui::Text(maxString.c_str()); + ImGui::PopStyleColor(); } else { ImGui::SetCursorScreenPos(ImVec2(p.x, p.y - 14)); ImGui::Text(""); @@ -520,9 +560,11 @@ void DrawQuest(ItemTrackerItem item) { }; void DrawItem(ItemTrackerItem item) { + uint32_t actualItemId = INV_CONTENT(item.id); int iconSize = CVarGetInteger("gItemTrackerIconSize", 36); bool hasItem = actualItemId != ITEM_NONE; + std::string itemName = ""; if (item.id == ITEM_NONE) { return; @@ -562,6 +604,11 @@ void DrawItem(ItemTrackerItem item) { actualItemId = item.id; hasItem = Flags_GetRandomizerInf(RAND_INF_GREG_FOUND); break; + case RG_TRIFORCE_PIECE: + actualItemId = item.id; + hasItem = gSaveContext.n64ddFlag && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT); + itemName = "Triforce Piece"; + break; } if (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end()) { @@ -569,13 +616,18 @@ void DrawItem(ItemTrackerItem item) { } ImGui::BeginGroup(); + ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasItem && IsValidSaveFile() ? item.name : item.nameFaded), ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); - + DrawItemCount(item); ImGui::EndGroup(); - UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(item.id)); + if (itemName == "") { + itemName = SohUtils::GetItemName(item.id); + } + + UIWidgets::SetLastItemHoverText(itemName); } void DrawBottle(ItemTrackerItem item) { @@ -891,6 +943,19 @@ void UpdateVectors() { mainWindowItems.insert(mainWindowItems.end(), gregItems.begin(), gregItems.end()); } + // If we're adding triforce pieces to the main window + if (CVarGetInteger("gItemTrackerTriforcePiecesDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) { + // If Greg isn't on the main window, add empty items to place the triforce pieces on a new row. + if (CVarGetInteger("gItemTrackerGregDisplayType", SECTION_DISPLAY_EXTENDED_HIDDEN) != SECTION_DISPLAY_EXTENDED_MAIN_WINDOW) { + while (mainWindowItems.size() % 6) { + mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem)); + } + } + + // Add triforce pieces + mainWindowItems.insert(mainWindowItems.end(), triforcePieces.begin(), triforcePieces.end()); + } + shouldUpdateVectors = false; } @@ -914,6 +979,7 @@ void ItemTrackerWindow::DrawElement() { (CVarGetInteger("gItemTrackerSongsDisplayType", SECTION_DISPLAY_MAIN_WINDOW) == SECTION_DISPLAY_MAIN_WINDOW) || (CVarGetInteger("gItemTrackerDungeonItemsDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) || (CVarGetInteger("gItemTrackerGregDisplayType", SECTION_DISPLAY_EXTENDED_HIDDEN) == SECTION_DISPLAY_EXTENDED_MAIN_WINDOW) || + (CVarGetInteger("gItemTrackerTriforcePiecesDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) || (CVarGetInteger("gItemTrackerNotesDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) ) { BeginFloatingWindows("Item Tracker##main window"); @@ -984,6 +1050,12 @@ void ItemTrackerWindow::DrawElement() { EndFloatingWindows(); } + if (CVarGetInteger("gItemTrackerTriforcePiecesDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE) { + BeginFloatingWindows("Triforce Piece Tracker"); + DrawItemsInRows(triforcePieces); + EndFloatingWindows(); + } + if (CVarGetInteger("gItemTrackerNotesDisplayType", SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE && CVarGetInteger("gItemTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { ImGui::SetNextWindowSize(ImVec2(400,300), ImGuiCond_FirstUseEver); BeginFloatingWindows("Personal Notes", ImGuiWindowFlags_NoFocusOnAppearing); @@ -995,6 +1067,7 @@ void ItemTrackerWindow::DrawElement() { static const char* itemTrackerCapacityTrackOptions[5] = { "No Numbers", "Current Capacity", "Current Ammo", "Current Capacity / Max Capacity", "Current Ammo / Current Capacity" }; static const char* itemTrackerKeyTrackOptions[3] = { "Collected / Max", "Current / Collected / Max", "Current / Max" }; +static const char* itemTrackerTriforcePieceTrackOptions[2] = { "Collected / Required", "Collected / Required / Max" }; static const char* windowTypes[2] = { "Floating", "Window" }; static const char* displayModes[2] = { "Always", "Combo Button Hold" }; static const char* buttons[14] = { "A", "B", "C-Up", "C-Down", "C-Left", "C-Right", "L", "Z", "R", "Start", "D-Up", "D-Down", "D-Left", "D-Right" }; @@ -1002,7 +1075,7 @@ static const char* displayTypes[3] = { "Hidden", "Main Window", "Separate" }; static const char* extendedDisplayTypes[4] = { "Hidden", "Main Window", "Misc Window", "Separate" }; void ItemTrackerSettingsWindow::DrawElement() { - ImGui::SetNextWindowSize(ImVec2(600,375), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(733, 472), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Item Tracker Settings", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { ImGui::End(); @@ -1055,6 +1128,8 @@ void ItemTrackerSettingsWindow::DrawElement() { UIWidgets::EnhancementSliderInt("Icon size : %dpx", "##ITEMTRACKERICONSIZE", "gItemTrackerIconSize", 25, 128, "", 36); UIWidgets::EnhancementSliderInt("Icon margins : %dpx", "##ITEMTRACKERSPACING", "gItemTrackerIconSpacing", -5, 50, "", 12); + UIWidgets::Spacer(0); + ImGui::Text("Ammo/Capacity Tracking"); UIWidgets::EnhancementCombobox("gItemTrackerCapacityTrack", itemTrackerCapacityTrackOptions, ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY); UIWidgets::InsertHelpHoverText("Customize what the numbers under each item are tracking." @@ -1064,10 +1139,19 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } } + + UIWidgets::Spacer(0); + ImGui::Text("Key Count Tracking"); UIWidgets::EnhancementCombobox("gItemTrackerKeyTrack", itemTrackerKeyTrackOptions, KEYS_COLLECTED_MAX); UIWidgets::InsertHelpHoverText("Customize what numbers are shown for key tracking."); + UIWidgets::Spacer(0); + + ImGui::Text("Triforce Piece Count Tracking"); + UIWidgets::EnhancementCombobox("gItemTrackerTriforcePieceTrack", itemTrackerTriforcePieceTrackOptions, TRIFORCE_PIECE_COLLECTED_REQUIRED_MAX); + UIWidgets::InsertHelpHoverText("Customize what numbers are shown for triforce piece tracking."); + ImGui::TableNextColumn(); if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Inventory", "gItemTrackerInventoryItemsDisplayType", displayTypes, SECTION_DISPLAY_MAIN_WINDOW)) { @@ -1107,6 +1191,10 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } + if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Triforce Pieces", "gItemTrackerTriforcePiecesDisplayType", displayTypes, SECTION_DISPLAY_HIDDEN)) { + shouldUpdateVectors = true; + } + if (CVarGetInteger("gItemTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { if (UIWidgets::LabeledRightAlignedEnhancementCombobox("Personal notes", "gItemTrackerNotesDisplayType", displayTypes, SECTION_DISPLAY_HIDDEN)) { shouldUpdateVectors = true; diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index a6984fac0..ed7443910 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -436,5 +436,8 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } + // Reset triforce pieces collected + gSaveContext.triforcePiecesCollected = 0; + SetStartingItems(); } diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7e1845dd0..a9cf62515 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1975,6 +1975,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { if (CVarGetInteger("gLetItSnow", 0)) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::IceTrapRandoMessageTableID, NUM_ICE_TRAP_MESSAGES + 1); } + } else if (player->getItemEntry.getItemId == RG_TRIFORCE_PIECE) { + messageEntry = Randomizer::GetTriforcePieceMessage(); } else { messageEntry = Randomizer_GetCustomGetItemMessage(player); } diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index b17b2cbfe..9736a3e22 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -136,6 +136,8 @@ void SaveManager::LoadRandomizerVersion1() { SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems); + SaveManager::Instance->LoadData("triforcePiecesCollected", gSaveContext.triforcePiecesCollected); + SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.pendingIceTrapCount); std::shared_ptr randomizer = OTRGlobals::Instance->gRandomizer; @@ -251,6 +253,8 @@ void SaveManager::LoadRandomizerVersion2() { SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems); + SaveManager::Instance->LoadData("triforcePiecesCollected", gSaveContext.triforcePiecesCollected); + SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.pendingIceTrapCount); std::shared_ptr randomizer = OTRGlobals::Instance->gRandomizer; @@ -338,6 +342,8 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f SaveManager::Instance->SaveData("adultTradeItems", saveContext->adultTradeItems); + SaveManager::Instance->SaveData("triforcePiecesCollected", gSaveContext.triforcePiecesCollected); + SaveManager::Instance->SaveData("pendingIceTrapCount", saveContext->pendingIceTrapCount); std::shared_ptr randomizer = OTRGlobals::Instance->gRandomizer; diff --git a/soh/src/code/z_draw.c b/soh/src/code/z_draw.c index 7c6b71c6c..cb28a498e 100644 --- a/soh/src/code/z_draw.c +++ b/soh/src/code/z_draw.c @@ -78,6 +78,8 @@ #include "objects/object_gi_sword_1/object_gi_sword_1.h" #include "objects/object_st/object_st.h" +#include "soh_assets.h" + // "Get Item" Model Draw Functions void GetItem_DrawMaskOrBombchu(PlayState* play, s16 drawId); void GetItem_DrawSoldOut(PlayState* play, s16 drawId); @@ -110,6 +112,7 @@ void GetItem_DrawJewelKokiri(PlayState* play, s16 drawId); void GetItem_DrawJewelGoron(PlayState* play, s16 drawId); void GetItem_DrawJewelZora(PlayState* play, s16 drawId); void GetItem_DrawGenericMusicNote(PlayState* play, s16 drawId); +void GetItem_DrawTriforcePiece(PlayState* play, s16 drawId); typedef struct { /* 0x00 */ void (*drawFunc)(PlayState*, s16); @@ -384,7 +387,8 @@ DrawItemTableEntry sDrawItemTable[] = { { GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, //Saria's song { GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, //Sun's song { GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, //Song of time - { GetItem_DrawGenericMusicNote, { gGiSongNoteDL } } //Song of storms + { GetItem_DrawGenericMusicNote, { gGiSongNoteDL } }, //Song of storms + { GetItem_DrawTriforcePiece, { gTriforcePiece0DL } } // Triforce Piece }; /** @@ -1031,3 +1035,33 @@ void GetItem_DrawWallet(PlayState* play, s16 drawId) { CLOSE_DISPS(play->state.gfxCtx); } + +void GetItem_DrawTriforcePiece(PlayState* play, s16 drawId) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + + Matrix_Scale(0.035f, 0.035f, 0.035f, MTXMODE_APPLY); + + uint16_t index = gSaveContext.triforcePiecesCollected % 3; + Gfx* triforcePieceDL; + + switch (index) { + case 1: + triforcePieceDL = (Gfx*) gTriforcePiece1DL; + break; + case 2: + triforcePieceDL = (Gfx*) gTriforcePiece2DL; + break; + default: + triforcePieceDL = (Gfx*) gTriforcePiece0DL; + break; + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), + G_MTX_MODELVIEW | G_MTX_LOAD); + + gSPDisplayList(POLY_OPA_DISP++, triforcePieceDL); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 82cb8a6b6..93bf8fbfd 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2528,6 +2528,21 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { return Return_Item_Entry(giEntry, RG_NONE); } + if (item == RG_TRIFORCE_PIECE) { + gSaveContext.triforcePiecesCollected++; + GameInteractor_SetTriforceHuntPieceGiven(true); + + // Teleport to credits when goal is reached. + if (gSaveContext.triforcePiecesCollected == Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED)) { + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] = GAMEPLAYSTAT_TOTAL_TIME; + gSaveContext.sohStats.gameComplete = 1; + Play_PerformSave(play); + GameInteractor_SetTriforceHuntCreditsWarpActive(true); + } + + return Return_Item_Entry(giEntry, RG_NONE); + } + if (item == RG_PROGRESSIVE_BOMBCHUS) { if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 4232bf5ea..7e480ad27 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -2318,7 +2318,10 @@ void Play_PerformSave(PlayState* play) { } else { Save_SaveFile(); } - if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) { + uint8_t triforceHuntCompleted = + gSaveContext.n64ddFlag && gSaveContext.triforcePiecesCollected == Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) && + Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT); + if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF || triforceHuntCompleted) { Overlay_DisplayText(3.0f, "Game Saved"); } } diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 934df5f1b..98dacc982 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -7,6 +7,7 @@ #include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/randomizer/draw.h" #include @@ -1292,6 +1293,8 @@ void Player_DrawGetItemImpl(PlayState* play, Player* this, Vec3f* refPos, s32 dr if (this->getItemEntry.modIndex == MOD_RANDOMIZER && this->getItemEntry.getItemId == RG_ICE_TRAP) { Player_DrawGetItemIceTrap(play, this, refPos, drawIdPlusOne, height); + } else if (this->getItemEntry.modIndex == MOD_RANDOMIZER && this->getItemEntry.getItemId == RG_TRIFORCE_PIECE) { + Randomizer_DrawTriforcePieceGI(play, this->getItemEntry); } else if (this->getItemEntry.drawFunc != NULL) { this->getItemEntry.drawFunc(play, &this->getItemEntry); } else {