From c91588d8af126f68f032b65e552f6d8a029ded22 Mon Sep 17 00:00:00 2001 From: Caladius Date: Thu, 30 Oct 2025 11:49:39 -0400 Subject: [Rando] Implement Traps (#1287) * Initial Trap Implementation with 1 Trap Example. * New TrapTypes and Per Trap Custom Messages * Spruce up Menu and add placeholders for toggling traps * Reordering and clean up * Seeded Trap Models * Trap Type Opt-in * Add Jinx Trap * Add Enemy (Like Like) Trap * Fix Shop Item draws and convert for Progressive * Placeholder for Custom Model * Implemented time trap[Please Squash this] (#13) * Implemented time trap * Finished comment because im braindead today * Forgot to fix indentation(whoops) * Changed things not mess with the games code, added some more Time Trap messages * Removed a include I forgot to yoink from the base code. * Removed header define for VerifyTimeSkip * Clangit * Missed some leftovers from a merge conflict. * Clang format throwing a fit over a empty line * Added Timeskip CVar placeholder * Fixed issue where game crashes past midnight * Added Moonfall skipping prevention messures. * Added fallback loop to handle the remaining cycles in the event of a overflow. * Cleaner implementation * Keep it simple stupid * changed function name to be more accurate * Removed outdated comment, used TimeSkipInc for moonfall skip prevention * Reverted max trap(accidentally pushed it while testing) * Removed old unused value * Reduced the iteration by 1 to be closer to where it was at before. * Will this fix Windows? * Added TRAP_TIME to trapToCvarMap * Corrected typo on comment * Clangit...again * Added calls to load environment values and adjust the weather. * Update Time Trap and clean up code * Add Flash effect to Time Trap * Added a few more timeTrapMessages * I hate you clang format * Fix Trading Post Kick Out Time * Added map for all locations I can think of that close. * Clangit yet again * Removed Inn handling because the edge case related to it closing cannot happen * Corrected a whoopie I made when I was dealing with a conflict * Removed pointless condition check that is always true * Removed duped variable * Removed unnecessary data from SCENE_8ITEMSHO kickout entry * Removed now used TRADING_POS_ACTOR_PARAM --------- Co-authored-by: Caladius * Comment out Kick Out code for later TODO * Clean out commented code * Adds Wallet Trap and updates some tooltips * More Trap Messages * Even more trap messages * moooooore messages * update for clang * Feedback changes for Time Trap * Update Trap functions * Fix Matrix call * Trap Model Import (Thanks Zrc) * rename * Adjust size * update name for uniformity * Update mm/2s2h/Rando/MiscBehavior/Traps.cpp Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com> * Update mm/2s2h/Rando/MiscBehavior/Traps.cpp Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com> * feedback updates * Update notifications to remove color codes from trap messages when item skip is on * VBify Object Dependency * Fix comma * fix headers * Refactor ObjectDependency hook * Check if item get cutscenes are being skipped * Use brackets for LUS include * Header update --------- Co-authored-by: sitton76 <58642183+sitton76@users.noreply.github.com> Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com> --- mm/2s2h/CustomMessage/CustomMessage.cpp | 14 + mm/2s2h/CustomMessage/CustomMessage.h | 1 + mm/2s2h/DeveloperTools/ObjectDependency.cpp | 16 ++ mm/2s2h/GameInteractor/GameInteractor.cpp | 11 +- mm/2s2h/GameInteractor/GameInteractor.h | 13 +- mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp | 1 + mm/2s2h/Rando/ActorBehavior/ActorBehavior.h | 1 + mm/2s2h/Rando/ActorBehavior/EnGirlA.cpp | 4 + mm/2s2h/Rando/DrawItem.cpp | 66 +++++ mm/2s2h/Rando/GiveItem.cpp | 4 + mm/2s2h/Rando/Menu.cpp | 66 ++++- mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp | 21 +- mm/2s2h/Rando/MiscBehavior/MiscBehavior.h | 1 + mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp | 12 + mm/2s2h/Rando/MiscBehavior/Traps.cpp | 282 +++++++++++++++++++++ mm/2s2h/Rando/MiscBehavior/Traps.h | 10 + mm/2s2h/Rando/StaticData/Items.cpp | 1 + mm/2s2h/Rando/StaticData/Options.cpp | 2 + mm/2s2h/Rando/StaticData/StaticData.h | 1 + mm/2s2h/Rando/Types.h | 4 + mm/assets/2s2h_assets.h | 3 + mm/assets/custom/objects/object_trap/gTrapDL | 11 + mm/assets/custom/objects/object_trap/ice_2 | Bin 0 -> 4188 bytes .../objects/object_trap/mat_object_trap_model_ice | 21 ++ .../objects/object_trap/object_trap_model_tri_0 | 158 ++++++++++++ .../objects/object_trap/object_trap_model_vtx_0 | 156 ++++++++++++ mm/src/code/z_scene.c | 3 +- 27 files changed, 866 insertions(+), 17 deletions(-) create mode 100644 mm/2s2h/DeveloperTools/ObjectDependency.cpp create mode 100644 mm/2s2h/Rando/MiscBehavior/Traps.cpp create mode 100644 mm/2s2h/Rando/MiscBehavior/Traps.h create mode 100644 mm/assets/custom/objects/object_trap/gTrapDL create mode 100644 mm/assets/custom/objects/object_trap/ice_2 create mode 100644 mm/assets/custom/objects/object_trap/mat_object_trap_model_ice create mode 100644 mm/assets/custom/objects/object_trap/object_trap_model_tri_0 create mode 100644 mm/assets/custom/objects/object_trap/object_trap_model_vtx_0 diff --git a/mm/2s2h/CustomMessage/CustomMessage.cpp b/mm/2s2h/CustomMessage/CustomMessage.cpp index b2b1cd311..ff3a16152 100644 --- a/mm/2s2h/CustomMessage/CustomMessage.cpp +++ b/mm/2s2h/CustomMessage/CustomMessage.cpp @@ -9,6 +9,20 @@ extern f32 sNESFontWidths[160]; CustomMessage::Entry activeCustomMessage; +std::string CustomMessage::RemoveColorCodes(const std::string& input) { + std::string output = input; + const std::vector codes = { "%r", "%w", "%y", "%g", "%b", "%p" }; + + for (const auto& code : codes) { + size_t pos = 0; + while ((pos = output.find(code, pos)) != std::string::npos) { + output.erase(pos, code.length()); + } + } + + return output; +} + void CustomMessage::StartTextbox(std::string msg, CustomMessage::Entry options) { options.msg = msg; activeCustomMessage = options; diff --git a/mm/2s2h/CustomMessage/CustomMessage.h b/mm/2s2h/CustomMessage/CustomMessage.h index a1668ca3f..6a9a0761a 100644 --- a/mm/2s2h/CustomMessage/CustomMessage.h +++ b/mm/2s2h/CustomMessage/CustomMessage.h @@ -32,6 +32,7 @@ void StartTextbox(std::string msg, Entry options = {}); void SetActiveCustomMessage(std::string msg, Entry options = {}); // Helpers +std::string RemoveColorCodes(const std::string& input); void Replace(std::string* msg, const std::string& placeholder, const std::string& value); void AddLineBreaks(std::string* msg); void ReplaceColorChars(std::string* msg); diff --git a/mm/2s2h/DeveloperTools/ObjectDependency.cpp b/mm/2s2h/DeveloperTools/ObjectDependency.cpp new file mode 100644 index 000000000..6811df9df --- /dev/null +++ b/mm/2s2h/DeveloperTools/ObjectDependency.cpp @@ -0,0 +1,16 @@ +#include +#include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/ShipInit.hpp" + +extern "C" { +#include "variables.h" +} + +#define CVAR_NAME "gDeveloperTools.DisableObjectDependency" +#define CVAR CVarGetInteger(CVAR_NAME, 0) + +void RegisterObjectDependency() { + COND_VB_SHOULD(VB_ENABLE_OBJECT_DEPENDENCY, CVAR, { *should = false; }); +} + +static RegisterShipInitFunc initFunc(RegisterObjectDependency, { CVAR_NAME }); diff --git a/mm/2s2h/GameInteractor/GameInteractor.cpp b/mm/2s2h/GameInteractor/GameInteractor.cpp index 7c3dd4d26..487af4f19 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/GameInteractor/GameInteractor.cpp @@ -510,10 +510,15 @@ void ProcessEvents(Actor* actor) { f32 x2 = e->posX * c - e->posZ * s; f32 z2 = e->posX * s + e->posZ * c; Actor_Spawn(&gPlayState->actorCtx, gPlayState, e->actorId, x + x2, y + e->posY, z + z2, 0, - e->rot + player->actor.world.rot.y, 0, e->params); + e->rotY + player->actor.world.rot.y, 0, e->params); } else { - Actor_Spawn(&gPlayState->actorCtx, gPlayState, e->actorId, e->posX, e->posY, e->posZ, 0, e->rot, 0, - e->params); + Actor_Spawn(&gPlayState->actorCtx, gPlayState, e->actorId, e->posX, e->posY, e->posZ, e->rotX, e->rotY, + e->rotZ, e->params); + } + GameInteractor::Instance->currentEvent = GIEventNone{}; + } else if (auto e = std::get_if(&nextEvent)) { + if (e->action) { + e->action(); } GameInteractor::Instance->currentEvent = GIEventNone{}; } diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 8c44f5b5a..c0e74180a 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -255,7 +255,8 @@ typedef enum { VB_SET_CAMERA_FOV, VB_USE_ITEM_CONSIDER_ITEM_ACTION, VB_ENEMY_DROP_COLLECTIBLE, - VB_DRAW_SLIME_RANDO_ITEM + VB_DRAW_SLIME_RANDO_ITEM, + VB_ENABLE_OBJECT_DEPENDENCY, } GIVanillaBehavior; typedef enum { @@ -369,7 +370,9 @@ struct GIEventSpawnActor { f32 posX; f32 posY; f32 posZ; - s16 rot; + s16 rotX; + s16 rotY; + s16 rotZ; s32 params; // if true, the coordinates are made relative to the player's position and rotation, 0 rotation is facing the same // direction as the player, x+ is to the players right, y+ is up, z+ is in front of the player @@ -383,7 +386,11 @@ struct GIEventTransition { u8 transitionType; }; -typedef std::variant GIEvent; +struct GIEventTrap { + std::function action; +}; + +typedef std::variant GIEvent; class GameInteractor { public: diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp index 04600c8bd..af100a565 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp @@ -142,6 +142,7 @@ void Rando::ActorBehavior::OnFileLoad() { Rando::ActorBehavior::InitObjWarpstoneBehavior(); Rando::ActorBehavior::InitPlayerBehavior(); Rando::ActorBehavior::InitSoulsBehavior(); + Rando::ActorBehavior::InitTrapsBehavior(); COND_HOOK(ShouldVanillaBehavior, IS_RANDO, MiscVanillaBehaviorHandler); } diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h index 30b82dfda..1a9a3dd64 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h @@ -98,6 +98,7 @@ void InitObjTsuboBehavior(); void InitObjWarpstoneBehavior(); void InitPlayerBehavior(); void InitSoulsBehavior(); +void InitTrapsBehavior(); } // namespace ActorBehavior diff --git a/mm/2s2h/Rando/ActorBehavior/EnGirlA.cpp b/mm/2s2h/Rando/ActorBehavior/EnGirlA.cpp index 5f20dba6b..6ee339972 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnGirlA.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnGirlA.cpp @@ -1,6 +1,7 @@ #include "ActorBehavior.h" #include #include "2s2h/CustomMessage/CustomMessage.h" +#include "2s2h/Rando/MiscBehavior/Traps.h" extern "C" { #include "variables.h" @@ -67,6 +68,9 @@ void EnGirlA_RandoBuyFunc(PlayState* play, EnGirlA* enGirlA) { RandoItemId randoItemId = Rando::ConvertItem(randoSaveCheck.randoItemId, (RandoCheckId)enGirlA->actor.world.rot.z); randoSaveCheck.obtained = true; Rupees_ChangeBy(-play->msgCtx.unk1206C); + if (randoItemId == RI_TRAP) { + RollTrapType(); + } Rando::GiveItem(randoItemId); } diff --git a/mm/2s2h/Rando/DrawItem.cpp b/mm/2s2h/Rando/DrawItem.cpp index 54ccd568b..73e752e4f 100644 --- a/mm/2s2h/Rando/DrawItem.cpp +++ b/mm/2s2h/Rando/DrawItem.cpp @@ -16,6 +16,8 @@ extern "C" { #include "objects/object_sek/object_sek.h" #include "objects/object_st/object_st.h" +#include "assets/overlays/ovl_Arrow_Ice/ovl_Arrow_Ice.h" + Gfx* ResourceMgr_LoadGfxByName(const char* path); } @@ -297,6 +299,64 @@ void DrawSkulltulaToken(RandoItemId randoItemId, Actor* actor) { CLOSE_DISPS(gPlayState->state.gfxCtx); } +void DrawTrapModel() { + 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); + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gTrapDL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); +} + +void DrawRandomTrapModel(RandoItemId randoItemId, Actor* actor) { + uint32_t seed = gSaveContext.save.shipSaveInfo.rando.finalSeed / 100000; + int actorData = (int)gPlayState->sceneId + seed; + + if (actor != NULL) { + actorData += abs(actor->home.pos.x + actor->home.pos.y + actor->params); + } + + int drawRandoItemId = actorData % ((int)RI_MAX - 3); + + if (drawRandoItemId == RI_UNKNOWN || drawRandoItemId >= RI_TRAP) { + drawRandoItemId++; + } + + // Handle Progressive Items + switch (drawRandoItemId) { + case RI_BOMB_BAG_20: + case RI_BOMB_BAG_30: + case RI_BOMB_BAG_40: + drawRandoItemId = RI_PROGRESSIVE_BOMB_BAG; + break; + case RI_BOW: + case RI_QUIVER_40: + case RI_QUIVER_50: + drawRandoItemId = RI_PROGRESSIVE_BOW; + break; + case RI_SINGLE_MAGIC: + case RI_DOUBLE_MAGIC: + drawRandoItemId = RI_PROGRESSIVE_MAGIC; + break; + case RI_SWORD_GILDED: + case RI_SWORD_KOKIRI: + case RI_SWORD_RAZOR: + drawRandoItemId = RI_PROGRESSIVE_SWORD; + break; + case RI_WALLET_ADULT: + case RI_WALLET_GIANT: + drawRandoItemId = RI_PROGRESSIVE_WALLET; + break; + default: + break; + } + + Rando::DrawItem((RandoItemId)drawRandoItemId, actor); +} + void DrawTriforcePiece(RandoItemId randoItemId) { Gfx* triforcePieceModels[3] = { (Gfx*)gTriforcePiece0DL, @@ -473,6 +533,12 @@ void Rando::DrawItem(RandoItemId randoItemId, Actor* actor) { case RI_TRIFORCE_PIECE: DrawTriforcePiece(randoItemId); break; + case RI_TRAP: + DrawRandomTrapModel(randoItemId, actor); + break; + case RI_MAX_TRAP: + DrawTrapModel(); + break; case RI_NONE: case RI_UNKNOWN: break; diff --git a/mm/2s2h/Rando/GiveItem.cpp b/mm/2s2h/Rando/GiveItem.cpp index 28a9b1819..f76cfbed8 100644 --- a/mm/2s2h/Rando/GiveItem.cpp +++ b/mm/2s2h/Rando/GiveItem.cpp @@ -1,4 +1,5 @@ #include "Rando/Rando.h" +#include "Rando/MiscBehavior/MiscBehavior.h" extern "C" { #include "variables.h" @@ -288,6 +289,9 @@ void Rando::GiveItem(RandoItemId randoItemId) { case RI_ABILITY_SWIM: Flags_SetRandoInf(RANDO_INF_OBTAINED_SWIM); break; + case RI_TRAP: + Rando::MiscBehavior::OfferTrapItem(); + break; case RI_JUNK: case RI_NONE: break; diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index b5fc784c2..4f489d03a 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -290,6 +290,14 @@ static void DrawItemsTab() { CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); CVarCheckbox("Infinite Upgrades", "gPlaceholderBool", CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Song of Double Time", "gPlaceholderBool", + CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Inverted Song of Time", "gPlaceholderBool", + CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Saria's Song", "gPlaceholderBool", + CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Sun's Song", "gPlaceholderBool", + CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); ImGui::EndChild(); ImGui::SameLine(); ImGui::BeginChild("randoItemsColumn2", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); @@ -313,14 +321,56 @@ static void DrawItemsTab() { ImGui::EndChild(); ImGui::SameLine(); ImGui::BeginChild("randoItemsColumn3", ImVec2(columnWidth, ImGui::GetContentRegionAvail().y)); - CVarCheckbox("Song of Double Time", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); - CVarCheckbox("Inverted Song of Time", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); - CVarCheckbox("Saria's Song", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); - CVarCheckbox("Sun's Song", "gPlaceholderBool", - CheckboxOptions({ { .disabled = true, .disabledTooltip = "Coming Soon" } })); + CVarCheckbox("Shuffle Traps", Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, + CheckboxOptions({ { .tooltip = "Ice Trap time!" } })); + CVarSliderInt( + "##trapcount", Rando::StaticData::Options[RO_TRAP_AMOUNT].cvar, + IntSliderOptions({ { .tooltip = "How many Traps are shuffled into the Item Pool.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } }) + .LabelPosition(LabelPosition::None) + .Color(UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5))) + .Format("Traps: %i") + .Min(1) + .Max(10) + .DefaultValue(5)); + ImGui::SeparatorText("Toggle Trap Types"); + CVarCheckbox( + "Freeze Traps", "gRando.Traps.Freeze", + CheckboxOptions({ { .tooltip = "Freezes Link in place.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( + "Blast Traps", "gRando.Traps.Blast", + CheckboxOptions({ { .tooltip = "Link explodes with Powder Keg force.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( + "Shock Traps", "gRando.Traps.Shock", + CheckboxOptions({ { .tooltip = "Shocks Link for a few seconds.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( + "Jinx Traps", "gRando.Traps.Jinx", + CheckboxOptions({ { .tooltip = "Afflicts Link with Jinx.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( + "Wallet Traps", "gRando.Traps.Wallet", + CheckboxOptions({ { .tooltip = "Links rupees scatter around him.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( // This only spawns a Like Like, more enemies may be added in the future but each would need fine + // tuning + "Like Like Traps", "gRando.Traps.Enemy", + CheckboxOptions({ { .tooltip = "Spawns a Like Like on top of Link.", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); + CVarCheckbox( + "Time Traps", "gRando.Traps.Time", + CheckboxOptions({ { .tooltip = "Advances Time 90 Minutes (Game Time).", + .disabled = (bool)!CVarGetInteger(Rando::StaticData::Options[RO_SHUFFLE_TRAPS].cvar, 0), + .disabledTooltip = "Shuffle Traps is disabled." } })); ImGui::EndChild(); } diff --git a/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp b/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp index bef7eb663..9f372fe4f 100644 --- a/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp +++ b/mm/2s2h/Rando/MiscBehavior/CheckQueue.cpp @@ -1,13 +1,16 @@ #include "MiscBehavior.h" +#include #include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/CustomItem/CustomItem.h" #include "2s2h/CustomMessage/CustomMessage.h" #include "2s2h/BenGui/Notification.h" #include "2s2h/Rando/StaticData/StaticData.h" #include "2s2h/ShipUtils.h" +#include "Traps.h" extern "C" { #include "variables.h" +#include extern TexturePtr gItemIcons[131]; extern s16 D_801CFF94[250]; } @@ -58,10 +61,20 @@ void Rando::MiscBehavior::CheckQueue() { randoItemId = RI_TRIFORCE_PIECE_PREVIOUS; } + if (randoItemId == RI_TRAP) { + prefix = ""; + message = GetTrapMessage(); + // We need to remove the Color Codes if the player is skipping Item Get Cutscenes as the + // Notification Emit doesnt support it. + if (CVarGetInteger("gEnhancements.Cutscenes.SkipGetItemCutscenes", 0) >= 2) { + message = CustomMessage::RemoveColorCodes(message); + } + } + CustomMessage::Entry entry = { .textboxType = 2, .icon = Rando::StaticData::GetIconForZMessage(randoItemId), - .msg = prefix + " " + message + "!", + .msg = (prefix == "" ? "" : prefix + " ") + message + (randoItemId == RI_TRAP ? "" : "!"), }; if (CUSTOM_ITEM_FLAGS & CustomItem::GIVE_ITEM_CUTSCENE) { @@ -88,7 +101,11 @@ void Rando::MiscBehavior::CheckQueue() { // If the item has been given, the CUSTOM_ITEM_PARAM is set to the RI, prior to that it's the RC if (CUSTOM_ITEM_FLAGS & CustomItem::CALLED_ACTION) { - randoItemId = (RandoItemId)CUSTOM_ITEM_PARAM; + if ((RandoItemId)CUSTOM_ITEM_PARAM == RI_TRAP) { + randoItemId = RI_MAX_TRAP; + } else { + randoItemId = (RandoItemId)CUSTOM_ITEM_PARAM; + } } else { auto& randoSaveCheck = RANDO_SAVE_CHECKS[CUSTOM_ITEM_PARAM]; randoItemId = diff --git a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h index 18432c372..d0f93f436 100644 --- a/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h +++ b/mm/2s2h/Rando/MiscBehavior/MiscBehavior.h @@ -21,6 +21,7 @@ void OnFileCreate(s16 fileNum); void OnFlagSet(FlagType flagType, u32 flag); void OnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); void OnSceneInit(s16 sceneId, s8 spawnNum); +void OfferTrapItem(); } // namespace MiscBehavior diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp index f41e47afc..6edec7fb1 100644 --- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp +++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp @@ -323,6 +323,18 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) { throw std::runtime_error("No items in logic"); } + // Handle Shuffling Traps + if (RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRAPS] == RO_GENERIC_YES) { + for (int i = 0; i < RANDO_SAVE_OPTIONS[RO_TRAP_AMOUNT]; i++) { + for (int j = 0; j < itemPool.size(); j++) { + if (itemPool[j] == RI_JUNK) { + itemPool[j] = RI_TRAP; + break; + } + } + } + } + int heartPiecesRemoved = 0; // Add/Remove junk items to/from the pool to make the item pool size match the check pool size while (checkPool.size() != itemPool.size()) { diff --git a/mm/2s2h/Rando/MiscBehavior/Traps.cpp b/mm/2s2h/Rando/MiscBehavior/Traps.cpp new file mode 100644 index 000000000..6a7528dc2 --- /dev/null +++ b/mm/2s2h/Rando/MiscBehavior/Traps.cpp @@ -0,0 +1,282 @@ +#include "Traps.h" +#include +#include "MiscBehavior.h" +#include "Rando/ActorBehavior/ActorBehavior.h" +#include "2s2h/DeveloperTools/SaveEditor.h" + +extern "C" { +#include "variables.h" +#include "functions.h" +#include "overlays/actors/ovl_En_Time_Tag/z_en_time_tag.h" +void func_80833B18(PlayState* play, Player* thisx, s32 arg2, f32 speed, f32 velocityY, s16 arg5, + s32 invincibilityTimer); +void EnTimeTag_KickOut_Transition(EnTimeTag* enTimeTag, PlayState* play); +} + +#define MORNING_TIME 0x4000 +#define DAY_LENGTH 0x10000 +// Adjust so that 6 A.M. is 0 and 5:59 A.M. is 0xFFFF +#define ZERO_DAY_START(time) (((u16)(time - MORNING_TIME) % DAY_LENGTH)) + +extern void UpdateGameTime(u16 gameTime); + +int roll = TRAP_FREEZE; +const u16 timeSkipInterval = 4000; + +std::map trapToCvarMap = { + { TRAP_FREEZE, "gRando.Traps.Freeze" }, { TRAP_BLAST, "gRando.Traps.Blast" }, + { TRAP_SHOCK, "gRando.Traps.Shock" }, { TRAP_JINX, "gRando.Traps.Jinx" }, + { TRAP_WALLET, "gRando.Traps.Wallet" }, { TRAP_ENEMY, "gRando.Traps.Enemy" }, + { TRAP_TIME, "gRando.Traps.Time" }, +}; + +std::vector getEnabledTrapTypes() { + std::vector enabledTrapTypes; + for (auto& trap : trapToCvarMap) { + if (CVarGetInteger(trap.second, 0)) { + enabledTrapTypes.push_back(trap.first); + } + } + if (enabledTrapTypes.size() == 0) { + enabledTrapTypes.push_back(TRAP_FREEZE); + } + return enabledTrapTypes; +}; + +int RollTrapType() { + auto enabledTraps = getEnabledTrapTypes(); + roll = enabledTraps[rand() % enabledTraps.size()]; + return roll; +} + +// clang-format off +std::vector defaultTrapMessages = { + "This item is available in the %bRando DLC%w.", + "This is what happens when %gCaladius%w is left unsupervised.", + "Oh no!", + "Uh oh!", + "KEKW", + "We've been trying to reach you about your Horse's %gextended warranty%w.", + "Admit it, you wish this was %gGreg%w.", + "Error 404: Item Not Found", + "Get dunked on!", + "This %rTrap%w is brought to you by today's sponsor...\n%gRaid Shadow Legends%w!", +}; + +std::vector freezeTrapMessages = { + "%rOcarina of Time%w called, they want their %bIce Trap%w back.", + "Greetings from %bSnowhead%w! Wish you were here.", + "This item was too %bcool%w for you anyway.", + "Let me see your best %bKing Zora%w impersonation.", + "There's no business like %bSnow%w business!", + "How much does a polar bear weigh? Enough to break the %bice%w.", + "You found the %yTrifo%w... Wait, nevermind...", + "Quick Time Event! Don't die.", +}; + +std::vector blastTrapMessages = { + "Coming to you live from the %yThunderdome%w!", + "There was supposed to be an Earth shattering %yKaboom%w!", + "Pardon me while I %yburst%w.", + "This is not the greatest %yblast%w in the world, this is just a tribute.", + "Hey look, this %yitem%w is ticking!", + "This is sure to make %gPamela%w leave the house.", + "You found a %yDodongo Snack%w!", +}; + +std::vector shockTrapMessages = { + "We're losing him!\n%gCLEAR%w", + "It's got what %gplants%w need.", + "On todays episode of Grey's Anatomy...", + "I'm giving it all I've got, captain!", + "Now simulating a CD in a Microwave.", + "Hang in there, for %gMeryl%w." +}; + +std::vector jinxTrapMessages = { + "Looks like someone's got a case of the %bMondays%w. :( ", + "Your wrist hurts! The doctor says no more swords.", +}; + +std::vector walletTrapMessages = { + "Spare some %rchange%w?", + "%rBreaking News%w: the Moon hasn't crashed, but Termina's economy sure has." +}; + +std::vector enemyTrapMessages = { + "You made a new friend!", + // Like Like Specific, will need to adjust if new enemies are added. + "Someone likes you! They %rLike Like%w you!", + "You don't need a shield anyway.", + "This item sucks.", +}; + +std::vector timeTrapMessages = { + "%yTime%w flashes before your eyes!", + "You have played the %ySun's Song%w!", + "The %rGoddess of Time%w smites you!", + "Spent an hour and a half admiring this %bfake item%w.", + "You found a great place to take a nap!", + "Break time! Not like the world is ending right?", + "I just need you to tell me how to get to the %ytime machine%w.", + "Mweep", +}; + +std::map> trapMessageList = { + { TRAP_FREEZE, freezeTrapMessages }, + { TRAP_BLAST, blastTrapMessages }, + { TRAP_SHOCK, shockTrapMessages }, + { TRAP_JINX, jinxTrapMessages }, + { TRAP_WALLET, walletTrapMessages }, + { TRAP_ENEMY, enemyTrapMessages }, + { TRAP_TIME, timeTrapMessages }, +}; +// clang-format on + +std::string GetTrapMessage() { + RollTrapType(); + auto findIt = trapMessageList.find((TrapTypes)roll); + if (findIt == trapMessageList.end()) { + return defaultTrapMessages[rand() % defaultTrapMessages.size()]; + } else { + return findIt->second[rand() % findIt->second.size()]; + } +} + +void Rando::MiscBehavior::OfferTrapItem() { + if (!gPlayState) { + return; + } + + switch (roll) { + case TRAP_FREEZE: + GameInteractor::Instance->events.emplace_back( + GIEventTrap{ .action = []() { func_80833B18(gPlayState, GET_PLAYER(gPlayState), 3, 0, 0, 0, 0); } }); + break; + case TRAP_BLAST: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_BOM, GET_PLAYER(gPlayState)->actor.world.pos.x, + GET_PLAYER(gPlayState)->actor.world.pos.y, GET_PLAYER(gPlayState)->actor.world.pos.z, 1, 0, + 0, 0); + } }); + break; + case TRAP_SHOCK: + GameInteractor::Instance->events.emplace_back( + GIEventTrap{ .action = []() { func_80833B18(gPlayState, GET_PLAYER(gPlayState), 4, 0, 0, 0, 0); } }); + break; + case TRAP_JINX: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_PlaySfx(&GET_PLAYER(gPlayState)->actor, NA_SE_EN_BUBLE_BITE); + gSaveContext.jinxTimer = 1200; + } }); + break; + case TRAP_WALLET: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + int16_t currentRupees = gSaveContext.save.saveInfo.playerData.rupees; + if (currentRupees != 0) { + Vec3f positional = GET_PLAYER(gPlayState)->actor.world.pos; + positional.y = GET_PLAYER(gPlayState)->actor.world.pos.y + 100.0f; + Item00Type rupee = ITEM00_RUPEE_GREEN; + int16_t spawnedRupees = 0; + int16_t remainingRupees = currentRupees; + for (int i = spawnedRupees; spawnedRupees < remainingRupees;) { + if (currentRupees >= 20) { + rupee = ITEM00_RUPEE_RED; + spawnedRupees += 20; + Rupees_ChangeBy(-20); + currentRupees -= 20; + } else if (currentRupees >= 5) { + rupee = ITEM00_RUPEE_BLUE; + spawnedRupees += 5; + Rupees_ChangeBy(-5); + currentRupees -= 5; + } else if (currentRupees >= 1) { + rupee = ITEM00_RUPEE_GREEN; + spawnedRupees += 1; + Rupees_ChangeBy(-1); + currentRupees -= 1; + } + EnItem00* rupeeActor = (EnItem00*)Item_DropCollectible(gPlayState, &positional, rupee); + rupeeActor->actor.speed = Rand_CenteredFloat(5.0f); + rupeeActor->unk152 = 600; // Extending Time before Despawning + } + } + } }); + break; + case TRAP_ENEMY: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_RR, GET_PLAYER(gPlayState)->actor.world.pos.x, + GET_PLAYER(gPlayState)->actor.world.pos.y, GET_PLAYER(gPlayState)->actor.world.pos.z, 0, 0, + 0, 1); + } }); + break; + case TRAP_TIME: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + u16 previous_time = gSaveContext.save.time; + u16 new_time = gSaveContext.save.time + timeSkipInterval; + if (previous_time < MORNING_TIME && new_time >= MORNING_TIME) { + // Handles case where Night -> Day + if (gSaveContext.save.day != 3) { + gSaveContext.save.day++; + gSaveContext.save.eventDayCount++; + UpdateGameTime(new_time); + Interface_NewDay(gPlayState, CURRENT_DAY); + // Load environment values for new day + Environment_NewDay(&gPlayState->envCtx); + // Clear weather from day 2 + gWeatherMode = WEATHER_MODE_CLEAR; + gPlayState->envCtx.lightningState = LIGHTNING_OFF; + } else { + // Handles Moonfall case, prevents skipping past it by setting time right before Moonfall. + UpdateGameTime(MORNING_TIME - (timeSkipInterval / 10)); + } + } else { + // Every other case + UpdateGameTime(new_time); + } + TransitionFade_SetColor(&gPlayState->unk_18E48, 0x000000); + R_TRANS_FADE_FLASH_ALPHA_STEP = -1; + Player_PlaySfx(GET_PLAYER(gPlayState), NA_SE_SY_TRANSFORM_MASK_FLASH); + + // Handle kickouts, if needed + EnTimeTag* enTimeTag = (EnTimeTag*)Actor_FindNearby(gPlayState, &GET_PLAYER(gPlayState)->actor, + ACTOR_EN_TIME_TAG, ACTORCAT_ITEMACTION, 99999.9f); + if (enTimeTag != nullptr) { + TimeTagType timeTagType = (TimeTagType)TIMETAG_GET_TYPE(&enTimeTag->actor); + if (timeTagType == TIMETAG_KICKOUT_DOOR || timeTagType >= TIMETAG_KICKOUT_FINAL_HOURS) { + s16 kickoutHour = TIMETAG_KICKOUT_HOUR(&enTimeTag->actor); + s16 kickoutMinute = TIMETAG_KICKOUT_MINUTE(&enTimeTag->actor); + s32 kickoutTime = CLOCK_TIME(kickoutHour, kickoutMinute); + kickoutTime = ZERO_DAY_START(kickoutTime); + previous_time = ZERO_DAY_START(previous_time); + new_time = ZERO_DAY_START(new_time); + // If we were here before the kickout time, and now it's after, then get out of my house + if (previous_time <= kickoutTime && new_time >= kickoutTime) { + // Unless this is the Stock Pot Inn, and the room key is obtained + if (!(gPlayState->sceneId == SCENE_YADOYA && + Flags_GetRandoInf(RANDO_INF_OBTAINED_ROOM_KEY))) { + // This comes from EnTimeTag_KickOut_WaitForTime + Player_SetCsActionWithHaltedActors(gPlayState, &enTimeTag->actor, PLAYER_CSACTION_WAIT); + Message_StartTextbox(gPlayState, 0x1883 + TIMETAG_KICKOUT_GET_TEXT(&enTimeTag->actor), + NULL); + enTimeTag->actionFunc = EnTimeTag_KickOut_Transition; + } + } + } + } + } }); + break; + default: + break; + } +} + +void Rando::ActorBehavior::InitTrapsBehavior() { + // Selectively disable object dependency for actors spawned by traps + COND_VB_SHOULD(VB_ENABLE_OBJECT_DEPENDENCY, IS_RANDO && RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRAPS], { + ObjectId objectId = (ObjectId)va_arg(args, int); + if (objectId == OBJECT_RR) { // Like-Like + *should = false; + } + }); +} \ No newline at end of file diff --git a/mm/2s2h/Rando/MiscBehavior/Traps.h b/mm/2s2h/Rando/MiscBehavior/Traps.h new file mode 100644 index 000000000..a842c2462 --- /dev/null +++ b/mm/2s2h/Rando/MiscBehavior/Traps.h @@ -0,0 +1,10 @@ +#ifndef RANDO_TRAP_H +#define RANDO_TRAP_H + +#include "Rando/Rando.h" +typedef enum { TRAP_FREEZE, TRAP_BLAST, TRAP_SHOCK, TRAP_JINX, TRAP_WALLET, TRAP_ENEMY, TRAP_TIME, TRAP_MAX } TrapTypes; + +extern int RollTrapType(); +extern std::string GetTrapMessage(); + +#endif \ No newline at end of file diff --git a/mm/2s2h/Rando/StaticData/Items.cpp b/mm/2s2h/Rando/StaticData/Items.cpp index 2173d519c..0b6644884 100644 --- a/mm/2s2h/Rando/StaticData/Items.cpp +++ b/mm/2s2h/Rando/StaticData/Items.cpp @@ -187,6 +187,7 @@ std::map 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_TRAP, "a", "Knockoff Item", RITYPE_LESSER, ITEM_NONE, GI_NONE, GID_NONE), 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), diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index b7d40747f..16410bc14 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -23,6 +23,7 @@ std::map Options = { RO(RO_HINTS_OATH_TO_ORDER, RO_GENERIC_OFF), RO(RO_HINTS_PURCHASEABLE, RO_GENERIC_OFF), RO(RO_HINTS_SPIDER_HOUSES, RO_GENERIC_OFF), + RO(RO_TRAP_AMOUNT, 5), RO(RO_LOGIC, RO_LOGIC_GLITCHLESS), RO(RO_MINIMUM_STRAY_FAIRIES, STRAY_FAIRY_SCATTERED_TOTAL), RO(RO_PLENTIFUL_ITEMS, RO_GENERIC_OFF), @@ -36,6 +37,7 @@ std::map Options = { RO(RO_SHUFFLE_FROGS, RO_GENERIC_OFF), RO(RO_SHUFFLE_GOLD_SKULLTULAS, RO_GENERIC_OFF), RO(RO_SHUFFLE_GRASS_DROPS, RO_GENERIC_OFF), + RO(RO_SHUFFLE_TRAPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_OWL_STATUES, RO_GENERIC_OFF), RO(RO_SHUFFLE_POT_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_SHOPS, RO_GENERIC_OFF), diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index b072f31f3..858c43716 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -53,6 +53,7 @@ u8 GetIconForZMessage(RandoItemId itemId); const char* GetIconTexturePath(RandoItemId itemId); bool ShouldShowGetItemCutscene(RandoItemId itemId); std::string GetItemName(RandoItemId randoItemId, bool includeArticle = true); +std::string GetTrapMessage(); struct RandoStaticOption { RandoOptionId randoOptionId; diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index 55f72831d..297749727 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -2455,6 +2455,7 @@ typedef enum { RI_TINGLE_MAP_SNOWHEAD, RI_TINGLE_MAP_STONE_TOWER, RI_TINGLE_MAP_WOODFALL, + RI_TRAP, RI_TRIFORCE_PIECE, RI_TRIFORCE_PIECE_PREVIOUS, RI_WALLET_ADULT, @@ -2464,6 +2465,7 @@ typedef enum { RI_WOODFALL_MAP, RI_WOODFALL_SMALL_KEY, RI_WOODFALL_STRAY_FAIRY, + RI_MAX_TRAP, RI_MAX, } RandoItemId; @@ -2785,6 +2787,7 @@ typedef enum { RO_HINTS_OATH_TO_ORDER, RO_HINTS_PURCHASEABLE, RO_HINTS_SPIDER_HOUSES, + RO_TRAP_AMOUNT, RO_LOGIC, RO_MINIMUM_STRAY_FAIRIES, RO_PLENTIFUL_ITEMS, @@ -2798,6 +2801,7 @@ typedef enum { RO_SHUFFLE_FROGS, RO_SHUFFLE_GRASS_DROPS, RO_SHUFFLE_GOLD_SKULLTULAS, + RO_SHUFFLE_TRAPS, RO_SHUFFLE_OWL_STATUES, RO_SHUFFLE_POT_DROPS, RO_SHUFFLE_SHOPS, diff --git a/mm/assets/2s2h_assets.h b/mm/assets/2s2h_assets.h index 0ddb4497e..f19690152 100644 --- a/mm/assets/2s2h_assets.h +++ b/mm/assets/2s2h_assets.h @@ -385,3 +385,6 @@ static const ALIGN_ASSET(2) char gTriforcePiece2DL[] = dgTriforcePiece2DL; #define dgTriforcePieceCompletedDL "__OTR__objects/object_triforce_completed/gTriforcePieceCompletedDL" static const ALIGN_ASSET(2) char gTriforcePieceCompletedDL[] = dgTriforcePieceCompletedDL; + +#define dgTrapDL "__OTR__objects/object_trap/gTrapDL" +static const ALIGN_ASSET(2) char gTrapDL[] = dgTrapDL; diff --git a/mm/assets/custom/objects/object_trap/gTrapDL b/mm/assets/custom/objects/object_trap/gTrapDL new file mode 100644 index 000000000..5ba726a43 --- /dev/null +++ b/mm/assets/custom/objects/object_trap/gTrapDL @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/mm/assets/custom/objects/object_trap/ice_2 b/mm/assets/custom/objects/object_trap/ice_2 new file mode 100644 index 000000000..671d0a95f Binary files /dev/null and b/mm/assets/custom/objects/object_trap/ice_2 differ diff --git a/mm/assets/custom/objects/object_trap/mat_object_trap_model_ice b/mm/assets/custom/objects/object_trap/mat_object_trap_model_ice new file mode 100644 index 000000000..26fb276cc --- /dev/null +++ b/mm/assets/custom/objects/object_trap/mat_object_trap_model_ice @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/mm/assets/custom/objects/object_trap/object_trap_model_tri_0 b/mm/assets/custom/objects/object_trap/object_trap_model_tri_0 new file mode 100644 index 000000000..fb46fa598 --- /dev/null +++ b/mm/assets/custom/objects/object_trap/object_trap_model_tri_0 @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mm/assets/custom/objects/object_trap/object_trap_model_vtx_0 b/mm/assets/custom/objects/object_trap/object_trap_model_vtx_0 new file mode 100644 index 000000000..6ffc4dfe3 --- /dev/null +++ b/mm/assets/custom/objects/object_trap/object_trap_model_vtx_0 @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mm/src/code/z_scene.c b/mm/src/code/z_scene.c index e76d74ed7..52def8ff0 100644 --- a/mm/src/code/z_scene.c +++ b/mm/src/code/z_scene.c @@ -1,5 +1,6 @@ #include "global.h" #include +#include "GameInteractor/GameInteractor.h" /** * Spawn an object file of a specified ID that will persist through room changes. @@ -112,7 +113,7 @@ s32 Object_GetSlot(ObjectContext* objectCtx, s16 objectId) { } } - return CVarGetInteger("gDeveloperTools.DisableObjectDependency", 0) ? 0 : OBJECT_SLOT_NONE; + return GameInteractor_Should(VB_ENABLE_OBJECT_DEPENDENCY, true, objectId) ? OBJECT_SLOT_NONE : 0; } s32 Object_IsLoaded(ObjectContext* objectCtx, s32 slot) { -- cgit v1.2.3