summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Cox <garrettjcox@gmail.com>2024-12-16 13:22:09 -0600
committerMalkierian <malkierian@gmail.com>2025-11-21 11:26:11 -0700
commitd37a68c7ccf64e0b4e2fac50988342b0c2213db7 (patch)
treed9fc03183e95ce9fda858be161e77224b29532ea
parent06aefb30f75f67eed05f891761c69e7c339a3a21 (diff)
Various cleanup
-rw-r--r--soh/soh/Enhancements/Holiday/AGreenSpoon.cpp1
-rw-r--r--soh/soh/Enhancements/Holiday/Caladius.cpp185
-rw-r--r--soh/soh/Enhancements/Holiday/Fredomato.cpp409
-rw-r--r--soh/soh/Enhancements/Holiday/Fredomato.h18
-rw-r--r--soh/soh/Enhancements/Holiday/Grimey.cpp30
-rw-r--r--soh/soh/Enhancements/Holiday/Holiday.hpp2
-rw-r--r--soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp153
-rw-r--r--soh/soh/Enhancements/Holiday/NotProxySaw.cpp1
-rw-r--r--soh/soh/Enhancements/Holiday/Pablo.cpp7
-rw-r--r--soh/soh/Enhancements/Holiday/ProxySaw.cpp51
-rw-r--r--soh/soh/Enhancements/Holiday/lilDavid.cpp1
-rw-r--r--soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp14
-rw-r--r--soh/soh/Enhancements/TimeDisplay/TimeDisplay.h1
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h1
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp4
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h1
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h4
-rw-r--r--soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp8
-rw-r--r--soh/src/code/z_player_lib.c1
-rw-r--r--soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c4
-rw-r--r--soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c12
-rw-r--r--soh/src/overlays/actors/ovl_player_actor/z_player.c15
22 files changed, 653 insertions, 270 deletions
diff --git a/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp b/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp
index 1d632e912..8551efe60 100644
--- a/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp
+++ b/soh/soh/Enhancements/Holiday/AGreenSpoon.cpp
@@ -66,6 +66,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 d4e625ca2..31fb82af8 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,48 +82,138 @@ 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<Actor*, Present> 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);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
std::string msg = std::to_string(giftsCollected).c_str();
msg += " Gifts in Inventory.";
- Notification::Emit({ .itemIcon = "RG_TRIFORCE_PIECE", .message = msg });
+ Notification::Emit({
+ .itemIcon = "RG_TRIFORCE_PIECE",
+ .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;
+ uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0);
+ uint32_t giftsRequired = CVarGetInteger(CVAR("OrnExch.Amount"), 15);
+ if (giftsCollected < giftsRequired) {
+ std::string msg = "You only have %r " + std::to_string(giftsCollected) + "%w If you bring me %g"
+ + std::to_string(giftsRequired) + "%w I'll give you a reward!";
+ messageEntry = CustomMessage(msg);
+ } else {
+ std::string msg = "A present? And %g" + std::to_string(giftsRequired) +
+ "%w to boot? Here's your reward, bring me more if you find any!";
+ messageEntry = CustomMessage(msg);
+ reduceGifts = true;
+ }
+ messageEntry.AutoFormat();
+ messageEntry.LoadIntoFont();
+ *loadFromMessageTable = false;
+
+ if (reduceGifts) {
+ vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy();
+ giftsCollected -= giftsRequired;
+ CVarSetInteger(CVAR("GiftsCollected"), giftsCollected);
+ Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
+
+ std::string msg = std::to_string(giftsCollected).c_str();
+ msg += " Gifts in Inventory.";
+ Notification::Emit({
+ .itemIcon = "RG_TRIFORCE_PIECE",
+ .message = msg
+ });
+ }
});
- COND_ID_HOOK(
- OnOpenText, 0x204A, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](u16* textId, bool* loadFromMessageTable) {
- auto messageEntry = CustomMessage("");
- bool reduceGifts = false;
- uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0);
- uint32_t giftsRequired = CVarGetInteger(CVAR("OrnExch.Amount"), 15);
- if (giftsCollected < giftsRequired) {
- std::string msg = "You only have %r " + std::to_string(giftsCollected) + "%w If you bring me %g" +
- std::to_string(giftsRequired) + "%w I'll give you a reward!";
- messageEntry = CustomMessage(msg);
+
+ 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 {
- std::string msg = "A present? And %g" + std::to_string(giftsRequired) +
- "%w to boot? Here's your reward, bring me more if you find any!";
- messageEntry = CustomMessage(msg);
- reduceGifts = true;
+ pos.x = 0;
+ pos.z = 0;
}
- messageEntry.AutoFormat();
- messageEntry.LoadIntoFont();
- *loadFromMessageTable = false;
-
- if (reduceGifts) {
- vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy();
- giftsCollected -= giftsRequired;
- CVarSetInteger(CVAR("GiftsCollected"), giftsCollected);
- Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
-
- std::string msg = std::to_string(giftsCollected).c_str();
- msg += " Gifts in Inventory.";
- Notification::Emit({ .itemIcon = "RG_TRIFORCE_PIECE", .message = msg });
+ // 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() {
@@ -185,31 +277,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);
+ }
}
static void RegisterMod() {
diff --git a/soh/soh/Enhancements/Holiday/Fredomato.cpp b/soh/soh/Enhancements/Holiday/Fredomato.cpp
index c2ef2a936..1d39819cc 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
@@ -28,31 +35,38 @@ static CollisionPoly snowballPoly;
static f32 raycastResult;
const s16 entrances[] = {
- 0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1,
- 0x0037, 0x0205, 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447,
- 0x009C, 0x033C, 0x00C9, 0x026A, 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E,
- 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2, 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349,
- 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D, 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4,
- 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD, 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, 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, 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD,
- 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D, 0x0130, 0x03AC, 0x0123, 0x0365,
- 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9, 0x0246, 0x01C1,
- 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
- 0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417,
- 0x0423, 0x008D, 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
+ 0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1, 0x0037, 0x0205,
+ 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447, 0x009C, 0x033C, 0x00C9, 0x026A,
+ 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E, 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2,
+ 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349, 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D,
+ 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4, 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD,
+ 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,
+ 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,
+ 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD, 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D,
+ 0x0130, 0x03AC, 0x0123, 0x0365, 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9,
+ 0x0246, 0x01C1, 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
+ 0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417, 0x0423, 0x008D,
+ 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
};
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) {
@@ -64,9 +78,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);
@@ -108,11 +124,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;
@@ -120,35 +135,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<Actor*> trees;
+
+ specialTree = nullptr;
+
+ while (actor != NULL) {
+ if (ACTOR_EN_WOOD02 == actor->id && actor->params < 10) {
+ trees.push_back(actor);
+ }
+ actor = actor->next;
+ }
+
+ 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;
- spawnAttempts++;
+ 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 000000000..3479b3f1e
--- /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 3580a59d0..45dd560b2 100644
--- a/soh/soh/Enhancements/Holiday/Grimey.cpp
+++ b/soh/soh/Enhancements/Holiday/Grimey.cpp
@@ -103,13 +103,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,
@@ -192,9 +212,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 299b4b536..ca5aaf688 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<std::function<void()>> holidayDrawFuncs = {};
inline std::vector<std::function<void()>> holidayRegisterFuncs = {};
diff --git a/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp b/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp
deleted file mode 100644
index fd72e0a29..000000000
--- a/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp
+++ /dev/null
@@ -1,153 +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<Actor*, Present> presents;
-
-void Present_Init(Actor* actor, PlayState* play) {
- Present present;
- presents[actor] = present;
-
- actor->gravity = -1;
- Actor_MoveForward(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 b52732d10..4b6c0f600 100644
--- a/soh/soh/Enhancements/Holiday/NotProxySaw.cpp
+++ b/soh/soh/Enhancements/Holiday/NotProxySaw.cpp
@@ -126,6 +126,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 fcb821117..2025863c8 100644
--- a/soh/soh/Enhancements/Holiday/Pablo.cpp
+++ b/soh/soh/Enhancements/Holiday/Pablo.cpp
@@ -141,9 +141,10 @@ void ShinyDrawImGui() {
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 32e6db264..681b10a64 100644
--- a/soh/soh/Enhancements/Holiday/ProxySaw.cpp
+++ b/soh/soh/Enhancements/Holiday/ProxySaw.cpp
@@ -105,25 +105,23 @@ static void SpawnIcebergs() {
}
const s16 entrances[] = {
- 0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1,
- 0x0037, 0x0205, 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447,
- 0x009C, 0x033C, 0x00C9, 0x026A, 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E,
- 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2, 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349,
- 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D, 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4,
- 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD, 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, 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, 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD,
- 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D, 0x0130, 0x03AC, 0x0123, 0x0365,
- 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9, 0x0246, 0x01C1,
- 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
- 0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417,
- 0x0423, 0x008D, 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
+ 0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1, 0x0037, 0x0205,
+ 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447, 0x009C, 0x033C, 0x00C9, 0x026A,
+ 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E, 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2,
+ 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349, 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D,
+ 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4, 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD,
+ 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,
+ 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,
+ 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD, 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D,
+ 0x0130, 0x03AC, 0x0123, 0x0365, 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9,
+ 0x0246, 0x01C1, 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
+ 0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417, 0x0423, 0x008D,
+ 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
};
static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) {
@@ -148,6 +146,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;
@@ -160,8 +166,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);
@@ -201,12 +207,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 4648cb2dd..529b72bf3 100644
--- a/soh/soh/Enhancements/Holiday/lilDavid.cpp
+++ b/soh/soh/Enhancements/Holiday/lilDavid.cpp
@@ -120,6 +120,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 8f1f0ac40..9369f9771 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 <global.h>
#include "assets/textures/parameter_static/parameter_static.h"
@@ -40,7 +41,8 @@ const std::vector<TimeObject> timeDisplayList = {
{ DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_TIME_DISPLAY("Timers.InGameTimer") },
{ DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_TIME_DISPLAY("Timers.TimeofDay") },
{ DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_TIME_DISPLAY("Timers.HotWater") },
- { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") }
+ { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") },
+ { DISPLAY_FRED_QUEST, "Display Fred's Quest", CVAR_TIME_DISPLAY("Timers.FredsQuest") }
};
static std::vector<TimeObject> 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 c6635b5a7..cf1c71c15 100644
--- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h
+++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.h
@@ -18,6 +18,7 @@ typedef enum TimerDisplay {
DISPLAY_TIME_OF_DAY,
DISPLAY_CONDITIONAL_TIMER,
DISPLAY_NAVI_TIMER,
+ DISPLAY_FRED_QUEST,
} TimerDisplay;
typedef enum NaviTimerValues {
diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h
index 8bdb2a66d..44296118e 100644
--- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h
+++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h
@@ -43,6 +43,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(OnPlayerHoldUpShield, ());
diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp
index f9c906b73..1ab72fe81 100644
--- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp
+++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp
@@ -203,6 +203,10 @@ void GameInteractor_ExecuteOnPlayerBonk() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>();
}
+void GameInteractor_ExecuteOnPlayerRoll() {
+ GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerRoll>();
+}
+
void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerHealthChange>(amount);
}
diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h
index c14a15a7f..3a94c0748 100644
--- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h
+++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h
@@ -44,6 +44,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_ExecuteOnPlayerHoldUpShield();
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index bea1514ae..83924aed1 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2351,6 +2351,10 @@ typedef enum {
// - `*int32_t` // ItemID
VB_USE_ITEM,
+ VB_DRAW_SKEL_LIMB,
+ VB_DRAW_SKEL_FLEX_LIMB,
+ VB_PLAYER_ROLL,
+
} GIVanillaBehavior;
#endif
diff --git a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp
index 74bc44699..663c56350 100644
--- a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp
+++ b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp
@@ -4,6 +4,8 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/OTRGlobals.h"
+static bool isResultOfHandling = false;
+
/**
* SET_CHECK_STATUS
*
@@ -11,7 +13,7 @@
*/
void Anchor::SendPacket_SetCheckStatus(RandomizerCheck rc) {
- if (!IsSaveLoaded() || isProcessingIncomingPacket || !roomState.syncItemsAndFlags) {
+ if (!IsSaveLoaded() || isResultOfHandling) {
return;
}
@@ -39,6 +41,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) {
RandomizerCheck rc = payload["rc"].get<RandomizerCheck>();
RandomizerCheckStatus status = payload["status"].get<RandomizerCheckStatus>();
bool skipped = payload["skipped"].get<bool>();
+
+ isResultOfHandling = true;
if (randoContext->GetItemLocation(rc)->GetCheckStatus() != status) {
randoContext->GetItemLocation(rc)->SetCheckStatus(status);
@@ -46,6 +50,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) {
if (randoContext->GetItemLocation(rc)->GetIsSkipped() != skipped) {
randoContext->GetItemLocation(rc)->SetIsSkipped(skipped);
}
+
CheckTracker::RecalculateAllAreaTotals();
CheckTracker::RecalculateAvailableChecks();
+ isResultOfHandling = false;
}
diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c
index e4f002c92..d45856324 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 <stdlib.h>
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 2f1525466..8023d0236 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
@@ -554,7 +554,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);
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 a96e7a818..9df511517 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
@@ -7,6 +7,7 @@
#include "z_en_wood02.h"
#include "objects/object_wood02/object_wood02.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/Enhancements/Holiday/Fredomato.h"
#define FLAGS 0
@@ -368,8 +369,17 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
if (this->actor.home.rot.y != 0) {
dropsSpawnPt = this->actor.world.pos;
dropsSpawnPt.y += 200.0f;
-
+ if (HandleTreeBonk(&this->actor)) {
+ // no-op
+ } else
if (GameInteractor_Should(VB_TREE_DROP_ITEM, true, this)) {
+ 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);
+ }
+ }
+ } else {
if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) {
if (GameInteractor_Should(VB_TREE_DROP_COLLECTIBLE, true, this)) {
Item_DropCollectibleRandom(play, &this->actor, &dropsSpawnPt, this->unk_14C << 4);
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 55fd5a353..006c43412 100644
--- a/soh/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c
@@ -6304,11 +6304,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.ship.stats.count[COUNT_ROLLS]++;
+ GameInteractor_ExecuteOnPlayerRoll();
}
s32 Player_TryRoll(Player* this, PlayState* play) {
@@ -8570,6 +8575,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) {