From 39609bdac96678e5c6432377ae0cdc4136aa6e7b Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Mon, 16 Dec 2024 13:22:09 -0600 Subject: [PATCH] Various cleanup --- soh/soh/Enhancements/Holiday/AGreenSpoon.cpp | 1 + soh/soh/Enhancements/Holiday/Caladius.cpp | 127 +++++- soh/soh/Enhancements/Holiday/Fredomato.cpp | 374 +++++++++++++++++- soh/soh/Enhancements/Holiday/Fredomato.h | 18 + soh/soh/Enhancements/Holiday/Grimey.cpp | 30 +- soh/soh/Enhancements/Holiday/Holiday.hpp | 2 - soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp | 150 ------- soh/soh/Enhancements/Holiday/NotProxySaw.cpp | 1 + soh/soh/Enhancements/Holiday/Pablo.cpp | 6 +- soh/soh/Enhancements/Holiday/ProxySaw.cpp | 17 +- soh/soh/Enhancements/Holiday/lilDavid.cpp | 1 + .../Enhancements/TimeDisplay/TimeDisplay.cpp | 14 +- .../Enhancements/TimeDisplay/TimeDisplay.h | 3 +- .../game-interactor/GameInteractor.h | 1 + .../GameInteractor_HookTable.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 4 + .../game-interactor/GameInteractor_Hooks.h | 1 + .../Network/Anchor/Packets/SetCheckStatus.cpp | 8 +- .../Anchor/Packets/UpdateTeamState.cpp | 2 +- soh/soh/Notification/Notification.cpp | 2 +- soh/soh/SohMenuBar.cpp | 2 +- soh/src/code/z_player_lib.c | 1 + soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c | 4 +- .../actors/ovl_En_Wood02/z_en_wood02.c | 5 +- .../actors/ovl_player_actor/z_player.c | 15 + 25 files changed, 583 insertions(+), 207 deletions(-) create mode 100644 soh/soh/Enhancements/Holiday/Fredomato.h delete mode 100644 soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp diff --git a/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp b/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp index 2e745a9ee74..6885aa3457d 100644 --- a/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp +++ b/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp @@ -67,6 +67,7 @@ static void DrawMenu() { if (UIWidgets::EnhancementCheckbox("Evil Gossip Stone", CVAR("EvilGossipStone"))) { OnConfigurationChanged(); } + UIWidgets::Tooltip("Don't you dare talk to them."); } static void RegisterMod() { diff --git a/soh/soh/Enhancements/Holiday/Caladius.cpp b/soh/soh/Enhancements/Holiday/Caladius.cpp index 61d8a84d937..5eb632a5de1 100644 --- a/soh/soh/Enhancements/Holiday/Caladius.cpp +++ b/soh/soh/Enhancements/Holiday/Caladius.cpp @@ -5,6 +5,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/custom-message/CustomMessageManager.h" #include "soh/Enhancements/randomizer/randomizer.h" +#include "soh/frame_interpolation.h" +#include "soh_assets.h" extern "C" { #include "macros.h" @@ -80,9 +82,28 @@ void RandomizeBoulder(Actor* refActor) { Actor_Kill(actor); } -static void OnPresentChange() { - isExchangeDisabled = !CVarGetInteger(CVAR("OrnExch.Enabled"), 0); - COND_ID_HOOK(OnActorKill, ACTOR_EN_OE2, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](void* actorRef) { +bool spawningPresents = false; + +struct Present { +}; + +std::unordered_map presents; + +void Present_Init(Actor* actor, PlayState* play) { + Present present; + presents[actor] = present; + + actor->gravity = -1; + Actor_MoveXZGravity(actor); + actor->shape.rot.y = Random(0, 0xFFFF); + + Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF); +} + +void Present_Update(Actor* actor, PlayState* play) { + Present* present = &presents[actor]; + + if (actor->xzDistToPlayer < 50.0f && actor->yDistToPlayer < 50.0f) { uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0); giftsCollected++; CVarSetInteger(CVAR("GiftsCollected"), giftsCollected); @@ -91,9 +112,33 @@ static void OnPresentChange() { msg += " Gifts in Inventory."; Notification::Emit({ .itemIcon = "RG_TRIFORCE_PIECE", - .message = msg + .message = msg, + .messageColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f), }); - }); + Actor_Kill(actor); + } +} + +void Present_Draw(Actor* actor, PlayState* play) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + + Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY); + Matrix_Translate(49.20f, 0.0f, -106.60f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gXmasDecor100DL); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Present_Destroy(Actor* actor, PlayState* play) { + presents.erase(actor); +} + +static void OnPresentChange() { + isExchangeDisabled = !CVarGetInteger(CVAR("OrnExch.Enabled"), 0); COND_ID_HOOK(OnOpenText, 0x204A, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](u16 * textId, bool* loadFromMessageTable) { auto messageEntry = CustomMessage(""); bool reduceGifts = false; @@ -127,6 +172,48 @@ static void OnPresentChange() { }); } }); + + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), []() { + presents.clear(); + Vec3f pos; + static CollisionPoly presentPoly; + static f32 raycastResult; + pos.y = 9999.0f; + int spawnAttempts = 0; + while (spawnAttempts < 20) { + if (GET_PLAYER(gPlayState) != nullptr) { + pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x; + pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z; + } else { + pos.x = 0; + pos.z = 0; + } + // X/Z anywhere from -1000.0 to +1000.0 from player + pos.x += (float)(Random(0, 20000)) - 10000.0f; + pos.z += (float)(Random(0, 20000)) - 10000.0f; + + raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &presentPoly, &pos); + + if (raycastResult > BGCHECK_Y_MIN) { + spawningPresents = true; + Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, pos.x, raycastResult, pos.z, 0, 0, 0, 0, false); + spawningPresents = false; + // break; + } + + spawnAttempts++; + } + }); + + COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](void* actorRef, bool* should) { + Actor* actor = (Actor*)actorRef; + if (spawningPresents) { + actor->init = Present_Init; + actor->update = Present_Update; + actor->draw = Present_Draw; + actor->destroy = Present_Destroy; + } + }); } static void OnBlitzChange() { @@ -194,32 +281,32 @@ static void DrawMenu() { } UIWidgets::Tooltip("Can you beat your objective before the Fever sets in?/n" "- Obtaining Ice Traps extends your timer."); - if (UIWidgets::EnhancementSliderFloat("", "##FontScale", CVAR("FontScale"), - 1.0f, 5.0f, "Font: %.1fx", 1.0f, false, false, isFeverDisabled)) { - OnFeverConfigurationChanged(); + if (CVarGetInteger(CVAR("Fever.Enabled"), 0)) { + if (UIWidgets::EnhancementSliderFloat("", "##FontScale", CVAR("FontScale"), + 1.0f, 5.0f, "Font: %.1fx", 1.0f, false, false, isFeverDisabled)) { + OnFeverConfigurationChanged(); + } + UIWidgets::PaddedEnhancementSliderInt("Starting Timer: %d minutes", "##StartTime", CVAR("StartTimer"), + 5, 30, "", 15, true, true, false, isFeverDisabled); + UIWidgets::PaddedEnhancementSliderInt("Time Extensions: %d minutes", "##ExtendTime", CVAR("ExtendTimer"), + 1, 10, "", 5, true, true, false, isFeverDisabled); } - UIWidgets::PaddedEnhancementSliderInt("Starting Timer: %d minutes", "##StartTime", CVAR("StartTimer"), - 5, 30, "", 15, true, true, false, isFeverDisabled); - UIWidgets::PaddedEnhancementSliderInt("Time Extensions: %d minutes", "##ExtendTime", CVAR("ExtendTimer"), - 1, 10, "", 5, true, true, false, isFeverDisabled); UIWidgets::PaddedSeparator(); if (UIWidgets::EnhancementCheckbox("Boulder Blitz", CVAR("Blitz.Enabled"))) { OnBlitzChange(); } + UIWidgets::Tooltip("Boulders will randomly be replaced with other boulder types."); UIWidgets::PaddedSeparator(); if (UIWidgets::EnhancementCheckbox("Ornament Exchange", CVAR("OrnExch.Enabled"))) { OnPresentChange(); - bool toggle = CVarGetInteger(CVAR("OrnExch.Enabled"), 0); - CVarSetInteger("gHoliday.ItsHeckinPat.GiftsForNPCs", toggle); - OnConfigChanged(); } - UIWidgets::Tooltip("See Malon as Young Link in Lon Lon Ranch to exchange Gifts for Ornaments!\n" - "Note: Enabling this will set \"Gifts For NPCs\" to match."); - UIWidgets::PaddedEnhancementSliderInt("Gifts Required: %d Gifts", "##GiftsReq", CVAR("OrnExch.Amount"), - 5, 30, "", 15, true, true, false, isExchangeDisabled); - + UIWidgets::Tooltip("See Malon as Young Link in Lon Lon Ranch to exchange Gifts for Ornaments!"); + if (CVarGetInteger(CVAR("OrnExch.Enabled"), 0)) { + UIWidgets::PaddedEnhancementSliderInt("Gifts Required: %d Gifts", "##GiftsReq", CVAR("OrnExch.Amount"), + 5, 30, "", 15, true, true, false, isExchangeDisabled); + } } diff --git a/soh/soh/Enhancements/Holiday/Fredomato.cpp b/soh/soh/Enhancements/Holiday/Fredomato.cpp index bb01ea991a3..df33ccfe848 100644 --- a/soh/soh/Enhancements/Holiday/Fredomato.cpp +++ b/soh/soh/Enhancements/Holiday/Fredomato.cpp @@ -7,9 +7,15 @@ #include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/randomizer/3drando/location_access.hpp" #include "soh/Enhancements/randomizer/entrance.h" +#include "soh/Enhancements/custom-collectible/CustomCollectible.h" +#include "soh/Notification/Notification.h" +#include "soh/Enhancements/nametag.h" #include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_md/object_md.h" +#include "objects/object_trap/object_trap.h" +#include "objects/object_toryo/object_toryo.h" #include "src/overlays/actors/ovl_Door_Ana/z_door_ana.h" extern "C" { #include "macros.h" @@ -20,6 +26,7 @@ extern PlayState* gPlayState; void DoorAna_SetupAction(DoorAna* doorAna, DoorAnaActionFunc actionFunc); void DoorAna_GrabPlayer(DoorAna* doorAna, PlayState* play); } +extern GetItemEntry vanillaQueuedItemEntry; #define AUTHOR "Fredomato" #define CVAR(v) "gHoliday." AUTHOR "." v @@ -36,7 +43,7 @@ const s16 entrances[] = { 0x00B7, 0x0201, 0x003B, 0x0463, 0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394, 0x0272, 0x0211, 0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802, 0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809, 0x070A, 0x080A, - 0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812, + 0x070B, 0x080B, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812, 0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718, 0x0818, 0x0719, 0x0819, 0x081A, 0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F, 0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361, 0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286, 0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE, @@ -51,6 +58,15 @@ static bool midoGrottoInit = false; static SkelAnime midoSkelAnime; static Vec3s midoJointTable[17]; static Vec3s midoMorphTable[17]; +int FredsQuestWoodCollected = 0; +int FredsQuestWoodOnHand = 0; +static int lastDisplayedCount = -1; +static bool FredsQuestComplete = false; +static SkelAnime collectionPointSkelAnime; +static Vec3s collectionPointJointTable[17]; +static Vec3s collectionPointMorphTable[17]; +static std::string collectionPointNametag; + static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) { if (!midoGrottoInit) { @@ -61,9 +77,11 @@ static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) { Actor* actor = &doorAna->actor; Player* player = GET_PLAYER(play); - Math_SmoothStepToF(&actor->world.pos.x, player->actor.world.pos.x, 0.1f, 10.0f, 0.0f); - Math_SmoothStepToF(&actor->world.pos.z, player->actor.world.pos.z, 0.1f, 10.0f, 0.0f); - Math_SmoothStepToF(&actor->world.pos.y, player->actor.world.pos.y, 0.1f, 10.0f, 0.0f); + if (!Player_InCsMode(play)) { + Math_SmoothStepToF(&actor->world.pos.x, player->actor.world.pos.x, 0.1f, 10.0f, 0.0f); + Math_SmoothStepToF(&actor->world.pos.z, player->actor.world.pos.z, 0.1f, 10.0f, 0.0f); + Math_SmoothStepToF(&actor->world.pos.y, player->actor.world.pos.y, 0.1f, 10.0f, 0.0f); + } Math_ApproachS(&doorAna->actor.shape.rot.y, doorAna->actor.yawTowardsPlayer, 5, 0xBB8); @@ -103,11 +121,10 @@ static void RandomGrotto_Draw(Actor* actor, PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } -static void SpawnRandomGrotto() { +static Vec3f FindValidPos(f32 distance) { Vec3f pos; pos.y = 9999.0f; - int spawnAttempts = 0; - while (spawnAttempts < 50) { + while (true) { if (GET_PLAYER(gPlayState) != nullptr) { pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x; pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z; @@ -115,34 +132,355 @@ static void SpawnRandomGrotto() { pos.x = 0; pos.z = 0; } - // X/Z anywhere from -1000.0 to +1000.0 from player - pos.x += (float)(Random(0, 2000)) - 1000.0f; - pos.z += (float)(Random(0, 2000)) - 1000.0f; + pos.x += (float)(Random(0, distance)) - distance / 2; + pos.z += (float)(Random(0, distance)) - distance / 2; raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &pos); if (raycastResult > BGCHECK_Y_MIN) { - Actor* grotto = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_ANA, pos.x, raycastResult, pos.z, 0, 0, 0, 0, false); - midoGrottoInit = false; - DoorAna_SetupAction((DoorAna*)grotto, RandomGrotto_WaitOpen); - grotto->draw = RandomGrotto_Draw; - break; + pos.y = raycastResult; + return pos; + } + } +} + +// TODO: If in hyrule field and treeChopper is on, teleport somewhere else in hyrule field +static void SpawnRandomGrotto() { + if ( + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY || + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT || + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS + ) { + return; + } + + Vec3f pos = FindValidPos(2000.0f); + Actor* grotto = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_ANA, pos.x, pos.y, pos.z, 0, 0, 0, 0, false); + midoGrottoInit = false; + DoorAna_SetupAction((DoorAna*)grotto, RandomGrotto_WaitOpen); + grotto->draw = RandomGrotto_Draw; +} + +void SpawnStick(Vec3f pos) { + CustomCollectible::Spawn(pos.x, pos.y + 150.0f, pos.z, 0, CustomCollectible::KILL_ON_TOUCH | CustomCollectible::TOSS_ON_SPAWN, 0, [](Actor* actor, PlayState* play) { + FredsQuestWoodOnHand++; + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + }, [](Actor* actor, PlayState* play) { + Matrix_Scale(40.0f, 40.0f, 40.0f, MTXMODE_APPLY); + for (int i = 4; i < 7; i++) { + Matrix_RotateZYX(800 * i, 0, 800 * i, MTXMODE_APPLY); + GetItem_Draw(play, GID_STICK); + } + }); +} + +Actor* specialTree = nullptr; + +void ChooseSpecialTree() { + Actor* actor = gPlayState->actorCtx.actorLists[ACTORCAT_PROP].head; + std::vector trees; + + specialTree = nullptr; + + while (actor != NULL) { + if (ACTOR_EN_WOOD02 == actor->id && actor->params < 10) { + trees.push_back(actor); } + actor = actor->next; + } - spawnAttempts++; + if (trees.size() <= 1) { + return; } + + specialTree = trees[Random(0, trees.size() - 1)]; +} + +extern "C" bool HandleTreeBonk(Actor* actor) { + if (!CVarGetInteger(CVAR("FredsQuest.Enabled"), 0)) { + return false; + } + + int damage = 2; + // random chance of doing a crit + if (Random(0, 100) < 30) { + damage = 4; + } + + if (actor->colChkInfo.health - damage <= 0) { + if (specialTree == actor) { + ChooseSpecialTree(); + + for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.SpecialBreakDropRate"), 10); i++) { + SpawnStick(actor->world.pos); + } + } else { + for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.TreeBreakDropRate"), 3); i++) { + SpawnStick(actor->world.pos); + } + } + + // Move tree (instead of killing and spawning another) + actor->colChkInfo.health = 8; + Vec3f pos = FindValidPos(5000.0f); + actor->world.pos.x = pos.x; + actor->world.pos.y = pos.y; + actor->world.pos.z = pos.z; + } else { + actor->colChkInfo.health -= damage; + for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.TreeBonkDropRate"), 1); i++) { + SpawnStick(actor->world.pos); + } + } + + return true; +} + +void DrawCrazyTaxiArrow(Actor* actor, PlayState* play) { + if (specialTree == nullptr || !CVarGetInteger(CVAR("FredsQuest.CrazyTaxiArrow"), 0)) { + return; + } + + s16 yaw = Actor_WorldYawTowardActor(actor, specialTree); + Math_ApproachS(&actor->shape.rot.y, yaw, 5, 10000); + + OPEN_DISPS(gPlayState->state.gfxCtx); + + Gfx_SetupDL_4Xlu(gPlayState->state.gfxCtx); + + Matrix_Scale(50.0f, 50.0f, 50.0f, MTXMODE_APPLY); + Matrix_Translate(0.0f, 70.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateY(5.86f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 255, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 0, 255); + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gDebugArrowDL); + + CLOSE_DISPS(gPlayState->state.gfxCtx); +} + +void SpawnCrazyTaxiArrow() { + EnItem00* arrow = CustomCollectible::Spawn(0, 0, 0, 0, CustomCollectible::KEEP_ON_PLAYER, 0, NULL, NULL); + arrow->actor.draw = DrawCrazyTaxiArrow; +} + +void CollectionPoint_Update(Actor* actor, PlayState* play) { + EnItem00* enItem00 = (EnItem00*)actor; + + SkelAnime_Update(&collectionPointSkelAnime); + + if (FredsQuestComplete) { + return; + } + + if (lastDisplayedCount != FredsQuestWoodCollected) { + lastDisplayedCount = FredsQuestWoodCollected; + collectionPointNametag = "Bring me wood!"; + if (FredsQuestWoodCollected > 0) { + collectionPointNametag += std::string(" (") + std::to_string(FredsQuestWoodCollected) + "/" + std::to_string(CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300)) + ")"; + } + NameTag_RemoveAllForActor(actor); + NameTag_RegisterForActorWithOptions(actor, collectionPointNametag.c_str(), { .yOffset = 100 }); + } + + if ((actor->xzDistToPlayer <= 200.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(50.0f))) { + if (FredsQuestWoodOnHand) { + FredsQuestWoodCollected++; + FredsQuestWoodOnHand--; + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + + if (FredsQuestWoodCollected >= CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300)) { + FredsQuestComplete = true; + collectionPointNametag = "You're a hero!"; + NameTag_RemoveAllForActor(actor); + NameTag_RegisterForActorWithOptions(actor, collectionPointNametag.c_str(), { .yOffset = 100 }); + + if (IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_TRIFORCE_HUNT)) { + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy(); + } else { + vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_HEART_CONTAINER).GetGIEntry_Copy(); + } + + } + } + } +} + +void CollectionPoint_Draw(Actor* actor, PlayState* play) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + SkelAnime_DrawSkeletonOpa(play, &collectionPointSkelAnime, NULL, NULL, actor); + + // For every 2% of the goal, draw a stick at a different angle, building a tree + Matrix_Scale(40.0f, 40.0f, 40.0f, MTXMODE_APPLY); + Matrix_Translate(0, 0, -300.0f, MTXMODE_APPLY); + for (int i = 0; i < FredsQuestWoodCollected / (CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300) / 50); i++) { + float angle = 10 * i; + float radius = (50 - i) * 0.5f; // Radius decreases as it goes up + float height = 10.0f; // Incremental height + + Matrix_Translate(radius * cosf(angle), height, radius * sinf(angle), MTXMODE_APPLY); + Matrix_RotateY(angle, MTXMODE_APPLY); + GetItem_Draw(play, GID_STICK); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +void SpawnCollectionPoint() { + EnItem00* collectionPoint = CustomCollectible::Spawn(859.0f, 347.0f, 5185.0f, 0xB000, 0, 0, NULL, NULL); + collectionPoint->actor.update = CollectionPoint_Update; + collectionPoint->actor.draw = CollectionPoint_Draw; + collectionPoint->actor.flags |= ACTOR_FLAG_DRAW_WHILE_CULLED; + SkelAnime_InitFlex(gPlayState, &collectionPointSkelAnime, (FlexSkeletonHeader*)&object_toryo_Skel_007150, + (AnimationHeader*)&object_toryo_Anim_000E50, collectionPointJointTable, collectionPointMorphTable, 17); +} + +void RandomTrap_Update(Actor* actor, PlayState* play) { + EnItem00* enItem00 = (EnItem00*)actor; + + enItem00->unk_158--; + if (enItem00->unk_158 == 0) { + Actor_Kill(actor); + return; + } + + Math_ApproachS(&actor->world.rot.y, actor->yawTowardsPlayer, 5, 0xBB8); + actor->speedXZ = 3.0f; + + // TODO: CVar for speed + // Multiply speed by distance + actor->speedXZ += actor->xzDistToPlayer * 0.01f; + if (actor->xzDistToPlayer > 1000.0f && actor->velocity.y < -3.0f) { + actor->velocity.y += ABS(actor->yDistToPlayer) * 0.01f; + } + + actor->shape.rot.y += 0x1000; + + if ((actor->xzDistToPlayer <= 50.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) { + // TODO: Random crowd control effect + GameInteractor::RawAction::KnockbackPlayer(5.0f); + Actor_Kill(actor); + } + + if (actor->gravity != 0.0f) { + Actor_MoveXZGravity(actor); + Actor_UpdateBgCheckInfo(play, actor, 20.0f, 15.0f, 15.0f, 0x1D); + } + + if (actor->bgCheckFlags & 0x0003) { + actor->speedXZ = 0.0f; + } +} + +void RandomTrap_Draw(Actor* actor, PlayState* play) { + OPEN_DISPS(play->state.gfxCtx); + + Matrix_Scale(4.0f, 4.0f, 4.0f, MTXMODE_APPLY); + Matrix_Translate(0, -200.0f, 0, MTXMODE_APPLY); + func_8002EBCC(actor, play, 1); + Gfx_DrawDListOpa(play, (Gfx*)gSlidingBladeTrapDL); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void SpawnRandomTrap() { + Vec3f pos = FindValidPos(2000.0f); + EnItem00* randomTrap = CustomCollectible::Spawn(pos.x, pos.y, pos.z, 0, CustomCollectible::TOSS_ON_SPAWN, 0, NULL, NULL); + SoundSource_PlaySfxAtFixedWorldPos(gPlayState, &randomTrap->actor.world.pos, 20, NA_SE_EV_LIGHTNING); + randomTrap->actor.update = RandomTrap_Update; + randomTrap->actor.draw = RandomTrap_Draw; + randomTrap->unk_158 = 20 * CVarGetInteger(CVAR("RandomTraps.Lifetime"), 30); +} + +void OnSceneInit() { + // Reset wood collected + FredsQuestWoodCollected = 0; + FredsQuestWoodOnHand = 0; + lastDisplayedCount = -1; + FredsQuestComplete = false; + + if (gPlayState->sceneNum != SCENE_HYRULE_FIELD) { + return; + } + + ChooseSpecialTree(); + SpawnCrazyTaxiArrow(); + SpawnCollectionPoint(); } static void ConfigurationChanged() { - COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("KrampusHole"), 0), SpawnRandomGrotto); + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), OnSceneInit); + + COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("RandomTraps.Enabled"), 0), []() { + if (rand() % CVarGetInteger(CVAR("RandomTraps.SpawnChance"), 400) == 0) { + SpawnRandomTrap(); + } + }); + + COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), []() { + if (CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 60) == 0 || FredsQuestWoodOnHand <= CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 60)) { + GameInteractor::State::RunSpeedModifier = 0; + } else { + GameInteractor::State::RunSpeedModifier = -2; + } + }); + + COND_VB_SHOULD(VB_PLAYER_ROLL, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), { + if (FredsQuestWoodOnHand > CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 0)) { + *should = false; + } + }); } static void DrawMenu() { ImGui::SeparatorText(AUTHOR); - if (UIWidgets::EnhancementCheckbox("The Krampus Hole", CVAR("KrampusHole"))) { + // UIWidgets::EnhancementSliderFloat("Xfloat", "Xfloat", CVAR("tmpxf"), 0.0f, 10.0f, "%.2f", 1.0f, false); + // UIWidgets::EnhancementSliderFloat("Yfloat", "Yfloat", CVAR("tmpyf"), 0.0f, 10.0f, "%.2f", 1.0f, false); + // UIWidgets::EnhancementSliderFloat("Zfloat", "Zfloat", CVAR("tmpzf"), 0.0f, 10.0f, "%.2f", 1.0f, false); + // UIWidgets::EnhancementSliderInt("Xs", "Xs", CVAR("tmpxs"), 0, UINT16_MAX, "%d", 1, false); + // UIWidgets::EnhancementSliderInt("Ys", "Ys", CVAR("tmpys"), 0, UINT16_MAX, "%d", 1, false); + // UIWidgets::EnhancementSliderInt("Zs", "Zs", CVAR("tmpzs"), 0, UINT16_MAX, "%d", 1, false); + if (UIWidgets::EnhancementCheckbox("Fred's Quest", CVAR("FredsQuest.Enabled"))) { ConfigurationChanged(); } + UIWidgets::Tooltip("Collect wood and bring it to the collection point in Hyrule Field for a small reward."); + if (CVarGetInteger(CVAR("FredsQuest.Enabled"), 0)) { + if (UIWidgets::EnhancementCheckbox("Crazy Taxi Arrow", CVAR("FredsQuest.CrazyTaxiArrow"))) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Wood Needed", "##FredsQuest.WoodNeeded", CVAR("FredsQuest.WoodNeeded"), 0, 1000, "%d", 300, false)) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Tree Bonk Drop Rate", "##FredsQuest.TreeBonkDropRate", CVAR("FredsQuest.TreeBonkDropRate"), 0, 10, "%d", 1, false)) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Tree Break Drop Rate", "##FredsQuest.TreeBreakDropRate", CVAR("FredsQuest.TreeBreakDropRate"), 0, 50, "%d", 3, false)) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Special Break Drop Rate", "##FredsQuest.SpecialBreakDropRate", CVAR("FredsQuest.SpecialBreakDropRate"), 0, 50, "%d", 10, false)) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Encumbered Threshold", "##FredsQuest.EncumberedThreshold", CVAR("FredsQuest.EncumberedThreshold"), 0, 200, "%d", 60, false)) { + ConfigurationChanged(); + } + UIWidgets::Tooltip("If you have more than this many sticks, you will be encumbered and run slower. 0 for disabled"); + } + if (UIWidgets::EnhancementCheckbox("Random Traps", CVAR("RandomTraps.Enabled"))) { + ConfigurationChanged(); + } + UIWidgets::Tooltip("Random traps will spawn around you at a configurable rate. (Currently only knockback)"); + if (CVarGetInteger(CVAR("RandomTraps.Enabled"), 0)) { + if (UIWidgets::EnhancementSliderInt("Trap Lifetime (Seconds)", "##RandomTraps.Lifetime", CVAR("RandomTraps.Lifetime"), 0, 60, "%d", 30, false)) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementSliderInt("Spawn Chance", "##RandomTraps.SpawnChance", CVAR("RandomTraps.SpawnChance"), 40, 2000, "%d", 1000, false)) { + ConfigurationChanged(); + } + } } static void RegisterMod() { diff --git a/soh/soh/Enhancements/Holiday/Fredomato.h b/soh/soh/Enhancements/Holiday/Fredomato.h new file mode 100644 index 00000000000..3479b3f1ecc --- /dev/null +++ b/soh/soh/Enhancements/Holiday/Fredomato.h @@ -0,0 +1,18 @@ +#ifndef FRED_H +#define FRED_H + +#ifdef __cplusplus +extern int FredsQuestWoodCollected; +extern int FredsQuestWoodOnHand; + +extern "C" { +#include "z64actor.h" +#endif + +bool HandleTreeBonk(Actor* actor); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/soh/Enhancements/Holiday/Grimey.cpp b/soh/soh/Enhancements/Holiday/Grimey.cpp index 3bc5733b93c..094a355c2f4 100644 --- a/soh/soh/Enhancements/Holiday/Grimey.cpp +++ b/soh/soh/Enhancements/Holiday/Grimey.cpp @@ -102,13 +102,33 @@ void Penguin_Destroy(Actor* actor, PlayState* play) { static void OnConfigurationChanged() { COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("Hailstorm"), 0), []() { - // Every frame has a 1/300 chance of spawning hail - if (rand() % 300 == 0) { + // Every frame has a 1/500 chance of spawning close hail + if (rand() % 500 == 0) { int spawned = 0; while (spawned < 1) { Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos; - pos.x += (float)Random(0, 100) - 50.0f; - pos.z += (float)Random(0, 100) - 50.0f; + pos.x += (float)Random(0, 50) - 25.0f; + pos.z += (float)Random(0, 50) - 25.0f; + pos.y += 200.0f; + + Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0, 0, 0, 0, false); + EnNutsball* nut = (EnNutsball*)actor; + nut->actor.draw = EnNutsball_Draw; + nut->actor.shape.rot.y = 0; + nut->timer = 0; + nut->actionFunc = func_80ABBBA8; + nut->actor.speedXZ = 0.0f; + nut->actor.gravity = -2.0f; + spawned++; + } + } + // Every frame has a 1/50 chance of spawning far hail + if (rand() % 50 == 0) { + int spawned = 0; + while (spawned < 1) { + Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos; + pos.x += (float)Random(0, 500) - 250.0f; + pos.z += (float)Random(0, 500) - 250.0f; pos.y += 200.0f; Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0, 0, 0, 0, false); @@ -191,9 +211,11 @@ static void DrawMenu() { if (UIWidgets::EnhancementCheckbox("Penguins", CVAR("Penguins"))) { OnConfigurationChanged(); } + UIWidgets::Tooltip("Penguins will spawn in huddles throughout hyrule"); if (UIWidgets::EnhancementCheckbox("Hailstorm", CVAR("Hailstorm"))) { OnConfigurationChanged(); } + UIWidgets::Tooltip("Ever persistent hailstorm throughout hyrule"); } static void RegisterMod() { diff --git a/soh/soh/Enhancements/Holiday/Holiday.hpp b/soh/soh/Enhancements/Holiday/Holiday.hpp index e16bd005a94..9a671706068 100644 --- a/soh/soh/Enhancements/Holiday/Holiday.hpp +++ b/soh/soh/Enhancements/Holiday/Holiday.hpp @@ -8,8 +8,6 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h" -void OnConfigChanged(); - inline std::vector> holidayDrawFuncs = {}; inline std::vector> holidayRegisterFuncs = {}; diff --git a/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp b/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp deleted file mode 100644 index b2c8cc8ee66..00000000000 --- a/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "Holiday.hpp" -#include "soh_assets.h" -#include "soh/Enhancements/randomizer/3drando/random.hpp" -#include "soh/frame_interpolation.h" -#include "soh/Notification/Notification.h" -#include "objects/gameplay_field_keep/gameplay_field_keep.h" -#include "soh/Enhancements/custom-message/CustomMessageManager.h" -#include "soh/util.h" -#include "soh/Enhancements/randomizer/randomizer.h" - -extern "C" { -#include "macros.h" -#include "functions.h" -#include "variables.h" -extern PlayState* gPlayState; -} -extern GetItemEntry vanillaQueuedItemEntry; - -#define AUTHOR "ItsHeckinPat" -#define CVAR(v) "gHoliday." AUTHOR "." v - -bool spawningPresents = false; - -int collectedPresent = 0; - -struct Present { -}; - -std::unordered_map presents; - -void Present_Init(Actor* actor, PlayState* play) { - Present present; - presents[actor] = present; - - actor->gravity = -1; - Actor_MoveXZGravity(actor); - actor->shape.rot.y = Random(0, 0xFFFF); - - Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF); -} - -void Present_Update(Actor* actor, PlayState* play) { - Present* present = &presents[actor]; - - if (actor->xzDistToPlayer < 50.0f && actor->yDistToPlayer < 50.0f) { - collectedPresent++; - Notification::Emit({ - .itemIcon = "RG_TRIFORCE_PIECE", - .message = "You collected a present!", - .messageColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f), - }); - Actor_Kill(actor); - } -} - -void Present_Draw(Actor* actor, PlayState* play) { - OPEN_DISPS(play->state.gfxCtx); - - Gfx_SetupDL_25Opa(play->state.gfxCtx); - - Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY); - Matrix_Translate(49.20f, 0.0f, -106.60f, MTXMODE_APPLY); - gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); - gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); - gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gXmasDecor100DL); - - CLOSE_DISPS(play->state.gfxCtx); -} - -void Present_Destroy(Actor* actor, PlayState* play) { - presents.erase(actor); -} - -static CollisionPoly presentPoly; -static f32 raycastResult; - -void OnConfigChanged() { - COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("GiftsForNPCs"), 0), []() { - presents.clear(); - Vec3f pos; - pos.y = 9999.0f; - int spawnAttempts = 0; - while (spawnAttempts < 50) { - if (GET_PLAYER(gPlayState) != nullptr) { - pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x; - pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z; - } else { - pos.x = 0; - pos.z = 0; - } - // X/Z anywhere from -1000.0 to +1000.0 from player - pos.x += (float)(Random(0, 2000)) - 1000.0f; - pos.z += (float)(Random(0, 2000)) - 1000.0f; - - raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &presentPoly, &pos); - - if (raycastResult > BGCHECK_Y_MIN) { - spawningPresents = true; - Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, pos.x, raycastResult, pos.z, 0, 0, 0, 0, false); - spawningPresents = false; - // break; - } - - spawnAttempts++; - } - }); - - COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("GiftsForNPCs"), 0), [](void* actorRef, bool* should) { - Actor* actor = (Actor*)actorRef; - if (spawningPresents) { - actor->init = Present_Init; - actor->update = Present_Update; - actor->draw = Present_Draw; - actor->destroy = Present_Destroy; - } - }); - - COND_ID_HOOK(OnOpenText, 0x1019, CVarGetInteger(CVAR("GiftsForNPCs"), 0), [](u16 * textId, bool* loadFromMessageTable) { - if (collectedPresent <= 0) { - return; - } - - auto messageEntry = CustomMessage("A present??? FOR ME???"); - messageEntry.Format(); - messageEntry.LoadIntoFont(); - *loadFromMessageTable = false; - - vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PIECE_OF_HEART).GetGIEntry_Copy(); - - collectedPresent--; - }); -} - -static void DrawMenu() { - ImGui::SeparatorText(AUTHOR); - if (UIWidgets::EnhancementCheckbox("Gifts for NPCs", CVAR("GiftsForNPCs"))) { - OnConfigChanged(); - } -} - -static void RegisterMod() { - // #region Leave this alone unless you know what you are doing - OnConfigChanged(); - // #endregion - - // TODO: Anything you want to run once on startup -} - -// TODO: Uncomment this line to enable the mod -static Holiday holiday(DrawMenu, RegisterMod); diff --git a/soh/soh/Enhancements/Holiday/NotProxySaw.cpp b/soh/soh/Enhancements/Holiday/NotProxySaw.cpp index b6cf850916d..fe099546729 100644 --- a/soh/soh/Enhancements/Holiday/NotProxySaw.cpp +++ b/soh/soh/Enhancements/Holiday/NotProxySaw.cpp @@ -135,6 +135,7 @@ static void DrawMenu() { if (UIWidgets::EnhancementCheckbox("Ganon Dating Sim", CVAR("GanonDatingSim"))) { ConfigurationChanged(); } + UIWidgets::Tooltip("Prior to fighting him at the top of his Castle, you make an attempt to convince Ganon to join you instead."); } static void RegisterMod() { diff --git a/soh/soh/Enhancements/Holiday/Pablo.cpp b/soh/soh/Enhancements/Holiday/Pablo.cpp index 08173df86b2..0299964d79c 100644 --- a/soh/soh/Enhancements/Holiday/Pablo.cpp +++ b/soh/soh/Enhancements/Holiday/Pablo.cpp @@ -181,8 +181,10 @@ void ShinyDrawImGui() { UIWidgets::PaddedEnhancementCheckbox("Enable Shiny Enemies", CVAR("Shiny.Enabled"), true, false); UIWidgets::Tooltip("Allows enemies to be shiny.\nShiny enemies are 25% bigger and have 4 times the health but drop the equivalent of a gold rupee upon death"); - UIWidgets::PaddedEnhancementSliderInt("Shiny Chance: %d", "##ShinyChance", CVAR("Shiny.Chance"), 1, 8192, "", 8192, true, true, false, false, ""); - UIWidgets::Tooltip("The chance for an enemy to be shiny is 1 / Shiny Chance"); + if (CVarGetInteger(CVAR("Shiny.Enabled"), 0)) { + UIWidgets::PaddedEnhancementSliderInt("Shiny Chance: %d", "##ShinyChance", CVAR("Shiny.Chance"), 1, 8192, "", 8192, true, true, false, false, ""); + UIWidgets::Tooltip("The chance for an enemy to be shiny is 1 / Shiny Chance"); + } } #pragma endregion diff --git a/soh/soh/Enhancements/Holiday/ProxySaw.cpp b/soh/soh/Enhancements/Holiday/ProxySaw.cpp index 988ae28a95a..a4c9fe12293 100644 --- a/soh/soh/Enhancements/Holiday/ProxySaw.cpp +++ b/soh/soh/Enhancements/Holiday/ProxySaw.cpp @@ -125,7 +125,7 @@ const s16 entrances[] = { 0x00B7, 0x0201, 0x003B, 0x0463, 0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394, 0x0272, 0x0211, 0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802, 0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809, 0x070A, 0x080A, - 0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812, + 0x070B, 0x080B, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812, 0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718, 0x0818, 0x0719, 0x0819, 0x081A, 0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F, 0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361, 0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286, 0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE, @@ -158,6 +158,14 @@ static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) { } static void SpawnRandomGrotto() { + if ( + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY || + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT || + gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS + ) { + return; + } + Vec3f pos; pos.y = 9999.0f; int spawnAttempts = 0; @@ -170,8 +178,8 @@ static void SpawnRandomGrotto() { pos.z = 0; } // X/Z anywhere from -1000.0 to +1000.0 from player - pos.x += (float)(Random(0, 2000)) - 1000.0f; - pos.z += (float)(Random(0, 2000)) - 1000.0f; + pos.x += (float)(Random(0, 5000)) - 2500.0f; + pos.z += (float)(Random(0, 5000)) - 2500.0f; raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &pos); @@ -211,12 +219,15 @@ static void DrawMenu() { if (UIWidgets::EnhancementCheckbox("Snowballs", CVAR("Snowballs"))) { ConfigurationChanged(); } + UIWidgets::Tooltip("Rogue snowballs will spawn in Hyrule Field and Kakariko Village."); if (UIWidgets::EnhancementCheckbox("Lake Hylia Icebergs", CVAR("Icebergs"))) { ConfigurationChanged(); } + UIWidgets::Tooltip("Icebergs will spawn in Lake Hylia."); if (UIWidgets::EnhancementCheckbox("Down the Rabbit Hole", CVAR("DownTheRabbitHole"))) { ConfigurationChanged(); } + UIWidgets::Tooltip("Random grottos will spawn throughout Hyrule. Who knows where they will take you?"); if (UIWidgets::EnhancementCheckbox("Super Bonk", CVAR("SuperBonk"))) { ConfigurationChanged(); } diff --git a/soh/soh/Enhancements/Holiday/lilDavid.cpp b/soh/soh/Enhancements/Holiday/lilDavid.cpp index e96c1def018..33d8b2847ef 100644 --- a/soh/soh/Enhancements/Holiday/lilDavid.cpp +++ b/soh/soh/Enhancements/Holiday/lilDavid.cpp @@ -123,6 +123,7 @@ static void DrawMenu() { if (UIWidgets::EnhancementCheckbox("Bomb Arrows", CVAR("BombArrows.Enabled"))) { OnConfigurationChanged(); } + UIWidgets::Tooltip("Equip bombs over an already equipped Bow to shoot bomb arrows"); } static void RegisterMod() { diff --git a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp index cb9dfbb46db..0247e98dd32 100644 --- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp +++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp @@ -1,5 +1,6 @@ #include "TimeDisplay.h" #include "soh/Enhancements/gameplaystats.h" +#include "soh/Enhancements/Holiday/Fredomato.h" #include #include "assets/textures/parameter_static/parameter_static.h" @@ -40,7 +41,8 @@ const std::vector timeDisplayList = { { DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.InGameTimer") }, { DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_ENHANCEMENT("TimeDisplay.Timers.TimeofDay") }, { DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.HotWater") }, - { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.NaviTimer") } + { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.NaviTimer") }, + { DISPLAY_FRED_QUEST, "Display Fred's Quest", CVAR_ENHANCEMENT("TimeDisplay.Timers.FredsQuest") } }; static std::vector activeTimers; @@ -134,6 +136,10 @@ static void TimeDisplayGetTimer(uint32_t timeID) { } textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("NAVI_TIMER"); break; + case DISPLAY_FRED_QUEST: + timeDisplayTime = std::to_string(FredsQuestWoodOnHand) + "/" + std::to_string(FredsQuestWoodCollected) + "/" + + std::to_string(CVarGetInteger("gHoliday.Fredomato.FredsQuest.WoodNeeded", 300)); + textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ITEM_STICK"); default: break; } @@ -189,6 +195,12 @@ void TimeDisplayWindow::Draw() { ImGui::Image(textureDisplay, ImVec2(16.0f * fontScale, 16.0f * fontScale)); ImGui::TableNextColumn(); + if (timers.timeID == DISPLAY_FRED_QUEST) { + ImGui::Text("%s", timeDisplayTime.c_str()); + ImGui::PopID(); + continue; + } + if (timeDisplayTime != "-:--") { char* textToDecode = new char[timeDisplayTime.size() + 1]; textToDecode = std::strcpy(textToDecode, timeDisplayTime.c_str()); diff --git a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h index 090ac2901fa..3002ac8a427 100644 --- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h +++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h @@ -17,7 +17,8 @@ typedef enum { DISPLAY_IN_GAME_TIMER, DISPLAY_TIME_OF_DAY, DISPLAY_CONDITIONAL_TIMER, - DISPLAY_NAVI_TIMER + DISPLAY_NAVI_TIMER, + DISPLAY_FRED_QUEST, }; typedef enum { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 0e24e0ae5e7..333f6cc8979 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -371,6 +371,7 @@ typedef enum { // Vanilla condition: !Flags_GetInfTable(INFTABLE_145) // Opt: *EnRu1 VB_RUTO_BE_CONSIDERED_NOT_KIDNAPPED, + VB_PLAYER_ROLL, /*** Give Items ***/ diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 59f8cb11eab..5754df7f37c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -31,6 +31,7 @@ DEFINE_HOOK(OnEnemyDefeat, (void* actor)); DEFINE_HOOK(OnBossDefeat, (void* actor)); DEFINE_HOOK(OnTimestamp, (u8 item)); DEFINE_HOOK(OnPlayerBonk, ()); +DEFINE_HOOK(OnPlayerRoll, ()); DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount)); DEFINE_HOOK(OnPlayerBottleUpdate, (int16_t contents)); DEFINE_HOOK(OnPlayDestroy, ()); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 2ccd8e100bb..2ea19806c37 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -139,6 +139,10 @@ void GameInteractor_ExecuteOnPlayerBonk() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnPlayerRoll() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount) { GameInteractor::Instance->ExecuteHooks(amount); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index fd5d746f82e..9a2703f81ed 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -29,6 +29,7 @@ void GameInteractor_ExecuteOnEnemyDefeat(void* actor); void GameInteractor_ExecuteOnBossDefeat(void* actor); void GameInteractor_ExecuteOnTimestamp (u8 item); void GameInteractor_ExecuteOnPlayerBonk(); +void GameInteractor_ExecuteOnPlayerRoll(); void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount); void GameInteractor_ExecuteOnPlayerBottleUpdate(int16_t contents); void GameInteractor_ExecuteOnOcarinaSongAction(); diff --git a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp index 276e5258662..802b33a693f 100644 --- a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp +++ b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp @@ -6,6 +6,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/OTRGlobals.h" +static bool isResultOfHandling = false; + /** * SET_CHECK_STATUS * @@ -13,7 +15,7 @@ */ void Anchor::SendPacket_SetCheckStatus(RandomizerCheck rc) { - if (!IsSaveLoaded()) { + if (!IsSaveLoaded() || isResultOfHandling) { return; } @@ -41,6 +43,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) { RandomizerCheck rc = payload["rc"].get(); RandomizerCheckStatus status = payload["status"].get(); bool skipped = payload["skipped"].get(); + + isResultOfHandling = true; if (randoContext->GetItemLocation(rc)->GetCheckStatus() != status) { randoContext->GetItemLocation(rc)->SetCheckStatus(status); @@ -48,6 +52,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) { if (randoContext->GetItemLocation(rc)->GetIsSkipped() != skipped) { randoContext->GetItemLocation(rc)->SetIsSkipped(skipped); } + + isResultOfHandling = false; } #endif // ENABLE_REMOTE_CONTROL diff --git a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp index cbc9088eb53..fb937b4d11e 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp @@ -151,7 +151,7 @@ void Anchor::HandlePacket_UpdateTeamState(nlohmann::json payload) { gSaveContext.infTable[i] = loadedData.infTable[i]; } - for (int i = 0; i < 17; i++) { + for (int i = 0; i < 52; i++) { gSaveContext.randomizerInf[i] = loadedData.randomizerInf[i]; } diff --git a/soh/soh/Notification/Notification.cpp b/soh/soh/Notification/Notification.cpp index ebc4e168b35..5bc0e89740d 100644 --- a/soh/soh/Notification/Notification.cpp +++ b/soh/soh/Notification/Notification.cpp @@ -20,7 +20,7 @@ void Window::Draw() { const float margin = 30.0f; const float padding = 10.0f; - int position = CVarGetInteger(CVAR_SETTING("Notifications.Position"), 0); + int position = CVarGetInteger(CVAR_SETTING("Notifications.Position"), 3); // Top Left ImVec2 basePosition; diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 6d504afe58a..8e36abe6c00 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -586,7 +586,7 @@ void DrawSettingsMenu() { }; ImGui::Text("Position"); - UIWidgets::EnhancementCombobox(CVAR_SETTING("Notifications.Position"), notificationPosition, 0); + UIWidgets::EnhancementCombobox(CVAR_SETTING("Notifications.Position"), notificationPosition, 3); UIWidgets::EnhancementSliderFloat("Duration: %.1f seconds", "##NotificationDuration", CVAR_SETTING("Notifications.Duration"), 3.0f, 30.0f, "", 10.0f, false, true, false); UIWidgets::EnhancementSliderFloat("BG Opacity: %.1f %%", "##NotificaitonBgOpacity", CVAR_SETTING("Notifications.BgOpacity"), 0.0f, 1.0f, "", 0.5f, true, true, false); UIWidgets::EnhancementSliderFloat("Size: %.1f", "##NotificaitonSize", CVAR_SETTING("Notifications.Size"), 1.0f, 20.0f, "", 1.8f, false, true, false); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 576bf1eaf62..e662f8c4613 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -8,6 +8,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/randomizer/draw.h" +#include "soh/Enhancements/Holiday/Fredomato.h" #include "soh/ResourceManagerHelpers.h" #include diff --git a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c index 514e84bd328..084fbdf5cab 100644 --- a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c +++ b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c @@ -543,7 +543,9 @@ u16 func_80A6F810(PlayState* play, Actor* thisx) { return 0x5058; } case ENHY_TYPE_BOB_18: - if (!LINK_IS_ADULT) { + if (CVarGetInteger("gHoliday.Fredomato.TreeChopper", 0)) { + return 0x505e; + } else if (!LINK_IS_ADULT) { return (Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) ? 0x505F : ((Flags_GetInfTable(INFTABLE_163)) ? 0x505E : 0x505D); } else { return (this->unk_330 & 0x800) ? 0x5062 : ((Flags_GetInfTable(INFTABLE_164)) ? 0x5061 : 0x5060); diff --git a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c index 7ecb21288b6..a185540ab19 100644 --- a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c +++ b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c @@ -6,6 +6,7 @@ #include "z_en_wood02.h" #include "objects/object_wood02/object_wood02.h" +#include "soh/Enhancements/Holiday/Fredomato.h" #define FLAGS 0 @@ -368,7 +369,9 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) { dropsSpawnPt = this->actor.world.pos; dropsSpawnPt.y += 200.0f; - if ((this->unk_14C >= 0) && (this->unk_14C < 0x64) && (CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0)) && !(INV_CONTENT(ITEM_STICK) == ITEM_NONE)) { + if (HandleTreeBonk(&this->actor)) { + // no-op + } else if ((this->unk_14C >= 0) && (this->unk_14C < 0x64) && (CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0)) && !(INV_CONTENT(ITEM_STICK) == ITEM_NONE)) { (numDrops = (Rand_ZeroOne() * 4)); for (i = 0; i < numDrops; ++i) { Item_DropCollectible(play, &dropsSpawnPt, ITEM00_STICK); diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 28b33657b27..a5a68b455cd 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -6265,11 +6265,16 @@ s32 func_8083BBA0(Player* this, PlayState* play) { } void Player_SetupRoll(Player* this, PlayState* play) { + if (!GameInteractor_Should(VB_PLAYER_ROLL, true)) { + return; + } + Player_SetupAction(play, this, Player_Action_Roll, 0); LinkAnimation_PlayOnceSetSpeed(play, &this->skelAnime, GET_PLAYER_ANIM(PLAYER_ANIMGROUP_landing_roll, this->modelAnimType), 1.25f * sWaterSpeedFactor); gSaveContext.sohStats.count[COUNT_ROLLS]++; + GameInteractor_ExecuteOnPlayerRoll(); } s32 Player_TryRoll(Player* this, PlayState* play) { @@ -8554,6 +8559,16 @@ void Player_Action_808414F8(Player* this, PlayState* play) { } Player_GetMovementSpeedAndYaw(this, &speedTarget, &yawTarget, SPEED_MODE_LINEAR, play); + + int32_t giSpeedModifier = GameInteractor_RunSpeedModifier(); + if (giSpeedModifier != 0) { + if (giSpeedModifier > 0) { + speedTarget *= giSpeedModifier; + } else { + speedTarget /= abs(giSpeedModifier); + } + } + sp2C = func_8083FD78(this, &speedTarget, &yawTarget, play); if (sp2C >= 0) {