#include "hooks.hpp" #include "session.hpp" #include "randomizer_context.hpp" #include "ui/rando_config.hpp" #include "flags.h" #include "stages.h" #include "tools.h" #include "item.hpp" #include "item_ids.h" #include #include #include "c/c_damagereaction.h" #include "d/actor/d_a_alink.h" #include "d/actor/d_a_b_bq.h" #include "d/actor/d_a_demo_item.h" #include "d/actor/d_a_door_shutter.h" #include "d/actor/d_a_e_md.h" #include "d/actor/d_a_e_mk.h" #include "d/actor/d_a_kytag08.h" #include "d/actor/d_a_mg_rod.h" #include "d/actor/d_a_npc4.h" #include "d/actor/d_a_npc_bans.h" #include "d/actor/d_a_npc_fairy.h" #include "d/actor/d_a_npc_shad.h" #include "d/actor/d_a_npc_yelia.h" #include "d/actor/d_a_npc_ykm.h" #include "d/actor/d_a_npc_ykw.h" #include "d/actor/d_a_npc_zrc.h" #include "d/actor/d_a_npc_zrz.h" #include "d/actor/d_a_obj_bosswarp.h" #include "d/actor/d_a_obj_master_sword.h" #include "d/actor/d_a_obj_swBallC.h" #include "d/actor/d_a_obj_zra_rock.h" #include "d/actor/d_a_tag_kmsg.h" #include "d/actor/d_a_tbox2.h" #include "d/d_door_param2.h" #include "d/d_file_sel_info.h" #include "d/d_file_select.h" #include "d/d_gameover.h" #include "d/d_meter2_info.h" #include "d/d_msg_object.h" #include "d/d_s_name.h" #include "d/d_s_play.h" #include "d/d_save.h" #include "d/d_shop_system.h" #include "f_op/f_op_overlap_mng.h" #include "m_Do/m_Do_Reset.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect); DEFINE_HOOK(&dFile_info_c::setSaveData, dFile_info_c__setSaveData); DEFINE_HOOK(&dSv_event_c::isEventBit, dSv_event_c__isEventBit); DEFINE_HOOK(&dSv_event_c::onEventBit, dSv_event_c__onEventBit); DEFINE_HOOK(&dSv_memBit_c::isSwitch, dSv_memBit_c__isSwitch); DEFINE_HOOK(&dSv_memBit_c::onSwitch, dSv_memBit_c__onSwitch); DEFINE_HOOK(&dSv_memBit_c::onDungeonItem, dSv_memBit_c__onDungeonItem); DEFINE_HOOK(&dSv_memBit_c::offDungeonItem, dSv_memBit_c__offDungeonItem); DEFINE_HOOK(&dSv_memBit_c::isDungeonItem, dSv_memBit_c__isDungeonItem); DEFINE_HOOK(&dSv_player_status_b_c::isDarkClearLV, dSv_player_status_b_c__isDarkClearLV); DEFINE_HOOK(&dSv_player_item_c::checkEmptyBottle, dSv_player_item_c__checkEmptyBottle); DEFINE_HOOK(&dSv_player_item_c::setLineUpItem, dSv_player_item_c__setLineUpItem); DEFINE_HOOK(&dSv_info_c::onSwitch, dSv_info_c__onSwitch); #ifdef _MSVC_LANG #define setNextStage_sig "?dComIfGp_setNextStage@@YAXPEBDFCCMIHCFHH@Z" #else #define setNextStage_sig "_Z21dComIfGp_setNextStagePKcsaafjiasii" #endif DEFINE_HOOK_SYMBOL(setNextStage_sig, void(char const *, s16, s8, s8, f32, u32, int, s8, s16, int, int), setNextStage); DEFINE_HOOK_SYMBOL("daObj_Gb_Create", int(fopAc_ac_c*), ObjGb_Create); DEFINE_HOOK(&dMeter2Info_readItemTexture, readItemTexture); DEFINE_HOOK(&dShopSystem_c::seq_decide_yes, dShopSystem_c__seq_decide_yes); DEFINE_HOOK(&CheckFieldItemCreateHeap, dItemData_CheckFieldItemCreateHeap); DEFINE_HOOK(&dEvt_control_c::talkEnd, dEvt_control_c__talkEnd); DEFINE_HOOK(&dComIfG_play_c::getLayerNo_common_common, dComIfG_play_c__getLayerNo_common_common); DEFINE_HOOK(&dComIfGs_onStageSwitch, onStageSwitch); extern void getItemFunc(u8); DEFINE_HOOK(&getItemFunc, dItem_getItemFunc); extern int checkItemGet(u8 i_itemNo, int i_default); DEFINE_HOOK(&checkItemGet, dItem_checkItemGet); DEFINE_HOOK(&daAlink_c::decideDoStatus, daAlink_c__decideDoStatus); DEFINE_HOOK_SYMBOL("daAlink_searchBouDoor", void*(fopAc_ac_c*, void*), searchBouDoor); DEFINE_HOOK(&daAlink_c::checkGroundSpecialMode, daAlink_c__checkGroundSpecialMode); DEFINE_HOOK(&daAlink_c::setGetItemFace, daAlink_c__setGetItemFace); DEFINE_HOOK(&daAlink_c::setGetSubBgm, daAlink_c__setGetSubBgm); DEFINE_HOOK(&daAlink_c::procCoGetItem, daAlink_c__procCoGetItem); DEFINE_HOOK(&daAlink_c::procCoWarpInit, daAlink_c__procCoWarpInit); DEFINE_HOOK_SYMBOL("b_bq_end", void(b_bq_class*), bq_end); DEFINE_HOOK(&daDoor20_c::checkOpenMsgDoor, daDoor20_c__checkOpenMsgDoor); DEFINE_HOOK_SYMBOL("demo_camera_end", void(e_mk_class*), e_mk_demo_camera_end); DEFINE_HOOK(&dStage_changeScene4Event, changeScene4Event); DEFINE_HOOK_SYMBOL("dStage_playerInit", int(dStage_dt_c*, void*, int, void*), stage_playerInit); DEFINE_HOOK_SYMBOL("daKytag08_Execute", int(kytag08_class*), Kytag08_Execute); DEFINE_HOOK(&daNpcT_chkEvtBit, NpcT_chkEvtBit); DEFINE_HOOK(&daNpcF_chkEvtBit, NpcF_chkEvtBit); DEFINE_HOOK(&daNpcF_c::orderEvent, daNpcF_c__orderEvent); DEFINE_HOOK(&daNpc_Bans_c::isDelete, daNpc_Bans_c__isDelete); DEFINE_HOOK(&daNpc_Fairy_c::AppearDemoCall, daNpc_Fairy_c__AppearDemoCall); DEFINE_HOOK(&daNpcShad_c::Create, daNpcShad_c__Create); DEFINE_HOOK(&daNpcShad_c::wait_type1, daNpcShad_c__wait_type1); DEFINE_HOOK(&daNpc_Yelia_c::cutTakeWoodStatue, daNpc_Yelia_c__cutTakeWoodStatue); DEFINE_HOOK(&dSv_player_item_c::setWarashibeItem, dSv_player_item_c__setWarashibeItem); DEFINE_HOOK(&daNpc_ykM_c::isDelete, daNpc_ykM_c__isDelete); DEFINE_HOOK(&daNpc_ykW_c::isDelete, daNpc_ykW_c__isDelete); DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create); DEFINE_HOOK(&fopAcM_orderMapToolEvent, orderMapToolEvent); DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute); DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene); DEFINE_HOOK(&daNpc_zrZ_c::isDelete, daNpc_zrZ_c__isDelete); DEFINE_HOOK(&daTbox2_c::Create, daTbox2_c__Create); DEFINE_HOOK(&daTbox2_c::setGetDemoItem, daTbox2_c__setGetDemoItem); DEFINE_HOOK(&dGameover_c::_create, dGameover_c___create); DEFINE_HOOK(&daObjSwBallC_c::Create, daObjSwBallC_c__Create); DEFINE_HOOK(&daObjSwBallC_c::actionWait, daObjSwBallC_c__actionWait); DEFINE_HOOK_SYMBOL("daDitem_Execute", int(daDitem_c*), daDitem_c__execute); DEFINE_HOOK(&daObjBossWarp_c::demoProc, daObjBossWarp_c__demoProc); DEFINE_HOOK_SYMBOL("lure_heart", void(dmg_rod_class*), mgRod_lure_heart); DEFINE_HOOK_SYMBOL("uki_catch", void(dmg_rod_class*), mgRod_uki_catch); #ifdef _MSVC_LANG #define setEmptyBottle_noarg_sig "?setEmptyBottle@dSv_player_item_c@@QEAAXXZ" #else #define setEmptyBottle_noarg_sig "_ZN17dSv_player_item_c14setEmptyBottleEv" #endif DEFINE_HOOK_SYMBOL(setEmptyBottle_noarg_sig, void(dSv_player_item_c*), dSv_player_item_c__setEmptyBottle); DEFINE_HOOK(&daNpc_zrC_c::isDelete, daNpc_zrC_c__isDelete); DEFINE_HOOK(&daObjZraRock_c::create, daObjZraRock_c__create); #ifdef _MSVC_LANG #define phase_1_dScnPly_sig "?phase_1@@YAHPAUdScnPly_c@@@Z" // sig is wrong? #else #define phase_1_dScnPly_sig "_Z7phase_1P9dScnPly_c" #endif // DEFINE_HOOK_SYMBOL(phase_1_dScnPly_sig, int(dScnPly_c*), phase_1__dScnPly_c); namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } namespace randomizer::hooks { namespace { UiDialogHandle playModeDialog{0}; HookAction hookPreDataSelect(ModContext*, void* args, void* retval, void* userdata) { ui::g_dialogSelectModeState = ui::SelectReady; ui::g_file_select_window_ctx.is_proceed = false; return HOOK_CONTINUE; } HookAction hookPreSelectDataNameMove(ModContext*, void* args, void* retval, void* userdata) { dFile_select_c* i_this = mods::arg(args, 0); // if coming from "start randomizer" button, let transition occur as normal if (ui::g_file_select_window_ctx.is_proceed) { return HOOK_CONTINUE; } bool isHeaderTxtChange = i_this->headerTxtChangeAnm(); bool isFileRecScale = i_this->fileRecScaleAnm2(); bool isModoruTxtDisp = i_this->modoruTxtDispAnm(); // if selected vanilla mode, let transition occur as normal if (ui::g_dialogSelectModeState == ui::SelectVanilla) { return HOOK_CONTINUE; } if (ui::g_dialogSelectModeState == ui::SelectReady && isHeaderTxtChange == true && isFileRecScale == true && isModoruTxtDisp == true) { ui::g_dialogSelectModeState = ui::SelectWait; auto buildDialog = [i_this]() { UiDialogDesc desc = UI_DIALOG_DESC_INIT; desc.title = "Play Type"; desc.body_rml = "What mode would you like to play?"; desc.icon = "question-mark"; desc.variant = UI_DIALOG_NORMAL; UiDialogAction actions[2]; actions[0] = { .label = "Vanilla", .on_pressed = [](ModContext* ctx, UiDialogHandle dialogHandle, void*) { mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK); randomizer_GetContext() = RandomizerContext(); ui::g_dialogSelectModeState = ui::SelectVanilla; session::svc_mng.ui->dialog_close(ctx, dialogHandle); }, .user_data = nullptr, .keep_open = false, }; actions[1] = { .label = "Randomizer", .on_pressed = [](ModContext* ctx, UiDialogHandle dialogHandle, void* userdata) { mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK); ui::g_dialogSelectModeState = ui::SelectRandomizer; ui::buildFileSelectGateMenu(static_cast(userdata)); session::svc_mng.ui->dialog_close(ctx, dialogHandle); }, .user_data = i_this, .keep_open = false, }; desc.actions = actions; desc.action_count = 2; if (session::svc_mng.ui->dialog_push(session::svc_mng.mod_ctx, &desc, &playModeDialog) != MOD_OK) { mods::log::error("Failed to push dialog"); return MOD_ERROR; } return MOD_OK; }; if (buildDialog() != MOD_OK) { mods::log::error("Failed to build dialog"); return HOOK_CONTINUE; } } return HOOK_SKIP_ORIGINAL; } void hookPostSetSaveData(ModContext* ctx, void* args, void* retval, void* userdata) { dFile_info_c* i_this = mods::arg(args, 0); u8 i_dataNo = mods::arg(args, 3); if (*static_cast(retval) == 0) { char hash[64]; size_t size = sizeof(hash) - 1; ModResult rt = session::svc_mng.save->peek_blob(ctx, i_dataNo, "seed_hash", hash, &size); if (rt != MOD_OK || size == 0) { // leave file text vanilla if seed hash isn't found mods::log::debug("no seed_hash found for file {}", i_dataNo); return; } hash[size] = 0; const std::string curFileSeedHash = hash; if (!curFileSeedHash.empty()) { const auto setHBinding = [](J2DTextBox* tbox, J2DTextBoxHBinding bind) { tbox->mFlags &= 0b0011; tbox->mFlags |= ((bind & 3) << 2); }; // Overwrite "Save time" text with "Randomizer" auto saveTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_s_t_02')); SafeStringCopy(saveTimeText->getStringPtr(), "Randomizer"); setHBinding(saveTimeText, J2DTextBoxHBinding::HBIND_LEFT); // Overwrite the "Total play time" text with the seed hash auto playTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_p_t_02')); SafeStringCopy(playTimeText->getStringPtr(), curFileSeedHash.c_str()); // Give the text double the space on the menu incase the seed hash is long setHBinding(playTimeText, J2DTextBoxHBinding::HBIND_LEFT); playTimeText->resize(playTimeText->getWidth() * 2, playTimeText->getHeight()); } } } HookAction hookPreIsEventBit(ModContext*, void* args, void* retval, void*) { const u16 i_no = mods::arg(args, 1); auto& out = *static_cast(retval); switch (i_no) { case BO_TALKED_TO_YOU_AFTER_OPENING_IRON_BOOTS_CHEST: { if (daAlink_c::checkStageName(allStages[Ordon_Village_Interiors])) { out = dComIfGs_isEventBit(HEARD_BO_TEXT_AFTER_SUMO_FIGHT) ? TRUE : FALSE; return HOOK_SKIP_ORIGINAL; } break; } case GAVE_ILIA_HER_CHARM: // Gave Ilia the charm case CITY_OOCCOO_CS_WATCHED: // CiTS Intro CS watched { if (daAlink_c::checkStageName(allStages[Hidden_Village])) { if (!dComIfGs_isEventBit(GOT_ILIAS_CHARM)) { // If we haven't gotten the item from Impaz then we need to return false or it // will break her dialogue. out = FALSE; return HOOK_SKIP_ORIGINAL; } } break; } case GORON_MINES_CLEARED: { if (daAlink_c::checkStageName(allStages[Goron_Mines]) || daAlink_c::checkStageName(allStages[Death_Mountain_Interiors])) { out = FALSE; // The gorons will not act properly if the flag is set. return HOOK_SKIP_ORIGINAL; } break; } case ZORA_ESCORT_CLEARED: { if (daAlink_c::checkStageName(allStages[Castle_Town])) { // If the flag isn't set the player will be thrown into escort when they open the door out = TRUE; return HOOK_SKIP_ORIGINAL; } if (playerIsInRoomStage(0, allStages[Kakariko_Village_Interiors])) { out = TRUE; // Return true to prevent Renado/Ilia crash after ToT return HOOK_SKIP_ORIGINAL; } break; } case CITY_IN_THE_SKY_CLEARED: // Would like to find where this is checked and patch it there. { if (!dComIfGs_isEventBit(FIXED_THE_MIRROR_OF_TWILIGHT)) { if (randomizer_GetContext().mSettings[RandomizerContext::PALACE_OF_TWILIGHT_REQUIREMENTS] != RandomizerContext::VANILLA) { out = FALSE; return HOOK_SKIP_ORIGINAL; } } break; } case HOWLED_AT_SNOWPEAK_STONE: { if (daAlink_c::checkStageName(allStages[Snowpeak])) { // return false so the player can howl at the stone multiple times to remove map glitch out = FALSE; return HOOK_SKIP_ORIGINAL; } break; } case WATCHED_CUTSCENE_AFTER_GOATS_2: { if (playerIsInRoomStage(1, allStages[Ordon_Village_Interiors])) { // false -> Sera gives the milk item once they help the cat; // true -> the shop is always usable even if the cat is not returned. out = dComIfGs_isEventBit(SERAS_CAT_RETURNED_TO_SHOP) ? FALSE : TRUE; return HOOK_SKIP_ORIGINAL; } break; } case FIXED_THE_MIRROR_OF_TWILIGHT: { if (daAlink_c::checkStageName(allStages[Palace_of_Twilight])) { out = TRUE; // If the flag is not set, the player cannot leave PoT from the inside. return HOOK_SKIP_ORIGINAL; } break; } default: break; } return HOOK_CONTINUE; } HookAction hookPreOnEventBit(ModContext*, void* args, void*, void*) { const u16 i_no = mods:: arg(args, 1); switch (i_no) { // Wolf <-> Human crash patches/bug fixes: some cutscenes/events either crash or act // weird if Link is in the wrong form and the game no longer auto-transforms once the // Shadow Crystal has been obtained. case ENTERED_ORDON_SPRING_DAY_3: if (dComIfGs_isEventBit(TRANSFORMING_UNLOCKED)) { dComIfGs_setTransformStatus(0); } break; case WATCHED_CUTSCENE_AFTER_BEING_CAPTURED_IN_FARON_TWILIGHT: if (dComIfGs_isEventBit(TRANSFORMING_UNLOCKED)) { dComIfGs_setTransformStatus(1); } break; case MIDNAS_DESPERATE_HOUR_COMPLETED: dComIfGs_onDarkClearLV(3); break; case CLEARED_FARON_TWILIGHT: // If we've already cleared Eldin Twilight, Lanayru Twilight, and MDH if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) { if (dComIfGs_isDarkClearLV(2) && dComIfGs_isDarkClearLV(3)) { // Set the flag for the last transformed twilight; also puts Midna on the // player's back dComIfGs_onTransformLV(3); dComIfGs_onDarkClearLV(3); } } break; case CLEARED_ELDIN_TWILIGHT: dComIfGs_onEventBit(MAP_WARPING_UNLOCKED); // in glitched logic, you can skip the gorge bridge if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) { if (dComIfGs_isDarkClearLV(1) && dComIfGs_isDarkClearLV(3)) { dComIfGs_onTransformLV(3); dComIfGs_onDarkClearLV(3); } } // Set flag for the bridge between Castle Town and Eldin field if skip bridge // donation is on and both Eldin and Lanayru twilight are cleared if (dComIfGs_isEventBit(CLEARED_LANAYRU_TWILIGHT) && randomizer_GetContext().mSettings[RandomizerContext::SKIP_BRIDGE_DONATION] == RandomizerContext::ON) { dComIfGs_onEventBit(BRIDGE_REPAIR_FUNDRAISING_COMPLETED); dComIfGs_onStageSwitch(6, 0x1B); // Bridge exists } break; case CLEARED_LANAYRU_TWILIGHT: if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) { if (dComIfGs_isDarkClearLV(1) && dComIfGs_isDarkClearLV(2)) { dComIfGs_onTransformLV(3); dComIfGs_onDarkClearLV(3); } } if (dComIfGs_isEventBit(CLEARED_ELDIN_TWILIGHT) && randomizer_GetContext().mSettings[RandomizerContext::SKIP_BRIDGE_DONATION] == RandomizerContext::ON) { dComIfGs_onEventBit(BRIDGE_REPAIR_FUNDRAISING_COMPLETED); dComIfGs_onStageSwitch(6, 0x1B); // Bridge exists } break; case REMOVE_SWORD_SHIELD_FROM_WOLF_BACK: if (!dComIfGs_isEventBit(CLEARED_FARON_TWILIGHT)) { dComIfGs_onTransformLV(0); // Set the last transformed twilight to include Faron } break; case GAVE_TELMA_RENADOS_LETTER: offWarashibeItem(dItemNo_Randomizer_LETTER_e); break; default: break; } return HOOK_CONTINUE; } HookAction hookPreMembitIsSwitch(ModContext*, void* args, void* retval, void*) { if (getStageID() == Hidden_Village_Interiors) { if (mods::arg(args, 1) == 0x61) { // Is Impaz in her house *static_cast(retval) = TRUE; return HOOK_SKIP_ORIGINAL; } } return HOOK_CONTINUE; } // kinda hacky check to see if this membit object is the temp memory area in save info inline bool isTempMemBit(dSv_memBit_c* i_this) { return i_this == &dComIfGs_getSaveInfo()->getMemory().getBit(); } HookAction hookPreMembitOnSwitch(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); const int i_no = mods::arg(args, 1); if (isTempMemBit(i_this)) { if (getStageID() == Arbiters_Grounds) { // Poe flame CS trigger if (i_no == 0x26) { i_this->offSwitch(0x45); // Open the Poe gate return HOOK_SKIP_ORIGINAL; } } else if (getStageID() == Lake_Hylia) { // Lanayru Twilight End CS trigger if (i_no == 0xD) { if (dComIfGs_isEventBit(TRANSFORMING_UNLOCKED)) { // Set player to Human as the game will not do so if Shadow Crystal has // been obtained. dComIfGs_setTransformStatus(0); } } } else if (getStageID() == Kakariko_Village) { // Hawkeye is for sale if (i_no == 0x3E) { i_this->offSwitch(0xB); // Remove the coming soon sign so the hawkeye can be bought } } else if (getStageID() == Hyrule_Field) { // Destroyed North Eldin rocks barrier if (i_no == 0x11) { // Unlock Eldin Province on the map. Done manually rather than via // `onRegionBit`, which would see the rocks unbroken and skip the region. dComIfGs_getSaveData()->getPlayer().getPlayerFieldLastStayInfo().mRegion |= 0x08; } } } return HOOK_CONTINUE; } HookAction hookPreOnDungeonItem(ModContext*, void* args, void*, void*) { int i_no = mods::arg(args, 1); // Don't use the stage life collection flag for rando if (i_no == dSv_memBit_c::STAGE_LIFE) { return HOOK_SKIP_ORIGINAL; } // Don't turn Ooccoo into the note when defeating a boss else if (dComIfGs_isStageBossEnemy() && i_no == dSv_memBit_c::OOCCOO_NOTE) { return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreOffDungeonItem(ModContext*, void* args, void*, void*) { if (mods::arg(args, 1) == dSv_memBit_c::STAGE_LIFE) { return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreIsDungeonItem(ModContext*, void* args, void* retval, void*) { const int i_no = mods::arg(args, 1); auto& out = *static_cast(retval); switch (i_no) { case dSv_memBit_c::STAGE_LIFE: out = FALSE; return HOOK_SKIP_ORIGINAL; case dSv_memBit_c::STAGE_BOSS_ENEMY: { // If we are in a dungeon or fighting a midboss, we don't want the boss being // defeated to affect the gameplay. std::string stageName = dComIfGp_getStartStageName(); if (stageName.starts_with("D_MN")) { out = FALSE; return HOOK_SKIP_ORIGINAL; } break; } case dSv_memBit_c::STAGE_BOSS_ENEMY_2: { // If we are in the early rooms of FT, we don't want Ook being defeated to affect // gameplay if (daAlink_c::checkStageName("D_MN05") && dComIfGp_roomControl_getStayNo() < 4) { out = FALSE; return HOOK_SKIP_ORIGINAL; } break; } default: break; } return HOOK_CONTINUE; } HookAction hookPreIsDarkClearLV(ModContext*, void* args, void* retval, void*) { if (mods::arg(args, 1) == 0 && playerIsInRoomStage(1, allStages[Ordon_Village_Interiors])) { // Return false so Sera will give us the bottle if we have rescued the cat. *static_cast(retval) = FALSE; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreCheckEmptyBottle(ModContext*, void*, void* retval, void*) { if (getStageID() == Cave_of_Ordeals) { // Return 1 to allow the player to collect the floor 50 reward, as this makes the // game think the player has an empty bottle. *static_cast(retval) = 1; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } void hookPostSetLineUpItem(ModContext*, void* args, void*, void*) { // Allow rando to use all item slots. Checks the loaded hash rather than // randomizer_IsActive() because this runs on file select. if (randomizer_GetContext().mHash.empty()) { return; } auto* i_this = mods::arg(args, 0); if (i_this->mItems[7] == dItemNo_NONE_e) { return; } // append slot 7 after the vanilla lineup, unless already present int slot_idx = 0; for (; slot_idx < 24; slot_idx++) { const u8 lineup = i_this->mItemSlots[slot_idx]; if (lineup == 7) { return; } if (lineup == 0xFF) { break; } } if (slot_idx < 24) { i_this->mItemSlots[slot_idx] = 7; } } HookAction hookPreSaveInfoOnSwitch(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); const int i_no = mods::arg(args, 1); const int room_no = mods::arg(args, 2); // Set custom flag for the Temple of Time pedestal strike if (getStageID() == Sacred_Grove && i_no == 0xEE) { i_this->onSwitch(0x63, room_no); } // We check to see if the flag being set is for the UZR portal as a safety precaution. if (daAlink_c::checkStageName("F_SP126") && i_no == 0x15 && dComIfGs_getTransformStatus() == TF_STATUS_WOLF) { // Set the flag to make Iza 1 available and set the memory bit for having talked to her // after opening the portal as human. dComIfGs_onEventBit(0xB02); i_this->onSwitch(0x37, room_no); // Note for the above stuff. This works for now. Eventually would like to adjust this to // a FLW patch since I think we could accomplish similar results by having the conversation // continue as normal regardless of form, but I haven't looked into it that much. } return HOOK_CONTINUE; } bool hookLureHeart_isSkipGetItem = false; HookAction hookPreLureHeart(ModContext*, void* args, void*, void*) { hookLureHeart_isSkipGetItem = true; return HOOK_CONTINUE; } void hookPostLureHeart(ModContext*, void*, void*, void*) { hookLureHeart_isSkipGetItem = false; } HookAction hookPreGetItemFunc(ModContext*, void* args, void*, void*) { const u8 item = mods::arg(args, 0); // coming from lure_heart, don't give item here. let FLW message handle it if (hookLureHeart_isSkipGetItem && item == dItemNo_KAKERA_HEART_e) { return HOOK_SKIP_ORIGINAL; } item::exec_item_get(item); return HOOK_SKIP_ORIGINAL; } HookAction hookPreCheckItemGet(ModContext*, void* args, void* retval, void*) { *static_cast(retval) = item::check_item_get(mods::arg(args, 0), mods::arg(args, 1)); return HOOK_SKIP_ORIGINAL; } HookAction hookPreSetNextStage(ModContext*, void* args, void*, void*) { randomizer_checkAndOverrideEntranceData( mods::arg_ref(args, 0), mods::arg_ref(args, 2), mods::arg_ref(args, 1), mods::arg_ref(args, 3) ); return HOOK_CONTINUE; } HookAction hookPreObjGbCreate(ModContext*, void* args, void* retval, void*) { if (getStageID() == StageIDs::Mirror_Chamber && !randomizer_mirrorChamberWallShouldExist()) { *static_cast(retval) = cPhs_ERROR_e; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } void hookPostReadItemTexture(ModContext*, void* args, void*, void*) { const u8 item_no = mods::arg(args, 1); void* tex_buf1 = mods::arg(args, 2); if (tex_buf1 == nullptr || item_no != dItemNo_Randomizer_MAGIC_LV1_e) { return; } ResourceBuffer bti = RESOURCE_BUFFER_INIT; if (session::svc_mng.resource->load(session::svc_mng.mod_ctx, "shadow_crystal.bti", &bti) == MOD_OK) { std::memcpy(tex_buf1, bti.data, bti.size < 0xC00 ? bti.size : 0xC00); session::svc_mng.resource->free(session::svc_mng.mod_ctx, &bti); } } HookAction hookPreShopSeqDecideYes(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); int item_no = 0; if (i_this->mFlow.getEventId(&item_no) == 1 && playerIsInRoomStage(3, "R_SP109")) { const u16 key = static_cast((getStageID() << 8) | (item_no & 0xFF)); if (randomizer_GetContext().mShopOverrides.contains(key)) { i_this->setSoldOutFlag(); } } return HOOK_CONTINUE; } HookAction hookPreCheckFieldItemCreateHeap(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); switch (static_cast(i_this)->getItemNo()) { case dItemNo_Randomizer_EMPTY_BOTTLE_e: case dItemNo_Randomizer_HALF_MILK_BOTTLE_e: case dItemNo_Randomizer_OIL_BOTTLE3_e: case dItemNo_Randomizer_DROP_BOTTLE_e: case dItemNo_Randomizer_LINKS_SAVINGS_e: case dItemNo_Randomizer_POU_SPIRIT_e: *static_cast(retval) = CheckItemCreateHeap(i_this); return HOOK_SKIP_ORIGINAL; default: return HOOK_CONTINUE; } } void hookPostTalkEnd(ModContext*, void*, void*, void*) { if (g_randomizerState.getHasPendingToDChange()) { g_randomizerState.setHasPendingToDChange(false); g_randomizerState.handleTimeOfDayChange(); } } HookAction hookPreGetLayerNo(ModContext*, void* args, void* retval, void*) { auto i_stageName = mods::arg(args, 0); auto i_roomNo = mods::arg(args, 1); auto& layer = mods::arg_ref(args, 2); if (strcmp(dComIfGp_getStartStageName(), "S_MV000") == 0 || (strcmp(dComIfGp_getStartStageName(), "F_SP102") == 0 && layer == 10)) { return HOOK_CONTINUE; } int stageID = getStageID(i_stageName); bool condition = false; bool darkIsClear = false; if (layer < 0) { layer = -1; // Stage is in a Twilight state if (dKy_darkworld_stage_check(i_stageName, i_roomNo) == TRUE) { layer = 14; } if (layer < 13) { switch(stageID) { case Snowpeak_Ruins: { if (dComIfGs_isEventBit(SNOWPEAK_RUINS_CLEARED)) { layer = 3; } break; } case Snowpeak: { if (dComIfGs_isEventBit(SNOWPEAK_RUINS_CLEARED) && (i_roomNo != 0)) { layer = 3; } break; } case Faron_Woods: case Faron_Woods_Interiors: { if ((i_roomNo == 5) || (i_roomNo == 6)) { // North Faron or Mist Area condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo Saved if (condition) { layer = 3; } else { layer = 1; } } else { condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo Saved if (condition) { condition = dComIfGs_isEventBit(FOREST_TEMPLE_CLEARED); // Forest Temple Completed if (condition) { layer = 5; } } else { layer = 1; } } break; } case Kakariko_Village: { condition = dComIfGs_isEventBit(WATCHED_CUTSCENE_AFTER_GORON_MINES); // Cutscene after GM Watched if (condition == false) { condition = dComIfGs_isEventBit(GORON_MINES_CLEARED); // Goron Mines Completed if (condition == false) { layer = 2; // If it is night, the layer is different. dComIfG_get_timelayer(&layer); } else { layer = 12; } } else { layer = 2; dComIfG_get_timelayer(&layer); } break; } case Kakariko_Graveyard: { condition = dComIfGs_isEventBit(GOT_ZORA_ARMOR_FROM_RUTELA); // Got Zora Armor from Rutela if (condition == false) { condition = dComIfGs_isEventBit(ZORA_ESCORT_CLEARED); // Zora Escort Cleared if (condition == false) { layer = 2; // If it is night, the layer is different. dComIfG_get_timelayer(&layer); } else { layer = 4; } } else { layer = 2; dComIfG_get_timelayer(&layer); } break; } case Kakariko_Graveyard_Interiors: { if (((i_roomNo == 1 && (condition = dComIfGs_isEventBit(LAKEBED_TEMPLE_CLEARED), condition != false)))) // Lakebed Completed { layer = 4; dComIfG_get_timelayer(&layer); } else { layer = 2; dComIfG_get_timelayer(&layer); } break; } case Kakariko_Village_Interiors: { if (i_roomNo == 1) { // Lakebed Completed layer = 4; dComIfG_get_timelayer(&layer); } else if (i_roomNo == 3) { layer = 2; } else { layer = 2; dComIfG_get_timelayer(&layer); } break; } case Death_Mountain: { condition = dComIfGs_isEventBit(GORON_MINES_CLEARED); // Goron Mines Completed if (condition) { layer = 2; } break; } case Death_Mountain_Interiors: { layer = 0; break; } case Lake_Hylia: { if (i_roomNo == 1) { // Lanayru Spring condition = dComIfGs_isEventBit(LAKEBED_TEMPLE_CLEARED); // Lakebed Temple has been completed if (condition) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED); // MDH has been started if (condition == false) { layer = 9; } else { layer = 2; } } } else { condition = dComIfGs_isEventBit(SKY_CANNON_REPAIRED); // Sky Cannon Repaired if (condition == false) { condition = dComIfGs_isEventBit(WARPED_SKY_CANNON_TO_LAKE_HYLIA); // Sky Cannon Warped to Lake Hylia if (condition == false) { layer = 2; } else { layer = 1; } } else { layer = 3; } } break; } case Castle_Town_Interiors: { if (condition = dComIfGs_isEventBit(LAKEBED_TEMPLE_CLEARED),condition) { // Lakebed Temple Completed layer = 2; if (condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED),condition) { // MDH Completed layer = 0; } } if (i_roomNo == 5) { // Telma's Bar layer = 4; } break; } case Castle_Town: { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED); // MDH Completed if (condition == false) { condition = dComIfGs_isEventBit(LAKEBED_TEMPLE_CLEARED); // Lakebed Temple Completed if (condition == false) { if ((i_roomNo == 3) && (condition = dComIfGs_isEventBit(ZORA_ESCORT_CLEARED),condition != false)) { // Zora Escort Cleared layer = 1; } else if (i_roomNo == 4) { layer = 1; } } else { layer = 2; } } else { if (((i_roomNo == 4) || (i_roomNo == 3)) || (i_roomNo == 1)) { layer = 1; } else { layer = 0; } } if (i_roomNo == 0) { if (dComIfGs_getStartPoint() == 0xF) { layer = 5; } } break; } case Zoras_Domain: { layer = 0; break; } case Upper_Zoras_River: { condition = dComIfGs_isEventBit(IZA_1_MINIGAME_UNLOCKED); // Iza 1 Unlocked if (condition != false) { layer = 1; } break; } case Gerudo_Desert: { layer = 8; condition = dComIfGs_isEventBit(VISITED_DESERT_FOR_THE_FIRST_TIME); // Have been to desert if (condition != false) { layer = 0; } break; } case Zoras_River: { condition = dComIfGs_isEventBit(IZA_1_MINIGAME_DONE); // Iza 1 Minigame Completed if (condition == false) { condition = dComIfGs_isEventBit(STARTED_IZA_1_MINIGAME); // Iza 1 Minigame Started if (condition != false) { layer = 2; } } else { layer = 1; } break; } case Ordon_Village: { if (i_roomNo == 0) { if (!dKy_daynight_check()) { layer = 0; } else { layer = 5; } } else { if (i_roomNo == 1) { condition = dComIfGs_isEventBit(ORDON_DAY_1_FINISHED); // Ordon Day 1 done if (condition) { condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo Saved if (condition) { layer = 2; } else { layer = 4; } } else { layer = 3; } } } break; } case Ordon_Village_Interiors: { /* not used in randomizer anymore. keeping for documentation sake if ( i_roomNo == 1 ) // Sera's Shop { condition = dComIfGs_isEventBit( BOUGHT_SLINGSHOT_FROM_SERA ); // Bought slinghot from Sera if ( condition ) { layer = 2; } }*/ if (i_roomNo == 2) { // Jaggle's House darkIsClear = dComIfGs_isDarkClearLV(0); if (darkIsClear == false) { condition = dComIfGs_isEventBit(FINISHED_SEWERS); // First Trip to Sewers done if (condition != false) { layer = 1; } } else { layer = 1; } } /* not used in randomizer anymore. keeping for documentation sake else { if ( i_roomNo == 5 ) // Rusl's House { darkIsClear = libtp::tp::d_save::isDarkClearLV( playerStatusBPtr, 0 ); if ( darkIsClear != false ) { layer = 2; } } }*/ break; } case Ordon_Spring: { condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo saved if (condition) { condition = dComIfGs_isEventBit(FINISHED_SEWERS); // First trip to Sewers done if (condition) { darkIsClear = dComIfGs_isDarkClearLV(0); if (darkIsClear != false) { layer = 2; } else { layer = 4; } } else { layer = 0; } } else { condition = dComIfGs_isEventBit(TALO_CHASES_MONKEY); // Sword training done on Ordon Day 2 if (condition) { layer = 3; } else { layer = 1; } } break; } case Ordon_Ranch: { condition = dComIfGs_isEventBit(ORDON_DAY_1_FINISHED); // Day 1 done if (condition) { condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo Saved if (condition) { condition = dComIfGs_isEventBit(WATCHED_CUTSCENE_AFTER_GOATS_2); // Saw CS after Goats 2 done if (condition) { layer = 2; dComIfG_get_timelayer(&layer); } else { layer = 9; } } else { layer = 2; } } else { layer = 12; } break; } case Hyrule_Field: { // First 3 twilights are cleared if ((dComIfGs_getSaveData()->getPlayer().getPlayerStatusB().mDarkClearLevelFlag & 0x7) == 0x7) { if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) { layer = 6; } else if (dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED)) { layer = 4; } else { layer = 0; } } else { layer = 0; } break; } case Outside_Castle_Town: { if (i_roomNo == 8) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED); // MDH Completed if (condition == false) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED); // MDH State Activated if (condition != false) { layer = 4; } } else { layer = 6; } } else { if (i_roomNo == 0x10) { condition = dComIfGs_isEventBit(GOT_WOOD_STATUE); // Wooden Statue Gotten if (condition == false) { condition = dComIfGs_isEventBit(TALKED_TO_LOUISE_ABOUT_THE_STOLEN_STATUE); // Talked to Louise after Medicine Scent if (condition == false) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED); // MDH Completed if (condition == false) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED); // MDH State Activated if (condition != false) { layer = 4; } else { layer = 6; } } else { layer = 6; } } else { layer = 1; } } else { layer = 6; } } else { if (i_roomNo == 0x11) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED); // MDH Completed if (condition == false) { condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED); // MDH State Activated if (condition != false) { layer = 4; } } else { layer = 0; } } } } break; } case Hidden_Village: { condition = dComIfGs_isEventBit(GAVE_ILIA_THE_WOOD_STATUE); // Ilia shown the wooden statue if (condition != false) { condition = dComIfGs_isEventBit(GOT_ILIAS_CHARM); // Ilia shown Ilia's Charm if (condition != false) { layer = 1; } } else { layer = 1; } break; } case Castle_Town_Shops: { if (i_roomNo == 5) { layer = 0; condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_STARTED); if (condition) { layer = 1; condition = dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED); if (condition) { layer = 0; } } } else { condition = dComIfGs_isEventBit(MALO_MART_CASTLE_TOWN_BRANCH_IS_OPEN); // CT Shop is Malo Mart if (condition != false) { layer = 1; } } break; } case Sacred_Grove: { layer = 2; break; } case Bulblin_Camp: { condition = dComIfGs_isEventBit(ESCAPED_BURNING_TENT_IN_BULBLIN_CAMP); // Escaped Burning Tent in Bulblin Camp if (condition) { if (i_roomNo == 3) // Other states for this room are very similar, but do not have the boar // in the dzx. { // Setting state 1 solves for any potential softlocks regarding the boar in that area. layer = 1; } else { layer = 3; } } break; } case Faron_Woods_Cave: { condition = dComIfGs_isEventBit(ORDON_DAY_2_OVER); // Talo saved if (condition != false) { layer = 1; } break; } case Hyrule_Castle_Sewers: { condition = dComIfGs_isEventBit(FINISHED_SEWERS); // Sewers Finished if (condition) { layer = 13; } else { layer = 14; } break; } case Hyrule_Castle: { if (((i_roomNo != 0xb) && (i_roomNo != 0xd)) && (i_roomNo != 0xe)) { layer = 1; } break; } case Fishing_Pond: case Fishing_Pond_Interiors: { switch (g_env_light.fishing_hole_season) { case 1: layer = 0; break; case 2: layer = 1; break; case 3: layer = 2; break; case 4: layer = 3; break; } break; } default: { break; } } } } return HOOK_CONTINUE; } HookAction hookPreOnStageSwitch(ModContext*, void* args, void* retval, void*) { const int i_stageNo = mods::arg(args, 0); const int i_no = mods::arg(args, 1); // Avoid trying to get the save table if stag info is NULL if (dComIfGp_getStageStagInfo() == NULL) { dComIfGs_onSaveSwitch(i_stageNo, i_no); return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreDecideDoStatus(ModContext*, void* args, void* retval, void*) { daAlink_c* i_this = mods::arg(args, 0); bool set_status = false; if (i_this->mAttList != NULL) { s16 actor_name = fopAcM_GetName(i_this->field_0x27f4); if (actor_name == fpcNm_Tag_Lv6Gate_e || (actor_name == fpcNm_TAG_KMSG_e && static_cast(i_this->field_0x27f4)->getType() == 3)) { // Separate check for striking sword into the pedestal for randomizer if (!i_this->checkEquipAnime() && randomizer_checkTempleOfTimeRequirement()) { i_this->setDoStatus(BUTTON_STATUS_STRIKE); set_status = true; } } } if (set_status) { i_this->decideCommonDoStatus(); return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreSearchBouDoor(ModContext*, void* args, void* retval, void*) { // In randomizer, we don't want Bo preventing us from entering his house on Day 2 if (daAlink_c::checkStageName("F_SP103")) { *static_cast(retval) = nullptr; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreCheckGroundSpecialMode(ModContext*, void* args, void* retval, void*) { daAlink_c* i_this = mods::arg(args, 0); if (i_this->mLinkAcch.ChkGroundHit() && !i_this->checkModeFlg(daAlink_c::MODE_PLAYER_FLY) && !i_this->checkMagneBootsOn() && i_this->checkEndResetFlg0(daAlink_c::ERFLG0_FORCE_WOLF_CHANGE)) { u8 stage = getStageID(); // In rando, don't transform in twilight fog unless we have shadow crystal if (!dComIfGs_isEventBit(TRANSFORMING_UNLOCKED) && (stage == Palace_of_Twilight || stage == Phantom_Zant_1 || stage == Phantom_Zant_2)) { *static_cast(retval) = FALSE; return HOOK_SKIP_ORIGINAL; } *static_cast(retval) = i_this->procCoMetamorphoseInit(); return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } void hookPostSetGetItemFace(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); const u16 i_itemNo = mods::arg(args, 1); switch (i_itemNo) { case dItemNo_Randomizer_WOOD_STICK_e: case dItemNo_Randomizer_SWORD_e: case dItemNo_Randomizer_SHIELD_e: case dItemNo_Randomizer_MASTER_SWORD_e: case dItemNo_Randomizer_LIGHT_SWORD_e: case dItemNo_Randomizer_MAGIC_LV1_e: i_this->setFaceBasicBck(dRes_ID_ALANM_BCK_FI_e); break; case dItemNo_Randomizer_FOOLISH_ITEM_e: i_this->setFaceBasicBck(dRes_ID_ALANM_BCK_FJ_e); break; } } HookAction hookPreSetGetSubBgm(ModContext*, void* args, void*, void*) { enum { SETYPE_HEART, SETYPE_ITEM_GET, SETYPE_ITEM_GET_MINI, SETYPE_ITEM_GET_ME, SETYPE_ITEM_GET_INSECT, SETYPE_ITEM_GET_SMELL, SETYPE_ITEM_GET_POU, SETYPE_ITEM_GET_ME_S, SETYPE_NONE, }; static constexpr u8 getSeTypeRandomizer[255] = { /* dItemNo_Randomizer_HEART_e */ SETYPE_NONE, /* dItemNo_Randomizer_GREEN_RUPEE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BLUE_RUPEE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_YELLOW_RUPEE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_RED_RUPEE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_PURPLE_RUPEE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_ORANGE_RUPEE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_SILVER_RUPEE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_S_MAGIC_e */ SETYPE_NONE, /* dItemNo_Randomizer_L_MAGIC_e */ SETYPE_NONE, /* dItemNo_Randomizer_BOMB_5_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_10_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_20_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_30_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_ARROW_10_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_ARROW_20_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_ARROW_30_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_ARROW_1_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_PACHINKO_SHOT_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_FOOLISH_ITEM_e */ SETYPE_NONE, /* dItemNo_Randomizer_NOENTRY_20_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_21_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WATER_BOMB_5_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_WATER_BOMB_10_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_WATER_BOMB_20_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_WATER_BOMB_30_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_INSECT_5_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_INSECT_10_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_INSECT_20_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_BOMB_INSECT_30_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_RECOVERY_FAILY_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_TRIPLE_HEART_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_SMALL_KEY_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_KAKERA_HEART_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_UTAWA_HEART_e */ SETYPE_HEART, /* dItemNo_Randomizer_MAP_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_COMPUS_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_DUNGEON_EXIT_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BOSS_KEY_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_DUNGEON_BACK_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_SWORD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_MASTER_SWORD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_WOOD_SHIELD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_SHIELD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_HYLIA_SHIELD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_TKS_LETTER_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WEAR_CASUAL_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WEAR_KOKIRI_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ARMOR_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_WEAR_ZORA_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_MAGIC_LV1_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_DUNGEON_EXIT_2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WALLET_LV1_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WALLET_LV2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_WALLET_LV3_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_55_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_56_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_57_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_58_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_59_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_60_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ZORAS_JEWEL_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_HAWK_EYE_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_WOOD_STICK_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BOOMERANG_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_SPINNER_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_IRONBALL_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_BOW_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_HOOKSHOT_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_HVY_BOOTS_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_COPY_ROD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_W_HOOKSHOT_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_KANTERA_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_LIGHT_SWORD_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_FISHING_ROD_1_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_PACHINKO_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_COPY_ROD_2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_77_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_78_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BOMB_BAG_LV2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_BOMB_BAG_LV1_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_BOMB_IN_BAG_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_82_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LIGHT_ARROW_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ARROW_LV1_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ARROW_LV2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_ARROW_LV3_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_87_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LURE_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BOMB_ARROW_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_HAWK_ARROW_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BEE_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_JEWEL_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WORM_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_JEWEL_BEE_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_JEWEL_WORM_ROD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_EMPTY_BOTTLE_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_RED_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_GREEN_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BLUE_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_MILK_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_HALF_MILK_BOTTLE_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_OIL_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WATER_BOTTLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_OIL_BOTTLE_2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_RED_BOTTLE_2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_UGLY_SOUP_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_HOT_SPRING_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_FAIRY_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_HOT_SPRING_2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_OIL2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_OIL_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NORMAL_BOMB_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WATER_BOMB_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_POKE_BOMB_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_FAIRY_DROP_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_WORM_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_DROP_BOTTLE_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_BEE_CHILD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_RARE_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_CHUCHU_RED_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_BLUE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_GREEN_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_YELLOW_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_PURPLE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LV1_SOUP_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LV2_SOUP_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LV3_SOUP_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LETTER_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_BILL_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_WOOD_STATUE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_IRIAS_PENDANT_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_HORSE_FLUTE_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_133_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_134_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_135_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_136_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_137_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_138_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_139_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_140_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_141_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_142_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_143_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_RAFRELS_MEMO_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ASHS_SCRIBBLING_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_146_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_147_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_148_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_149_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_150_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_151_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_152_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_153_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_154_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_155_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_YELLOW2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_OIL_BOTTLE3_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_SHOP_BEE_CHILD_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_CHUCHU_BLACK_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LIGHT_DROP_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_DROP_CONTAINER_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_DROP_CONTAINER02_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_DROP_CONTAINER03_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_FILLED_CONTAINER_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_MIRROR_PIECE_2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_MIRROR_PIECE_3_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_MIRROR_PIECE_4_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_168_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_169_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_170_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_171_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_172_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_173_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_174_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_175_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_SMELL_YELIA_POUCH_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_SMELL_PUMPKIN_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_SMELL_POH_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_SMELL_FISH_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_SMELL_CHILDREN_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_SMELL_MEDICINE_e */ SETYPE_ITEM_GET_SMELL, /* dItemNo_Randomizer_NOENTRY_182_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_183_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_184_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_185_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_186_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_187_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_188_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_189_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_190_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_NOENTRY_191_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_M_BEETLE_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_BEETLE_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_BUTTERFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_BUTTERFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_STAG_BEETLE_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_STAG_BEETLE_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_GRASSHOPPER_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_GRASSHOPPER_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_NANAFUSHI_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_NANAFUSHI_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_DANGOMUSHI_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_DANGOMUSHI_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_MANTIS_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_MANTIS_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_LADYBUG_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_LADYBUG_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_SNAIL_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_SNAIL_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_DRAGONFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_DRAGONFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_ANT_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_ANT_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_M_MAYFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_F_MAYFLY_e */ SETYPE_ITEM_GET_INSECT, /* dItemNo_Randomizer_NOENTRY_216_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_217_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_218_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_219_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_220_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_221_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_222_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_223_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_POU_SPIRIT_e */ SETYPE_ITEM_GET_POU, /* dItemNo_Randomizer_NOENTRY_225_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_226_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_227_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_228_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_229_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_230_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_231_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_NOENTRY_232_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_ANCIENT_DOCUMENT_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_AIR_LETTER_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_ANCIENT_DOCUMENT2_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_LV7_DUNGEON_EXIT_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LINKS_SAVINGS_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_SMALL_KEY2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_POU_FIRE1_e */ SETYPE_NONE, /* dItemNo_Randomizer_POU_FIRE2_e */ SETYPE_NONE, /* dItemNo_Randomizer_POU_FIRE3_e */ SETYPE_NONE, /* dItemNo_Randomizer_POU_FIRE4_e */ SETYPE_NONE, /* dItemNo_Randomizer_BOSSRIDER_KEY_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_TOMATO_PUREE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_TASTE_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_LV5_BOSS_KEY_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_SURFBOARD_e */ SETYPE_NONE, /* dItemNo_Randomizer_KANTERA2_e */ SETYPE_ITEM_GET_ME, /* dItemNo_Randomizer_L2_KEY_PIECES1_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_L2_KEY_PIECES2_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_L2_KEY_PIECES3_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_KEY_OF_CARAVAN_e */ SETYPE_ITEM_GET_MINI, /* dItemNo_Randomizer_LV2_BOSS_KEY_e */ SETYPE_ITEM_GET, /* dItemNo_Randomizer_KEY_OF_FILONE_e */ SETYPE_ITEM_GET_MINI, }; static constexpr u32 bgmLabel[8] = { Z2BGM_HEART_GET, Z2BGM_ITEM_GET, Z2BGM_ITEM_GET_MINI, Z2BGM_ITEM_GET_ME, Z2BGM_ITEM_GET_INSECT, Z2BGM_ITEM_GET_SMELL, Z2BGM_ITEM_GET_POU, Z2BGM_ITEM_GET_ME_S, }; auto* i_this = mods::arg(args, 0); const int i_itemNo = mods::arg(args, 1); u32 se_type = getSeTypeRandomizer[i_itemNo]; if (se_type == SETYPE_ITEM_GET_ME && i_this->mProcVar4.field_0x3010 == 0) { se_type = SETYPE_ITEM_GET_ME_S; } if (se_type != SETYPE_NONE || i_itemNo == dItemNo_Randomizer_FOOLISH_ITEM_e) { mDoAud_subBgmStart(bgmLabel[se_type]); dComIfGp_setMesgBgmOn(); } return HOOK_SKIP_ORIGINAL; } HookAction hookPreProcCoGetItem(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (i_this->field_0x32cc != 0 || i_this->mProcVar2.field_0x300c != dItemNo_Randomizer_POU_SPIRIT_e) { return HOOK_CONTINUE; } auto* item_partner_p = static_cast(fopAcM_getItemEventPartner(i_this)); if (item_partner_p == nullptr || fpcM_IsCreating(fpcM_GetID(item_partner_p))) { return HOOK_CONTINUE; } // Don't show special text in rando if (dComIfGs_getPohSpiritNum() == 20) { i_this->field_0x32cc = i_this->mProcVar2.field_0x300c + 0x65; } return HOOK_CONTINUE; } u8 hookProcCoWarpInit_prevSlot18 = dItemNo_NONE_e; bool hookProcCoWarpInit_restoreSlot18 = false; HookAction hookPreProcCoWarpInit(ModContext*, void* args, void*, void*) { hookProcCoWarpInit_restoreSlot18 = false; auto* i_this = mods::arg(args, 0); const int param_0 = mods::arg(args, 1); const int param_1 = mods::arg(args, 2); // copy necessary functionality from og function to set up patch state if (param_0 != 0 || i_this->checkWolf()) { return HOOK_CONTINUE; } const BOOL isSideWarp = param_1 == 0 && ((daAlink_c::checkStageName("F_SP125") && fopAcM_GetRoomNo(i_this) == 4) || (daAlink_c::checkStageName("D_MN08") && fopAcM_GetRoomNo(i_this) == 0)); if (isSideWarp || !i_this->checkBossRoom() || fopAcM_GetRoomNo(i_this) != 50) { return HOOK_CONTINUE; } char stageName[32]; SAFE_STRCPY(stageName, dComIfGp_getStartStageName()); for (int i = 0; i < 32; i++) { if ((s64)stageName[i] == 0) { stageName[i - 1] = 0; break; } } if (!checkItemGet(dItemNo_DUNGEON_EXIT_e, 1) && !(checkItemGet(dItemNo_DUNGEON_BACK_e, 1) && strcmp(stageName, dComIfGs_getWarpStageName()) == 0)) { return HOOK_CONTINUE; } // In rando, we only want to clear the Ooccoo slot if Ooccoo is in it. // so continue with normal function if Ooccoo is in slot. u8 ooccooSlot = dComIfGs_getItem(SLOT_18, false); if (ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_e || ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_2_e || ooccooSlot == dItemNo_Randomizer_LV7_DUNGEON_EXIT_e) { return HOOK_CONTINUE; } hookProcCoWarpInit_prevSlot18 = ooccooSlot; hookProcCoWarpInit_restoreSlot18 = true; return HOOK_CONTINUE; } void hookPostProcCoWarpInit(ModContext*, void*, void*, void*) { if (hookProcCoWarpInit_restoreSlot18) { dComIfGs_setItem(SLOT_18, hookProcCoWarpInit_prevSlot18); hookProcCoWarpInit_restoreSlot18 = false; } } void hookPostBqEnd(ModContext*, void* args, void* retval, void*) { // If the player is wolf, they will softlock after the defeat cutscene is completed. checkTransformFromWolf(); } HookAction hookPreCheckOpenMsgDoor(ModContext*, void* args, void* retval, void*) { daDoor20_c* i_this = mods::arg(args, 0); int* param_1 = mods::arg(args, 1); if (!door_param2_c::isMsgDoor(i_this)) { *static_cast(retval) = 1; return HOOK_SKIP_ORIGINAL; } int msgNo = door_param2_c::getMsgNo(i_this); if (msgNo == 0xffff) { *param_1 = 0; *static_cast(retval) = 1; return HOOK_SKIP_ORIGINAL; } i_this->field_0x624.init(NULL, msgNo, 0, NULL); int rv = 1; // If we are in SPR, we don't want Yeta's msg flow to prevent us from opening the door if we haven't talked to her. if (!daAlink_c::checkStageName("D_MN11")) { rv = i_this->field_0x624.checkOpenDoor(i_this, param_1); } dMsgObject_endFlowGroup(); *static_cast(retval) = rv; return HOOK_SKIP_ORIGINAL; } void hookPostEmkDemoCameraEnd(ModContext*, void* args, void* retval, void*) { e_mk_class* i_this = mods::arg(args, 0); switch (i_this->demoSubMode) { case 6: if (i_this->demoCamCounter == 180) { // If the player is wolf, they will void and lose the boomerang check. checkTransformFromWolf(); } break; } } void hookPostChangeScene4Event(ModContext*, void* args, void* retval, void*) { int i_exitId = mods::arg(args, 0); s8 room_no = mods::arg(args, 1); stage_scls_info_dummy_class* scls; if (room_no == -1) { scls = dComIfGp_getStageSclsInfo(); } else { dStage_roomDt_c* room = dComIfGp_roomControl_getStatusRoomDt(room_no); scls = room->getSclsInfo(); } if (scls == NULL) { return; } stage_scls_info_class* scls_info = &scls->m_entries[i_exitId]; // If randomizer is active and we're loading the first spawn, set our starting time of day if (std::strcmp(scls_info->mStage, "F_SP103") && scls_info->mRoom == 1 && scls_info->mStart == 1) { dKy_set_nexttime(15.0f * randomizer_GetContext().mStartHour); g_randomizerState.mUpdateTracker = true; } } HookAction hookPreStagePlayerInit(ModContext*, void* args, void* retval, void*) { void* i_data = mods::arg(args, 1); int num = mods::arg(args, 2); stage_actor_class* player = (stage_actor_class*)((int*)i_data + 1); stage_actor_data_class* player_data = player->m_entries; // Modify entrance types in certain situations to avoid crashes for (size_t i = 0; i < num; ++i) { u8& entranceType = reinterpret_cast(&player_data[i].base.parameters)[2]; switch (entranceType) { // Only replace the entrance type if it is a door. case 0x80: case 0xA0: case 0xB0: { if (dComIfGs_getTransformStatus() == TF_STATUS_WOLF) { // Change the entrance type to play the animation of walking out of the // loading zone instead of entering through the door. entranceType = 0x50; } break; } // Water swimming entrance. // If we have this, but there isn't any water to spawn in, the game hangs case 0xD0: { // If there's no water, change to non-swimming entrance if (getStageID() == Lake_Hylia && !dComIfGs_isEventBit(WARPED_METEOR_TO_ZORAS_DOMAIN)) { entranceType = 0x50; } break; } default: break; } } return HOOK_CONTINUE; } void hookPostKytag08Execute(ModContext*, void* args, void* retval, void*) { kytag08_class* i_this = mods::arg(args, 0); if (i_this->mSizeTimer < 100 || dComIfGs_BossLife_public_Get() == 1) { dComIfGs_BossLife_public_Set(0); i_this->mTargetAvoidPos = i_this->current.pos; i_this->mSizeTimer = 180; mDoAud_startFogWipeTrigger(&i_this->current.pos); } } HookAction hookPreNpcTChkEvtBit(ModContext*, void* args, void* retval, void*) { u32 i_no = mods::arg(args, 0); switch (i_no) { case 0x153: // Checking if the player has Ending Blow if (getStageID() == Hidden_Skill) { *static_cast(retval) = TRUE; return HOOK_SKIP_ORIGINAL; } break; case 0x40: // Checking if the player has completed Goron Mines if (getStageID() == Kakariko_Village_Interiors) { // Return true so Barnes will sell bombs no matter what *static_cast(retval) = TRUE; return HOOK_SKIP_ORIGINAL; } break; } return HOOK_CONTINUE; } HookAction hookPreNpcFChkEvtBit(ModContext*, void* args, void* retval, void*) { u32 i_no = mods::arg(args, 0); // shad handling if (std::strcmp(dComIfGp_getStartStageName(), "R_SP209") == 0) { switch (i_no) { case 0x10B: // spawn even if vanilla city requirements aren't met *static_cast(retval) = TRUE; return HOOK_SKIP_ORIGINAL; case 0x12E: case 0x12F: // skip vanilla cannon spawn failure and move checks *static_cast(retval) = FALSE; return HOOK_SKIP_ORIGINAL; case 0x311: // despawn after custom flag is set *static_cast(retval) = dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x333]); return HOOK_SKIP_ORIGINAL; } } switch (i_no) { case 0x169: // Checking if Raised Mirror in Mirror Chamber // Only let Auru despawn in randomizer if we already collected his item if (getStageID() == Lake_Hylia) { *static_cast(retval) = dComIfGs_isEventBit(GOT_AURUS_MEMO); return HOOK_SKIP_ORIGINAL; } break; } return HOOK_CONTINUE; } HookAction hookPreNpcBansIsDelete(ModContext*, void* args, void* retval, void*) { daNpc_Bans_c* i_this = mods::arg(args, 0); switch (i_this->mType) { case 3: // MAKING_BOMBS *static_cast(retval) = TRUE; return HOOK_SKIP_ORIGINAL; case 4: // SHOP *static_cast(retval) = FALSE; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) { daNpcF_c* i_this = mods::arg(args, 0); int& i_forceSpeak = mods::arg_ref(args, 1); u16 i_priority = mods::arg(args, 4); // kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots if (i_this->eventInfo.getArchiveName() == nullptr) { return HOOK_CONTINUE; } const std::string arcName = i_this->eventInfo.getArchiveName(); if (arcName == "Bou4" && i_priority == 40) { i_forceSpeak = FALSE; } return HOOK_CONTINUE; } void hookPostFairyAppearDemoCall(ModContext*, void* args, void* retval, void*) { daNpc_Fairy_c* i_this = mods::arg(args, 0); // randomizer overrides EVT_APPEAR_50F_04 set to always be EVT_APPEAR_50F_01 if (i_this->field_0xff4 == 12) { i_this->field_0xff4 = 9; } } void hookPostYkMIsDelete(ModContext*, void* args, void* retval, void*) { daNpc_ykM_c* i_this = mods::arg(args, 0); if (i_this->mType == daNpc_ykM_c::TYPE_COOK) { // We don't want cooking Yeto to leave the dungeon, even if the BK is obtained. *static_cast(retval) = FALSE; } } void hookPostYkWIsDelete(ModContext*, void* args, void* retval, void*) { daNpc_ykW_c* i_this = mods::arg(args, 0); if (i_this->field_0xf80 == 1) { // We don't want Yeta to leave the dungeon, even if the BK is obtained. *static_cast(retval) = FALSE; } } void hookPostNpcZrzIsDelete(ModContext*, void* args, void* retval, void*) { if (dComIfGs_isEventBit(GOT_ZORA_ARMOR_FROM_RUTELA)) { return; } auto* i_this = mods::arg(args, 0); if (i_this->mDemoMode != 3) { return; } const int roomNo = fopAcM_GetRoomNo(i_this); if (dComIfGs_isSwitch(i_this->mSwitch1, roomNo) && dComIfGs_isSwitch(i_this->mSwitch2, roomNo)) { *static_cast(retval) = FALSE; } } u8 hookNpcShadWaitType1_patchItemNo = dItemNo_NONE_e; bool hookNpcShadWaitType1_isPatchItemNo = false; HookAction hookPreNpcShadWaitType1(ModContext*, void* args, void* retval, void*) { dEvt_control_c* event = dComIfGp_getEvent(); hookNpcShadWaitType1_isPatchItemNo = false; if (event->mPreItemNo >= dItemNo_Randomizer_ANCIENT_DOCUMENT_e) { // backup original item no so we can restore it in post hook hookNpcShadWaitType1_patchItemNo = event->mPreItemNo; // force item no so that the dominion rod check is forced regardless // of which skybook you have event->mPreItemNo = dItemNo_Randomizer_ANCIENT_DOCUMENT_e; hookNpcShadWaitType1_isPatchItemNo = true; } return HOOK_CONTINUE; } void hookPostNpcShadWaitType1(ModContext*, void* args, void* retval, void*) { if (hookNpcShadWaitType1_isPatchItemNo) { dComIfGp_getEvent()->mPreItemNo = hookNpcShadWaitType1_patchItemNo; hookNpcShadWaitType1_isPatchItemNo = false; } } // kinda ugly way to handle the wood statue. using a flag here to tell when its the right time // to be overriding the setWarashibeItem call with offWarashibeItem instead. maybe look into // a better solution later bool hookYeliaTakeWoodStatue_isOffWarashibeItem = false; HookAction hookPreNpcYeliaCutTakeWoodStatue(ModContext*, void* args, void*, void*) { hookYeliaTakeWoodStatue_isOffWarashibeItem = false; auto* i_this = mods::arg(args, 0); const int i_staffId = mods::arg(args, 1); const int yeliaStaffId = dComIfGp_evmng_getMyStaffId("Yelia", i_this, -1); if (i_staffId != yeliaStaffId || !dComIfGp_getEventManager().getIsAddvance(i_staffId)) { return HOOK_CONTINUE; } int* prm = dComIfGp_evmng_getMyIntegerP(i_staffId, "prm"); if (prm != nullptr && *prm == 99) { hookYeliaTakeWoodStatue_isOffWarashibeItem = true; } return HOOK_CONTINUE; } void hookPostNpcYeliaCutTakeWoodStatue(ModContext*, void*, void*, void*) { hookYeliaTakeWoodStatue_isOffWarashibeItem = false; } HookAction hookPreSetWarashibeItem(ModContext*, void* args, void*, void*) { if (!hookYeliaTakeWoodStatue_isOffWarashibeItem) { return HOOK_CONTINUE; } const u8 itemNo = mods::arg(args, 1); if (itemNo == dItemNo_NONE_e) { offWarashibeItem(dItemNo_Randomizer_WOOD_STATUE_e); return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } // kinda hacky, force the SkipInfo check to always fail so that the armor always gets created, // then restore the original SkipInfo afterward. needed to make the ball and chain check respawn // if the player leaves the room without grabbing it daE_MD_c* hookEMdCreate_skipActor = nullptr; u8 hookEMdCreate_prevSkipInfo = 0; HookAction hookPreEMdCreate(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (cDmr_SkipInfo == 0 || i_this->current.pos.z <= -1500.0f) { return HOOK_CONTINUE; } hookEMdCreate_skipActor = i_this; hookEMdCreate_prevSkipInfo = cDmr_SkipInfo; cDmr_SkipInfo = 0; return HOOK_CONTINUE; } void hookPostEMdCreate(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (hookEMdCreate_skipActor != i_this) { return; } cDmr_SkipInfo = hookEMdCreate_prevSkipInfo; hookEMdCreate_skipActor = nullptr; hookEMdCreate_prevSkipInfo = 0; } HookAction hookPreOrderMapToolEvent(ModContext*, void* args, void*, void*) { auto eventId = mods::arg(args, 1); // skip starting the master sword cutscene so we can handle the checks manually if (daAlink_c::checkStageName("F_SP117") && eventId == 2) { // Set the necessary flags to de-spawn the MS and set the save file event flag. dComIfGs_onTmpBit(0x820); dComIfGs_onEventBit(0x2120); return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); (i_this->*i_this->mActionFunc[1])(); dComIfG_Ccsp()->Set(&i_this->mCyl); i_this->mBtk.play(); i_this->mBrk.play(); if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) { // Don't automatically give the master sword in randomizer fopAcM_delete(i_this); } *static_cast(retval) = 1; return HOOK_SKIP_ORIGINAL; } void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* retval, void* userdata) { if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) { randomizer::session::registerStartingLocation(); } } void hookPostTbox2Create(ModContext*, void* args, void* retval, void*) { if (*static_cast(retval) != 1) { return; } auto* i_this = mods::arg(args, 0); u8 tboxId = fopAcM_GetParamBit(i_this, 16, 8); if (tboxId == 0xFF || !dComIfGs_isTbox(tboxId)) { return; } // If the flag for this box is set, open it // Set the action for not allowing the player to open it i_this->init_actionWait(); // Set the animation frame to open i_this->mpBck->setFrame(i_this->mpBck->getEndFrame()); // Set collision to open if (i_this->mpBgW != NULL) { dComIfG_Bgsp().Release(i_this->mpBgW); } if (i_this->mBoxBgW != NULL) { dComIfG_Bgsp().Regist(i_this->mBoxBgW, i_this); } } HookAction hookPreTbox2SetGetDemoItem(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (i_this->mReturnRupee) { return HOOK_CONTINUE; } u8 tboxId = fopAcM_GetParamBit(i_this, 16, 8); if (tboxId != 0xFF) { dComIfGs_onTbox(tboxId); } return HOOK_CONTINUE; } u8 hookGameoverCreate_prevSlot18 = dItemNo_NONE_e; bool hookGameoverCreate_restoreSlot18 = false; HookAction hookPreGameoverCreate(ModContext*, void*, void*, void*) { if (dMeter2Info_getGameOverType() == 1 && strcmp(dComIfGp_getLastPlayStageName(), "D_MN10A") == 0) { // In rando, we only want to clear the Ooccoo slot if Ooccoo is in it. // so continue with normal function if Ooccoo is in slot. u8 ooccooSlot = dComIfGs_getItem(SLOT_18, false); if (ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_e || ooccooSlot == dItemNo_Randomizer_DUNGEON_EXIT_2_e) { return HOOK_CONTINUE; } hookGameoverCreate_prevSlot18 = ooccooSlot; hookGameoverCreate_restoreSlot18 = true; } return HOOK_CONTINUE; } void hookPostGameoverCreate(ModContext*, void*, void*, void*) { if (hookGameoverCreate_restoreSlot18) { dComIfGs_setItem(SLOT_18, hookGameoverCreate_prevSlot18); hookGameoverCreate_restoreSlot18 = false; } } inline void createPalaceSolsRewardItem(daObjSwBallC_c* i_this) { cXyz scale{1.0f, 1.0f, 1.0f}; cXyz position{250.0f, -200.0f, 11000.0f}; initCreatePlayerItem( dItemNo_Randomizer_WOOD_STICK_e, 0x81, &position, fopAcM_GetRoomNo(i_this), &i_this->shape_angle, &scale); } void hookPostSwBallCreate(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); if (*static_cast(retval) != 1) { return; } if (fopAcM_isSwitch(i_this, 0x3d) && fopAcM_isSwitch(i_this, 0x3e)) { createPalaceSolsRewardItem(i_this); } } HookAction hookPreSwBallActionWait(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (!fopAcM_isSwitch(i_this, 0x3d) || !fopAcM_isSwitch(i_this, 0x3e)) { return HOOK_CONTINUE; } // Don't play the cutscene in rando, just spawn in the item for // Palace of Twilight Collect Both Sols dComIfGs_onTbox(10); dComIfGs_onTbox(11); i_this->calcLightBallScale(); i_this->field_0x574->setPlaySpeed(1.0f); if (i_this->field_0x574->play() != 0 && !fopAcM_isSwitch(i_this, 0x3f)) { fopAcM_onSwitch(i_this, 0x3f); fopAcM_onSwitch(i_this, 0x27); createPalaceSolsRewardItem(i_this); } return HOOK_SKIP_ORIGINAL; } void hookPostDitemExecute(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); // Certain items use field models that are too big to fit in link's hands so we want to scale them down to fit. switch (i_this->m_itemNo) { case dItemNo_Randomizer_MIRROR_PIECE_1_e: case dItemNo_Randomizer_MIRROR_PIECE_2_e: case dItemNo_Randomizer_MIRROR_PIECE_3_e: case dItemNo_Randomizer_MIRROR_PIECE_4_e: { i_this->scale.x = 0.05f; break; } case dItemNo_Randomizer_MASTER_SWORD_e: case dItemNo_Randomizer_LIGHT_SWORD_e: { i_this->scale.x = 0.001f; break; } } } // pretty ugly way of handling this, but oh well. basically, we reconstruct the actionTable // and use it to check that we're in the correct action before proceeding. then store info // about what level we're on, and use it in the post-hook to undo the flag that's normally set. daObjBossWarp_c* hookBossWarpDemoProc_patchActor = nullptr; int hookBossWarpDemoProc_nowLevel = -1; HookAction hookPreBossWarpDemoProc(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); hookBossWarpDemoProc_patchActor = nullptr; hookBossWarpDemoProc_nowLevel = -1; static const char* const actionTable[15] = { "WAIT", "APPEAR", "DISAPPEAR", "SCENE_CHG", "STONE_FALL", "STONE_MIDNA", "WALK_TARGET1", "APPEAR_END", "STONE_DELETE", "STONE_PUTAWAY", "WCHECK", "SETPOS", "SCALING", "STONE_SCALE", "HEART_MOVE", }; bool isRewardAction = dComIfGp_evmng_getIsAddvance(i_this->mStaffId) && dComIfGp_evmng_getMyActIdx(i_this->mStaffId, actionTable, 15, 0, 0) == 4; if (!isRewardAction) { return HOOK_CONTINUE; } // this was a static function in the original TU, so reconstructing it for use here auto getNowLevel = []() { static const char* const stages[9] = { "D_MN05A", "D_MN04A", "D_MN01A", "D_MN10A", "D_MN11A", "D_MN06A", "D_MN07A", "D_MN08A", "D_MN01A", }; for (int i = 0; i < 9; i++) { if (std::strcmp(dComIfGp_getStartStageName(), stages[i]) == 0) { return i; } } return -1; }; hookBossWarpDemoProc_patchActor = i_this; hookBossWarpDemoProc_nowLevel = getNowLevel(); return HOOK_CONTINUE; } void hookPostBossWarpDemoProc(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); if (hookBossWarpDemoProc_patchActor != i_this) { return; } int level = hookBossWarpDemoProc_nowLevel; hookBossWarpDemoProc_patchActor = nullptr; hookBossWarpDemoProc_nowLevel = -1; // undo the flag that was set in the original function switch (level) { case 0: dComIfGs_offCollectCrystal(0); break; case 1: dComIfGs_offCollectCrystal(1); break; case 2: dComIfGs_offCollectCrystal(2); break; case 4: dComIfGs_offCollectMirror(1); break; case 5: dComIfGs_offCollectMirror(2); break; case 6: dComIfGs_offCollectMirror(3); break; } } bool hookUkiCatch_isSkipSetBottle = false; HookAction hookPreUkiCatch(ModContext*, void* args, void*, void*) { auto* i_this = mods::arg(args, 0); hookUkiCatch_isSkipSetBottle = false; fopAc_ac_c* mgfish_a = fopAcM_SearchByID(i_this->mg_fish_id); mg_fish_class* mgfish = (mg_fish_class*)mgfish_a; if (mgfish == nullptr) { return HOOK_CONTINUE; } // replicating the bottle catch check here to remove rng check by overriding whatever catch // you have if you meet the bottle catch conditions if (!dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[468]) && strcmp(dComIfGp_getStartStageName(), "F_SP127") == 0) { fopAc_ac_c* player = dComIfGp_getPlayer(0); cXyz bin_pos(6800.0f, 30.0f, -270.0f); bin_pos -= player->current.pos; if (bin_pos.abs() < 2500.0f) { s16 angle = player->shape_angle.y - cM_atan2s(bin_pos.x, bin_pos.z); if (angle < 0x4000 && angle > -0x4000) { mgfish->mCaughtType = MG_CATCH_BIN; } } } // if catching a bottle, set a flag to skip giving an empty bottle so that the FLW patch // can handle the check if (mgfish->mCaughtType == MG_CATCH_BIN) { hookUkiCatch_isSkipSetBottle = true; } return HOOK_CONTINUE; } void hookPostUkiCatch(ModContext*, void*, void*, void*) { hookUkiCatch_isSkipSetBottle = false; } HookAction hookPreSetEmptyBottle(ModContext*, void*, void*, void*) { // coming from uki_catch, skip giving bottle. let FLW patch handle it if (hookUkiCatch_isSkipSetBottle) { return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } HookAction hookPreNpcZrCIsDelete(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); if (i_this->mType == 4 || i_this->mType == 0 || i_this->mType == 1 || (i_this->mType == 2 && daNpcF_chkEvtBit(0x108)) || i_this->mType == 3 ) { *static_cast(retval) = FALSE; return HOOK_SKIP_ORIGINAL; } return HOOK_CONTINUE; } void hookPostObjZraRockCreate(ModContext*, void* args, void* retval, void*) { auto* i_this = mods::arg(args, 0); if (*static_cast(retval) != cPhs_ERROR_e) { return; } // if returned cPhs_ERROR_e, check if it's because the switch was set. if (dComIfGs_isSwitch((fopAcM_GetParam(i_this) >> 8) & 0xff, fopAcM_GetRoomNo(i_this))) { // Don't delete the rock when we're following rutela if (!dComIfGs_isEventBit(GOT_ZORA_ARMOR_FROM_RUTELA) && dComIfGs_isEventBit(ZORA_ESCORT_CLEARED)) { *static_cast(retval) = cPhs_COMPLEATE_e; } } } void hookPostScnPlyPhase1(ModContext*, void*, void*, void*) { if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 && dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12) { // Undo item set for rando dComIfGs_offItemFirstBit(dItemNo_HORSE_FLUTE_e); dComIfGs_setItem(SLOT_21, dItemNo_NONE_e); } } } ModResult initialize() { #define ADD_HOOK_PRE(originalFn, hookFn) \ if (mods::hook::add_pre(hookFn) != MOD_OK) { \ mods::log::error("Failed to add pre-hook for " #originalFn); \ return MOD_ERROR; \ } #define ADD_HOOK_POST(originalFn, hookFn) \ if (mods::hook::add_post(hookFn) != MOD_OK) { \ mods::log::error("Failed to add post-hook for " #originalFn); \ return MOD_ERROR; \ } #define ADD_HOOK_REPLACE(originalFn, hookFn) \ if (mods::hook::replace(hookFn) != MOD_OK) { \ mods::log::error("Failed to add replace-hook for " #originalFn); \ return MOD_ERROR; \ } ADD_HOOK_PRE(dFile_select_c__selectDataNameMove, hookPreSelectDataNameMove); ADD_HOOK_PRE(dFile_select_c__dataSelect, hookPreDataSelect); ADD_HOOK_POST(dFile_info_c__setSaveData, hookPostSetSaveData); ADD_HOOK_PRE(dSv_event_c__isEventBit, hookPreIsEventBit); ADD_HOOK_PRE(dSv_event_c__onEventBit, hookPreOnEventBit); ADD_HOOK_PRE(dSv_memBit_c__isSwitch, hookPreMembitIsSwitch); ADD_HOOK_PRE(dSv_memBit_c__onSwitch, hookPreMembitOnSwitch); ADD_HOOK_PRE(dSv_memBit_c__onDungeonItem, hookPreOnDungeonItem); ADD_HOOK_PRE(dSv_memBit_c__offDungeonItem, hookPreOffDungeonItem); ADD_HOOK_PRE(dSv_memBit_c__isDungeonItem, hookPreIsDungeonItem); ADD_HOOK_PRE(dSv_player_status_b_c__isDarkClearLV, hookPreIsDarkClearLV); ADD_HOOK_PRE(dSv_player_item_c__checkEmptyBottle, hookPreCheckEmptyBottle); ADD_HOOK_POST(dSv_player_item_c__setLineUpItem, hookPostSetLineUpItem); ADD_HOOK_PRE(dSv_info_c__onSwitch, hookPreSaveInfoOnSwitch); ADD_HOOK_PRE(setNextStage, hookPreSetNextStage); ADD_HOOK_PRE(ObjGb_Create, hookPreObjGbCreate); ADD_HOOK_POST(readItemTexture, hookPostReadItemTexture); ADD_HOOK_PRE(dShopSystem_c__seq_decide_yes, hookPreShopSeqDecideYes); ADD_HOOK_PRE(dItemData_CheckFieldItemCreateHeap, hookPreCheckFieldItemCreateHeap); ADD_HOOK_POST(dEvt_control_c__talkEnd, hookPostTalkEnd); ADD_HOOK_PRE(dComIfG_play_c__getLayerNo_common_common, hookPreGetLayerNo); ADD_HOOK_PRE(dItem_getItemFunc, hookPreGetItemFunc); ADD_HOOK_PRE(dItem_checkItemGet, hookPreCheckItemGet); ADD_HOOK_PRE(onStageSwitch, hookPreOnStageSwitch); ADD_HOOK_PRE(daAlink_c__decideDoStatus, hookPreDecideDoStatus); ADD_HOOK_PRE(searchBouDoor, hookPreSearchBouDoor); ADD_HOOK_PRE(daAlink_c__checkGroundSpecialMode, hookPreCheckGroundSpecialMode); ADD_HOOK_POST(daAlink_c__setGetItemFace, hookPostSetGetItemFace); ADD_HOOK_PRE(daAlink_c__setGetSubBgm, hookPreSetGetSubBgm); ADD_HOOK_PRE(daAlink_c__procCoGetItem, hookPreProcCoGetItem); ADD_HOOK_PRE(daAlink_c__procCoWarpInit, hookPreProcCoWarpInit); ADD_HOOK_POST(daAlink_c__procCoWarpInit, hookPostProcCoWarpInit); ADD_HOOK_POST(bq_end, hookPostBqEnd); ADD_HOOK_PRE(daDoor20_c__checkOpenMsgDoor, hookPreCheckOpenMsgDoor); ADD_HOOK_POST(e_mk_demo_camera_end, hookPostEmkDemoCameraEnd); ADD_HOOK_POST(changeScene4Event, hookPostChangeScene4Event); ADD_HOOK_PRE(stage_playerInit, hookPreStagePlayerInit); ADD_HOOK_POST(Kytag08_Execute, hookPostKytag08Execute); ADD_HOOK_PRE(NpcT_chkEvtBit, hookPreNpcTChkEvtBit); ADD_HOOK_PRE(NpcF_chkEvtBit, hookPreNpcFChkEvtBit); ADD_HOOK_PRE(daNpcF_c__orderEvent, hookPreNpcFOrderEvent); ADD_HOOK_PRE(daNpc_Bans_c__isDelete, hookPreNpcBansIsDelete); ADD_HOOK_POST(daNpc_Fairy_c__AppearDemoCall, hookPostFairyAppearDemoCall); ADD_HOOK_PRE(daNpcShad_c__wait_type1, hookPreNpcShadWaitType1); ADD_HOOK_POST(daNpcShad_c__wait_type1, hookPostNpcShadWaitType1); ADD_HOOK_PRE(daNpc_Yelia_c__cutTakeWoodStatue, hookPreNpcYeliaCutTakeWoodStatue); ADD_HOOK_POST(daNpc_Yelia_c__cutTakeWoodStatue, hookPostNpcYeliaCutTakeWoodStatue); ADD_HOOK_PRE(dSv_player_item_c__setWarashibeItem, hookPreSetWarashibeItem); ADD_HOOK_POST(daNpc_ykM_c__isDelete, hookPostYkMIsDelete); ADD_HOOK_POST(daNpc_ykW_c__isDelete, hookPostYkWIsDelete); ADD_HOOK_PRE(daE_MD_c__create, hookPreEMdCreate); ADD_HOOK_POST(daE_MD_c__create, hookPostEMdCreate); ADD_HOOK_PRE(orderMapToolEvent, hookPreOrderMapToolEvent); ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute); ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene); ADD_HOOK_POST(daNpc_zrZ_c__isDelete, hookPostNpcZrzIsDelete); ADD_HOOK_POST(daTbox2_c__Create, hookPostTbox2Create); ADD_HOOK_PRE(daTbox2_c__setGetDemoItem, hookPreTbox2SetGetDemoItem); ADD_HOOK_PRE(dGameover_c___create, hookPreGameoverCreate); ADD_HOOK_POST(dGameover_c___create, hookPostGameoverCreate); ADD_HOOK_POST(daObjSwBallC_c__Create, hookPostSwBallCreate); ADD_HOOK_PRE(daObjSwBallC_c__actionWait, hookPreSwBallActionWait); ADD_HOOK_POST(daDitem_c__execute, hookPostDitemExecute); ADD_HOOK_PRE(daObjBossWarp_c__demoProc, hookPreBossWarpDemoProc); ADD_HOOK_POST(daObjBossWarp_c__demoProc, hookPostBossWarpDemoProc); ADD_HOOK_PRE(mgRod_lure_heart, hookPreLureHeart); ADD_HOOK_POST(mgRod_lure_heart, hookPostLureHeart); ADD_HOOK_PRE(mgRod_uki_catch, hookPreUkiCatch); ADD_HOOK_POST(mgRod_uki_catch, hookPostUkiCatch); ADD_HOOK_PRE(dSv_player_item_c__setEmptyBottle, hookPreSetEmptyBottle); ADD_HOOK_PRE(daNpc_zrC_c__isDelete, hookPreNpcZrCIsDelete); ADD_HOOK_POST(daObjZraRock_c__create, hookPostObjZraRockCreate); //ADD_HOOK_POST(phase_1__dScnPly_c, hookPostScnPlyPhase1); return MOD_OK; } ModResult uninstall() { auto svc_hook = session::svc_mng.hook; mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); //mods::hook::uninstall(); return MOD_OK; } }