From 563061bedac691126804e2525fd182b67af492ef Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Fri, 1 Nov 2024 09:09:13 -0500 Subject: [PATCH] Add holiday menu and various examples --- .../object_penguin/mat_object_penguin_DL_beak | 12 + .../object_penguin/mat_object_penguin_DL_eye | 21 + .../object_penguin/mat_object_penguin_DL_skin | 21 + .../objects/object_penguin/object_penguin_DL | 15 + .../object_penguin/object_penguin_DL_tri_0 | 862 ++++++++++++++++ .../object_penguin/object_penguin_DL_tri_1 | 210 ++++ .../object_penguin/object_penguin_DL_tri_2 | 9 + .../object_penguin/object_penguin_DL_vtx_0 | 922 ++++++++++++++++++ .../object_penguin/object_penguin_DL_vtx_1 | 215 ++++ .../object_penguin/object_penguin_DL_vtx_2 | 10 + .../custom/objects/object_penguin/ping_eye | Bin 0 -> 8284 bytes .../custom/objects/object_penguin/ping_tex | Bin 0 -> 8284 bytes soh/assets/soh_assets.h | 3 + soh/soh/Enhancements/Holiday/AGreenSpoon.cpp | 81 ++ soh/soh/Enhancements/Holiday/Example.cpp | 45 + soh/soh/Enhancements/Holiday/Fredomato.cpp | 155 +++ soh/soh/Enhancements/Holiday/Grimey.cpp | 208 ++++ soh/soh/Enhancements/Holiday/Holiday.hpp | 38 + soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp | 150 +++ soh/soh/Enhancements/Holiday/ProxySaw.cpp | 231 +++++ .../custom-message/CustomMessageManager.cpp | 15 + .../custom-message/CustomMessageManager.h | 3 + .../game-interactor/GameInteractor.h | 27 + .../GameInteractor_HookTable.h | 2 + .../game-interactor/GameInteractor_Hooks.cpp | 15 + .../game-interactor/GameInteractor_Hooks.h | 2 + soh/soh/Enhancements/mods.cpp | 96 +- .../Enhancements/timesaver_hook_handlers.cpp | 6 +- soh/soh/SohMenuBar.cpp | 5 + soh/soh/util.cpp | 13 + soh/soh/util.h | 1 + soh/src/code/z_actor.c | 34 +- soh/src/code/z_message_PAL.c | 7 +- .../overlays/actors/ovl_Door_Ana/z_door_ana.c | 2 +- .../actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 44 +- .../actors/ovl_En_Nutsball/z_en_nutsball.c | 2 +- 36 files changed, 3356 insertions(+), 126 deletions(-) create mode 100644 soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_beak create mode 100644 soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_eye create mode 100644 soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_skin create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_0 create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_1 create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_2 create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_0 create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_1 create mode 100644 soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_2 create mode 100644 soh/assets/custom/objects/object_penguin/ping_eye create mode 100644 soh/assets/custom/objects/object_penguin/ping_tex create mode 100644 soh/soh/Enhancements/Holiday/AGreenSpoon.cpp create mode 100644 soh/soh/Enhancements/Holiday/Example.cpp create mode 100644 soh/soh/Enhancements/Holiday/Fredomato.cpp create mode 100644 soh/soh/Enhancements/Holiday/Grimey.cpp create mode 100644 soh/soh/Enhancements/Holiday/Holiday.hpp create mode 100644 soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp create mode 100644 soh/soh/Enhancements/Holiday/ProxySaw.cpp diff --git a/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_beak b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_beak new file mode 100644 index 000000000..f83feec12 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_beak @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_eye b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_eye new file mode 100644 index 000000000..c24abe2ab --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_eye @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_skin b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_skin new file mode 100644 index 000000000..6c57863e3 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/mat_object_penguin_DL_skin @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL b/soh/assets/custom/objects/object_penguin/object_penguin_DL new file mode 100644 index 000000000..393bbc438 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_0 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_0 new file mode 100644 index 000000000..9039650d4 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_0 @@ -0,0 +1,862 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_1 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_1 new file mode 100644 index 000000000..efb326ace --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_1 @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_2 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_2 new file mode 100644 index 000000000..96c7933ec --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_tri_2 @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_0 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_0 new file mode 100644 index 000000000..f1a650d47 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_0 @@ -0,0 +1,922 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_1 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_1 new file mode 100644 index 000000000..5cd519f95 --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_1 @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_2 b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_2 new file mode 100644 index 000000000..97f03bf0f --- /dev/null +++ b/soh/assets/custom/objects/object_penguin/object_penguin_DL_vtx_2 @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/custom/objects/object_penguin/ping_eye b/soh/assets/custom/objects/object_penguin/ping_eye new file mode 100644 index 0000000000000000000000000000000000000000..32eb2e7e10e39ade1bea8a3473726669ab88169f GIT binary patch literal 8284 zcmb`MJ80ub7{{fnQU(^|Y?scMR4Gi@>Q)?AB+bFH#ggDAtx1*2xLG5D3CndU4Q5G7 z8MB2dGY2kQgb;-#;L@F~(nT<;9Njk~X*_z(LzaZ2hh_c#-+#V&M$=^t9Gz^9k@dNCP#u$fdYNwjaTjEdOmWFrVDhB2C&~x z7tYaqv|O*+-Z)bQSAlf71KVxPu7KOLtJbUaVm&?d9(vXjoW0sr)SND6S8gLi11A$@ z>4e=v10cTu)r7sv?D)E8y?~0rtVJ}$3nN#7l=quedp?@p+ZBj7&Sf>=bJKdVE7SY= zXw^=XL}nSt&^_dyI4%1E;=W-bPLj9RH{ipC({fKBC)q7SftU~UhEoIA9|e(b`PhX= zyXw@S99RV@z6^c2p1R=styJWU2J05e!CkI-3WR*@e9f*rDAS2rPyimB_*)NR4hZS~ zs_h(s^Tp`k1$a<(jvy1a8>2wP_r|jkWZk2x^X&7G2N$yuvzRF9k#p-A4VG^-3xFT=2cqb~`}**MLe4X}7t?7DKR9)!45auTn!lmuZr(>KtVh(G z#~jc!k^4A$>$QaFu`8za$~ZtuK;S-FuFHxZyMP^7u4z4t6r__byaSnkWmijzo;YAv zXIHcq2>Gk_bOEh>UPcwTZ@o+xGzUfejoUy?=wl0Ta(~l9O}7#Dhyp(3{xAEZWM3z% z@y7l*{}r|ZB0ettN{OY9HJWyXW`L0I)U4lS^R3@bEzCdxpJbn6yW2PV#vR%5SymQU z&ol#teDqVL05N|coBe&moV4^a{ZqHDA9VMP z12}%_p6M;!Ols(%LD~Gi(Sewcjs32sv-^Jc818rCC85HX@*mUB^ux(sj2-CMgZpPX zt3btnX#FJe&jgKmiXY@mQ zA7T50qfeGQ%f{#X@J#5yw*4X5AC3SrdqA1?w{KkQ$3~KuzCMQMNL}2vzre@yZ_Wrn zqW|d|=iNh2*+)taC+A(l-}XN&Kc@u5{qG&rFOmB}34DQ85k??U{~Nl`cR)P;P~@9* z{1Haa=ljC(m!ki-bo|Bec{v~&e=6nQUv&H#qx1OU@wY<#gXqKYcY@DTAew(jw?Dbj z^K(Ev|B`Hf|E2SU?6thDrkON3|Sytf5{nt-428G7d-dp(y=2v1v2YTiSeiT zfLA_hu=*oK7b+;a{w7VQ3bHd%as3gI^+%ZP2+AE(*)4cJb5P(HSbtTdV02U<=|rt{Pp)On^)sl*VE^r3wau_3Y78xhhUwN#9f>nsi7M3RU{K7B=647 d(7;zg$>p&#H832;A9^?XP5H+x8hcNb_&;wVkmUdX literal 0 HcmV?d00001 diff --git a/soh/assets/custom/objects/object_penguin/ping_tex b/soh/assets/custom/objects/object_penguin/ping_tex new file mode 100644 index 0000000000000000000000000000000000000000..9913decc7ddb183063709735b48aea252603001b GIT binary patch literal 8284 zcmc(jA#mbA7=^Mm^MS2FYoEe7d;2tbSuEZ2;eE5;8;tK}qt)0wUy?WdeBN(-*L?I`@qWGf zJ8{d8djHn?IJo3X?uR>i|GwV0UGh=+u=o46KXu1fJqNjm_x|*-amAO)qvtG}8z0^u zHm>;5$|Lvi{;)Y7UFZj0RbF|23=S83&|CA{Cw9EI7QCl=7ktvuZ&l@mUOZn?kK8@) zQF-Pb-s33Nf2JNY?euf8T(2uAQlmqv!8GdL7nRojUss##~(P#DRSMXQyq= zJ9RkE5wGac%JT{Gkxs zW#jB}|H+1OWDcDcBaW#NW0YBj&09p-PvU@&Y3smIud76b(I0T`pa52+~b%xd*yrv z8@|lzr~RF}?UdW|kvz;V>~dVu$=sHOZC8J(J=M#a%D&>N_O^V!H{x1e*0Z*Gi_&ez zzuLuNA9cEu(eG?_ZkoTa`)W0Rsd{t$l6|<(brp9~W-61fGSx35Usjc}n@GO1**9i< zabN9bPIq!K*I!`}0;VboF_tRFjIwkqsj8FCGJjg!ZUge?QB>$qG zZ%|}U!TBaxm+3d>^|R|^M+NoazH7=zyY3uc)*IMO>ifC0`stateFlags1 & PLAYER_STATE1_TALKING)) { + Math_ApproachS(&enGs->actor.shape.rot.y, enGs->actor.yawTowardsPlayer, 5, 0xBB8); + + if (enGs->unk_200 <= 0) { + float offsetDistance = 10.0f; + float offsetX = sinf(enGs->actor.shape.rot.y * (M_PI / 0x8000)) * offsetDistance; + float offsetZ = cosf(enGs->actor.shape.rot.y * (M_PI / 0x8000)) * offsetDistance; + + float dx = player->actor.world.pos.x - (enGs->actor.world.pos.x + offsetX); + float dy = player->actor.world.pos.y - 10.0f - enGs->actor.world.pos.y; + float dz = player->actor.world.pos.z - (enGs->actor.world.pos.z + offsetZ); + + s16 rotX = atan2f(dy, sqrtf(dx * dx + dz * dz)) * (0x8000 / M_PI); + s16 rotY = enGs->actor.shape.rot.y; + s16 rotZ = atan2f(dx, dz) * (0x8000 / M_PI); + + Actor* actor = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, + enGs->actor.world.pos.x + offsetX, + enGs->actor.world.pos.y + 40.0f, + enGs->actor.world.pos.z + offsetZ, + rotX, rotY, rotZ, + 100, false); + + EnClearTag* clearTag = (EnClearTag*)actor; + + enGs->unk_200 = 5; + } + + enGs->unk_200--; + } +} + +static void OnConfigurationChanged() { + COND_ID_HOOK(OnOpenText, 0x2053, CVarGetInteger(CVAR("EvilGossipStone"), 0), [](u16 * textId, bool* loadFromMessageTable) { + Actor* actor = Actor_FindNearby(gPlayState, &GET_PLAYER(gPlayState)->actor, ACTOR_EN_GS, ACTORCAT_PROP, 100.0f); + + if (actor == NULL) { + return; + } + + EnGs* gs = (EnGs*)actor; + gs->actionFunc = EnGs_Evil; + }); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + if (UIWidgets::EnhancementCheckbox("Evil Gossip Stone", CVAR("EvilGossipStone"))) { + OnConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #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/Example.cpp b/soh/soh/Enhancements/Holiday/Example.cpp new file mode 100644 index 000000000..235738811 --- /dev/null +++ b/soh/soh/Enhancements/Holiday/Example.cpp @@ -0,0 +1,45 @@ +#include "Holiday.hpp" + +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" +extern PlayState* gPlayState; + +// TODO: Include anything you need here from C land +} + +// TODO: Change this to YourName +#define AUTHOR "Example" +#define CVAR(v) "gHoliday." AUTHOR "." v + +static void OnConfigurationChanged() { + // TODO: Register any hooks or things that need to run on startup and when the main CVar is toggled + // Note: Hooks should be registered/unregistered depending on the CVar state (Use COND_HOOK or COND_ID_HOOK) + + // COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("Enabled"), 0), []() { + // // Spawn your own actors? + // }); + + // COND_ID_HOOK(OnActorInit, ACTOR_OBJ_TSUBO, CVarGetInteger(CVAR("DoSomethingWithPots"), 0), [](void* actorRef) { + // // Do something with pots? + // }); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + if (UIWidgets::EnhancementCheckbox("DoSomethingWithPots", CVAR("DoSomethingWithPots"))) { + OnConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #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/Fredomato.cpp b/soh/soh/Enhancements/Holiday/Fredomato.cpp new file mode 100644 index 000000000..5ac148c2d --- /dev/null +++ b/soh/soh/Enhancements/Holiday/Fredomato.cpp @@ -0,0 +1,155 @@ +#include "Holiday.hpp" +#include +#include "soh/UIWidgets.hpp" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "objects/object_dog/object_dog.h" +#include "soh/frame_interpolation.h" +#include "soh/Enhancements/randomizer/3drando/random.hpp" +#include "soh/Enhancements/randomizer/3drando/location_access.hpp" +#include "soh/Enhancements/randomizer/entrance.h" + +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "objects/object_md/object_md.h" +#include "src/overlays/actors/ovl_Door_Ana/z_door_ana.h" +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" + +extern PlayState* gPlayState; +void DoorAna_SetupAction(DoorAna* doorAna, DoorAnaActionFunc actionFunc); +void DoorAna_GrabPlayer(DoorAna* doorAna, PlayState* play); +} + +#define AUTHOR "Fredomato" +#define CVAR(v) "gHoliday." AUTHOR "." v + +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 +}; + +static bool midoGrottoInit = false; +static SkelAnime midoSkelAnime; +static Vec3s midoJointTable[17]; +static Vec3s midoMorphTable[17]; + +static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) { + if (!midoGrottoInit) { + midoGrottoInit = true; + SkelAnime_InitFlex(play, &midoSkelAnime, (FlexSkeletonHeader*)&gMidoSkel, (AnimationHeader*)&gMidoWalkingAnim, midoJointTable, midoMorphTable, 17); + } + SkelAnime_Update(&midoSkelAnime); + + 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); + + Math_ApproachS(&doorAna->actor.shape.rot.y, doorAna->actor.yawTowardsPlayer, 5, 0xBB8); + + if (Math_StepToF(&actor->scale.x, 0.01f, 0.001f)) { + if ((actor->targetMode != 0) && (play->transitionTrigger == TRANS_TRIGGER_OFF) && (player->stateFlags1 & PLAYER_STATE1_FLOOR_DISABLED) && (player->av1.actionVar1 == 0)) { + play->nextEntranceIndex = RandomElement(entrances); + DoorAna_SetupAction((DoorAna*)actor, DoorAna_GrabPlayer); + } else { + if (!Player_InCsMode(play) && !(player->stateFlags1 & (PLAYER_STATE1_ON_HORSE | PLAYER_STATE1_IN_WATER)) && + actor->xzDistToPlayer <= 15.0f && -50.0f <= actor->yDistToPlayer && + actor->yDistToPlayer <= 15.0f) { + player->stateFlags1 |= PLAYER_STATE1_FLOOR_DISABLED; + actor->targetMode = 1; + } else { + actor->targetMode = 0; + } + } + } + Actor_SetScale(actor, actor->scale.x); +} + +static void RandomGrotto_Draw(Actor* actor, PlayState* play) { + if (!midoGrottoInit) { + return; + } + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Xlu(play->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gGrottoDL); + + Matrix_Translate(0.0f, -2700.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD); + gSPSegment(POLY_OPA_DISP++, 0x08, (uintptr_t)gMidoEyeOpenTex); + func_80034BA0(play, &midoSkelAnime, NULL, NULL, actor, 255); + + CLOSE_DISPS(play->state.gfxCtx); +} + +static void SpawnRandomGrotto() { + 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, &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; + } + + spawnAttempts++; + } +} + +static void ConfigurationChanged() { + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("KrampusHole"), 0), SpawnRandomGrotto); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + + if (UIWidgets::EnhancementCheckbox("The Krampus Hole", CVAR("KrampusHole"))) { + ConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + ConfigurationChanged(); + // #endregion +} + +static Holiday holiday(DrawMenu, RegisterMod); diff --git a/soh/soh/Enhancements/Holiday/Grimey.cpp b/soh/soh/Enhancements/Holiday/Grimey.cpp new file mode 100644 index 000000000..cd21ce91c --- /dev/null +++ b/soh/soh/Enhancements/Holiday/Grimey.cpp @@ -0,0 +1,208 @@ +#include "Holiday.hpp" +#include "soh/Enhancements/randomizer/3drando/random.hpp" +#include "soh/frame_interpolation.h" +#include "soh_assets.h" +#include "overlays/actors/ovl_En_Nutsball/z_en_nutsball.h" + +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" + +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +extern PlayState* gPlayState; +void func_80ABBBA8(EnNutsball* nut, PlayState* play); +void EnNutsball_Draw(Actor* nut, PlayState* play); +} + +#define AUTHOR "Grimey" +#define CVAR(v) "gHoliday." AUTHOR "." v + +static bool spawningPenguins = false; + +typedef enum { + PENGUIN_STATE_IDLE, + PENGUIN_STATE_WALK, +} PenguinState; + +struct Penguin { + PenguinState state; + s16 timer; + s16 targetRot; +}; + +std::unordered_map penguins; + +void Penguin_Init(Actor* actor, PlayState* play) { + Penguin penguin; + penguin.state = PENGUIN_STATE_IDLE; + penguin.timer = 0; + actor->world.rot.y = penguin.targetRot = rand() % 0x10000; + penguins[actor] = penguin; + actor->gravity = -1.0f; + actor->flags &= ~ACTOR_FLAG_TARGETABLE; +} + +void Penguin_Update(Actor* actor, PlayState* play) { + Penguin* penguin = &penguins[actor]; + + if (penguin->timer <= 0) { + if (penguin->state == PENGUIN_STATE_IDLE) { + penguin->state = (PenguinState)(rand() % 3); + penguin->timer = rand() % (20 * 10) + (20 * 3); + } else { + penguin->state = PENGUIN_STATE_IDLE; + penguin->timer = rand() % (20 * 10) + (20 * 3); + } + } else { + penguin->timer--; + } + + if (rand() % 100 == 0) { + penguin->targetRot = rand() % 0x10000; + } + + switch (penguin->state) { + case PENGUIN_STATE_IDLE: + break; + case PENGUIN_STATE_WALK: + actor->speedXZ = 0.5f; + break; + } + + Math_SmoothStepToS(&actor->world.rot.y, penguin->targetRot, 1, 200, 0); + actor->shape.rot.y = actor->world.rot.y; + + if (actor->speedXZ < 0.0f) { + actor->speedXZ = 0.0f; + } + + Actor_MoveForward(actor); + + Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF); +} + +void Penguin_Draw(Actor* actor, PlayState* play) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + + Matrix_Scale(0.8f, 0.8f, 0.8f, MTXMODE_APPLY); + Matrix_Translate(0, 2000.0f, 0, 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*)gPenguinDL); + + CLOSE_DISPS(play->state.gfxCtx); +} + +void Penguin_Destroy(Actor* actor, PlayState* play) { + penguins.erase(actor); +} + +static void OnConfigurationChanged() { + COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("Hailstorm"), 0), []() { + // Every frame has a 1/300 chance of spawning hail + if (rand() % 300 == 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.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++; + } + } + }); + + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("Penguins"), 0), []() { + penguins.clear(); + + if (gPlayState->sceneNum != SCENE_HYRULE_FIELD) { + return; + } + + static Vec3f huddlePos; + static Vec3f spawnPos; + static f32 raycastResult; + static CollisionPoly poly; + + spawningPenguins = true; + + int huddlesSpawned = 0; + while (huddlesSpawned < 10) { + huddlePos.x = (float)(Random( + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -10000 : -2700) + 10000, + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 5000 : 2000) + 10000 + ) - (float)10000.0f); + huddlePos.y = 5000; + huddlePos.z = (float)(Random( + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -1000 : -2000) + 10000, + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 15000 : 2000) + 10000 + ) - (float)10000.0f); + + if (BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &poly, &huddlePos) <= BGCHECK_Y_MIN) { + continue; + } + + // 5-10 + int huddleSize = rand() % 6 + 5; + int penguinsSpawned = 0; + while (penguinsSpawned < huddleSize) { + spawnPos.x = huddlePos.x + rand() % 100 - 50; + spawnPos.y = huddlePos.y; + spawnPos.z = huddlePos.z + rand() % 100 - 50; + + raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &poly, &spawnPos); + + if (raycastResult > BGCHECK_Y_MIN) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, spawnPos.x, raycastResult, spawnPos.z, 0, 0, 0, 0, false); + penguinsSpawned++; + } + } + huddlesSpawned++; + } + + spawningPenguins = false; + }); + + COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("Penguins"), 0), [](void* actorRef, bool* should) { + Actor* actor = (Actor*)actorRef; + if (spawningPenguins) { + actor->init = Penguin_Init; + actor->update = Penguin_Update; + actor->draw = Penguin_Draw; + actor->destroy = Penguin_Destroy; + } + }); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + if (UIWidgets::EnhancementCheckbox("Penguins", CVAR("Penguins"))) { + OnConfigurationChanged(); + } + if (UIWidgets::EnhancementCheckbox("Hailstorm", CVAR("Hailstorm"))) { + OnConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #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/Holiday.hpp b/soh/soh/Enhancements/Holiday/Holiday.hpp new file mode 100644 index 000000000..9a6717060 --- /dev/null +++ b/soh/soh/Enhancements/Holiday/Holiday.hpp @@ -0,0 +1,38 @@ +#ifndef HOLIDAY_HPP +#define HOLIDAY_HPP + +#include +#include +#include +#include "soh/UIWidgets.hpp" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" + +inline std::vector> holidayDrawFuncs = {}; +inline std::vector> holidayRegisterFuncs = {}; + +inline void DrawHolidayMenu() { + if (ImGui::BeginMenu("Holiday")) { + for (auto& drawFunc : holidayDrawFuncs) { + ImGui::PushID(&drawFunc); + drawFunc(); + ImGui::PopID(); + } + ImGui::EndMenu(); + } +} + +inline void RegisterHoliday() { + for (auto& regFunc : holidayRegisterFuncs) { + regFunc(); + } +} + +struct Holiday { + Holiday(std::function drawFunc, std::function registerFunc) { + holidayDrawFuncs.push_back(drawFunc); + holidayRegisterFuncs.push_back(registerFunc); + } +}; + +#endif //HOLIDAY_HPP diff --git a/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp b/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp new file mode 100644 index 000000000..778b260ec --- /dev/null +++ b/soh/soh/Enhancements/Holiday/ItsHeckinPat.cpp @@ -0,0 +1,150 @@ +#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_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; + +static void OnConfigurationChanged() { + 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"))) { + OnConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #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/ProxySaw.cpp b/soh/soh/Enhancements/Holiday/ProxySaw.cpp new file mode 100644 index 000000000..988ae28a9 --- /dev/null +++ b/soh/soh/Enhancements/Holiday/ProxySaw.cpp @@ -0,0 +1,231 @@ +#include "Holiday.hpp" +#include +#include "soh/UIWidgets.hpp" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "objects/object_dog/object_dog.h" +#include "soh/frame_interpolation.h" +#include "soh/Enhancements/randomizer/3drando/random.hpp" +#include "soh/Enhancements/randomizer/3drando/location_access.hpp" +#include "soh/Enhancements/randomizer/entrance.h" + +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "objects/object_md/object_md.h" +#include "src/overlays/actors/ovl_Door_Ana/z_door_ana.h" +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" + +extern PlayState* gPlayState; +extern "C" s16 gEnSnowballId; +void DoorAna_SetupAction(DoorAna* doorAna, DoorAnaActionFunc actionFunc); +void DoorAna_GrabPlayer(DoorAna* doorAna, PlayState* play); +} + +#define AUTHOR "ProxySaw" +#define CVAR(v) "gHoliday." AUTHOR "." v + +static CollisionPoly snowballPoly; +static Vec3f snowballPos; +static f32 raycastResult; + +static u32 iceBlockParams[] = { + 0x214, + 0x1, + 0x11, + 0x10, + 0x20, +}; + +static void SpawnSnowballs() { + if (gPlayState->sceneNum != SCENE_HYRULE_FIELD && gPlayState->sceneNum != SCENE_KAKARIKO_VILLAGE) { + return; + } + + int actorsSpawned = 0; + + while (actorsSpawned < 30) { + snowballPos.x = (float)(Random( + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -10000 : -2700) + 10000, + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 5000 : 2000) + 10000 + ) - (float)10000.0f); + snowballPos.y = 5000; + snowballPos.z = (float)(Random( + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -1000 : -2000) + 10000, + (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 15000 : 2000) + 10000 + ) - (float)10000.0f); + + raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos); + + if (raycastResult > BGCHECK_Y_MIN) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, gEnSnowballId, snowballPos.x, raycastResult, + snowballPos.z, 0, 0, 0, gPlayState->sceneNum == SCENE_HYRULE_FIELD, 0); + actorsSpawned++; + } + } +} + +static void SpawnIcebergs() { + if (gPlayState->sceneNum != SCENE_LAKE_HYLIA) { + return; + } + + int actorsSpawned = 0; + + Vec3f spawnedIceBlockPos[15]; + + while (actorsSpawned < 15) { + Vec3f iceBlockPos; + iceBlockPos.x = (float)(Random( + (-4200) + 10000, + (3000) + 10000 + ) - (float)10000.0f); + iceBlockPos.y = -1713.0f; + iceBlockPos.z = (float)(Random( + (2600) + 10000, + (9000) + 10000 + ) - (float)10000.0f); + + raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &iceBlockPos); + + if (raycastResult > BGCHECK_Y_MIN) { + + bool overlaps = false; + for (int i = 0; i < actorsSpawned; i++) { + if (Math_Vec3f_DistXZ(&spawnedIceBlockPos[i], &iceBlockPos) < 500.0f) { + overlaps = true; + break; + } + } + + if (overlaps) { + continue; + } + + if (LINK_IS_ADULT && !Flags_GetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER)) { + iceBlockPos.y = raycastResult; + } else { + iceBlockPos.y = -1310.0f; + } + + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_BG_SPOT08_ICEBLOCK, iceBlockPos.x, iceBlockPos.y, + iceBlockPos.z, 0, (s16)Random(0, 0xFFFF), 0, RandomElement(iceBlockParams), 0); + spawnedIceBlockPos[actorsSpawned] = iceBlockPos; + actorsSpawned++; + } + } +} + +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 +}; + +static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) { + Actor* actor = &doorAna->actor; + Player* player = GET_PLAYER(play); + if (Math_StepToF(&actor->scale.x, 0.01f, 0.001f)) { + if ((actor->targetMode != 0) && (play->transitionTrigger == TRANS_TRIGGER_OFF) && (player->stateFlags1 & PLAYER_STATE1_FLOOR_DISABLED) && (player->av1.actionVar1 == 0)) { + play->nextEntranceIndex = RandomElement(entrances); + DoorAna_SetupAction((DoorAna*)actor, DoorAna_GrabPlayer); + } else { + if (!Player_InCsMode(play) && !(player->stateFlags1 & (PLAYER_STATE1_ON_HORSE | PLAYER_STATE1_IN_WATER)) && + actor->xzDistToPlayer <= 15.0f && -50.0f <= actor->yDistToPlayer && + actor->yDistToPlayer <= 15.0f) { + player->stateFlags1 |= PLAYER_STATE1_FLOOR_DISABLED; + actor->targetMode = 1; + } else { + actor->targetMode = 0; + } + } + } + Actor_SetScale(actor, actor->scale.x); +} + +static void SpawnRandomGrotto() { + 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, &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); + DoorAna_SetupAction((DoorAna*)grotto, RandomGrotto_WaitOpen); + break; + } + + spawnAttempts++; + } + +} + +static void ConfigurationChanged() { + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("Snowballs"), 0), SpawnSnowballs); + COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("SuperBonk"), 0), []() { + Player* player = GET_PLAYER(gPlayState); + if (player->actor.bgCheckFlags & 0x08 && ABS(player->linearVelocity) > 15.0f) { + player->yaw = ((player->actor.wallYaw - player->yaw) + player->actor.wallYaw) - 0x8000; + Player_PlaySfx(&player->actor, NA_SE_PL_BODY_HIT); + } + }); + + COND_HOOK(OnPlayerBonk, CVarGetInteger(CVAR("SuperBonk"), 0), []() { + Player* player = GET_PLAYER(gPlayState); + + player->linearVelocity = -100.0f; + }); + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("Icebergs"), 0), SpawnIcebergs); + COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("DownTheRabbitHole"), 0), SpawnRandomGrotto); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + + if (UIWidgets::EnhancementCheckbox("Snowballs", CVAR("Snowballs"))) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementCheckbox("Lake Hylia Icebergs", CVAR("Icebergs"))) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementCheckbox("Down the Rabbit Hole", CVAR("DownTheRabbitHole"))) { + ConfigurationChanged(); + } + if (UIWidgets::EnhancementCheckbox("Super Bonk", CVAR("SuperBonk"))) { + ConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + ConfigurationChanged(); + // #endregion +} + +static Holiday holiday(DrawMenu, RegisterMod); diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index c24b83fb6..477cb765e 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -7,6 +7,12 @@ #include #include +#include "soh/util.h" + +extern "C" { + PlayState* gPlayState; +} + using namespace std::literals::string_literals; static const std::unordered_map textBoxSpecialCharacters = { @@ -177,6 +183,15 @@ const TextBoxPosition& CustomMessage::GetTextBoxPosition() const { return position; } +void CustomMessage::LoadIntoFont() { + MessageContext* msgCtx = &gPlayState->msgCtx; + Font* font = &msgCtx->font; + char* buffer = font->msgBuf; + const int maxBufferSize = sizeof(font->msgBuf); + font->charTexBuf[0] = (type << 4) | position; + msgCtx->msgLength = font->msgLength = SohUtils::CopyStringToCharBuffer(GetEnglish(MF_RAW), buffer, maxBufferSize); +} + CustomMessage CustomMessage::operator+(const CustomMessage& right) const { std::vector newColors = colors; std::vector rColors = right.GetColors(); diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index 130feda73..ef9ecd725 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -69,6 +69,9 @@ class CustomMessage { void SetTextBoxType(TextBoxType boxType); const TextBoxPosition& GetTextBoxPosition() const; + // To only be used with OnOpenText hook + void LoadIntoFont(); + CustomMessage operator+(const CustomMessage& right) const; CustomMessage operator+(const std::string& right) const; void operator+=(const std::string& right); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 1667cda00..1ffea910e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -571,6 +571,33 @@ struct HookInfo { body; \ va_end(args); \ }) +#define COND_HOOK(hookType, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHook(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = GameInteractor::Instance->RegisterGameHook(body); \ + } \ + } +#define COND_ID_HOOK(hookType, id, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHookForID(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = GameInteractor::Instance->RegisterGameHookForID(id, body); \ + } \ + } +#define COND_VB_SHOULD(id, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHookForID(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = REGISTER_VB_SHOULD(id, body); \ + } \ + } class GameInteractor { public: diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index a2d5c56ec..06ef7cc21 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -20,6 +20,7 @@ DEFINE_HOOK(OnSceneSpawnActors, ()); DEFINE_HOOK(OnPlayerUpdate, ()); DEFINE_HOOK(OnOcarinaSongAction, ()); DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price)); +DEFINE_HOOK(ShouldActorInit, (void* actor, bool* result)); DEFINE_HOOK(OnActorInit, (void* actor)); DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor)); @@ -31,6 +32,7 @@ DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount)); DEFINE_HOOK(OnPlayerBottleUpdate, (int16_t contents)); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); +DEFINE_HOOK(OnOpenText, (u16 * textId, bool* loadFromMessageTable)); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); DEFINE_HOOK(OnSaveFile, (int32_t fileNum)); DEFINE_HOOK(OnLoadFile, (int32_t fileNum)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 39fc298a8..a2235f7f0 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -79,6 +79,15 @@ void GameInteractor_ExecuteOnActorInit(void* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +bool GameInteractor_ShouldActorInit(void* actor) { + bool result = true; + GameInteractor::Instance->ExecuteHooks(actor, &result); + GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor, &result); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor, &result); + GameInteractor::Instance->ExecuteHooksForFilter(actor, &result); + return result; +} + void GameInteractor_ExecuteOnActorUpdate(void* actor) { GameInteractor::Instance->ExecuteHooks(actor); GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor); @@ -131,6 +140,12 @@ void GameInteractor_ExecuteOnPlayDrawEnd() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable) { + GameInteractor::Instance->ExecuteHooks(textId, loadFromMessageTable); + GameInteractor::Instance->ExecuteHooksForID(*textId, textId, loadFromMessageTable); + GameInteractor::Instance->ExecuteHooksForFilter(textId, loadFromMessageTable); +} + bool GameInteractor_Should(GIVanillaBehavior flag, u32 result, ...) { // Only the external function can use the Variadic Function syntax // To pass the va args to the next caller must be done using va_list and reading the args into it diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 3438d269d..60326614d 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -19,6 +19,7 @@ void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneSpawnActors(); void GameInteractor_ExecuteOnPlayerUpdate(); void GameInteractor_ExecuteOnOcarinaSongAction(); +bool GameInteractor_ShouldActorInit(void* actor); void GameInteractor_ExecuteOnActorInit(void* actor); void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor); @@ -32,6 +33,7 @@ void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnPlayDestroy(); void GameInteractor_ExecuteOnPlayDrawEnd(); +void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable); bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); // MARK: - Save Files diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 5059d9b83..2e943b37f 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -13,6 +13,7 @@ #include "soh/Enhancements/TimeSavers/TimeSavers.h" #include "soh/Enhancements/cheat_hook_handlers.h" #include "soh/Enhancements/randomizer/hook_handlers.h" +#include "soh/Enhancements/Holiday/Holiday.hpp" #include "objects/object_gi_compass/object_gi_compass.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" @@ -1413,99 +1414,6 @@ void RegisterRandomizerCompasses() { }); } -static CollisionPoly snowballPoly; -static Vec3f snowballPos; -static f32 raycastResult; - -static u32 iceBlockParams[] = { - 0x214, - 0x1, - 0x11, - 0x10, - 0x20, -}; - -void RegisterSnowballs() { - GameInteractor::Instance->RegisterGameHook([]() { - if (gPlayState->sceneNum != SCENE_HYRULE_FIELD && gPlayState->sceneNum != SCENE_KAKARIKO_VILLAGE) { - return; - } - - int actorsSpawned = 0; - - while (actorsSpawned < 30) { - snowballPos.x = (float)(Random( - (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -10000 : -2700) + 10000, - (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 5000 : 2000) + 10000 - ) - (float)10000.0f); - snowballPos.y = 5000; - snowballPos.z = (float)(Random( - (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? -1000 : -2000) + 10000, - (gPlayState->sceneNum == SCENE_HYRULE_FIELD ? 15000 : 2000) + 10000 - ) - (float)10000.0f); - - raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &snowballPos); - - if (raycastResult > BGCHECK_Y_MIN) { - Actor_Spawn(&gPlayState->actorCtx, gPlayState, gEnSnowballId, snowballPos.x, raycastResult, - snowballPos.z, 0, 0, 0, gPlayState->sceneNum == SCENE_HYRULE_FIELD, 0); - actorsSpawned++; - } - } - }); - - GameInteractor::Instance->RegisterGameHook([]() { - if (gPlayState->sceneNum != SCENE_LAKE_HYLIA) { - return; - } - - int actorsSpawned = 0; - - Vec3f spawnedIceBlockPos[15]; - - while (actorsSpawned < 15) { - Vec3f iceBlockPos; - iceBlockPos.x = (float)(Random( - (-4200) + 10000, - (3000) + 10000 - ) - (float)10000.0f); - iceBlockPos.y = -1713.0f; - iceBlockPos.z = (float)(Random( - (2600) + 10000, - (9000) + 10000 - ) - (float)10000.0f); - - raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &iceBlockPos); - - if (raycastResult > BGCHECK_Y_MIN) { - - bool overlaps = false; - for (int i = 0; i < actorsSpawned; i++) { - if (Math_Vec3f_DistXZ(&spawnedIceBlockPos[i], &iceBlockPos) < 500.0f) { - overlaps = true; - break; - } - } - - if (overlaps) { - continue; - } - - if (LINK_IS_ADULT && !Flags_GetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER)) { - iceBlockPos.y = raycastResult; - } else { - iceBlockPos.y = -1310.0f; - } - - Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_BG_SPOT08_ICEBLOCK, iceBlockPos.x, iceBlockPos.y, - iceBlockPos.z, 0, (s16)Random(0, 0xFFFF), 0, RandomElement(iceBlockParams), 0); - spawnedIceBlockPos[actorsSpawned] = iceBlockPos; - actorsSpawned++; - } - } - }); -} - void InitMods() { BossRush_RegisterHooks(); RandomizerRegisterHooks(); @@ -1544,11 +1452,11 @@ void InitMods() { RegisterOpenAllHours(); RegisterToTMedallions(); RegisterRandomizerCompasses(); - RegisterSnowballs(); NameTag_RegisterHooks(); RegisterFloorSwitchesHook(); RegisterPatchHandHandler(); RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); RandoKaleido_RegisterHooks(); + RegisterHoliday(); } diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index 3e1bc9de1..e5ddf73e7 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -892,7 +892,7 @@ void TimeSaverOnSceneInitHandler(int16_t sceneNum) { } } -static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE; +GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE; void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) { if (!CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) return; @@ -1063,11 +1063,11 @@ void TimeSaverRegisterHooks() { onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnVanillaBehaviorHandler); onActorInitHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnActorInitHandler); onGameFrameUpdate = GameInteractor::Instance->RegisterGameHook(TimeSaverOnGameFrameUpdateHandler); + onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnPlayerUpdateHandler); + onItemReceiveHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnItemReceiveHandler); if (IS_RANDO) return; onFlagSetHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnFlagSetHandler); - onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnPlayerUpdateHandler); - onItemReceiveHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnItemReceiveHandler); }); } diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index bec69cceb..01f54b276 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -41,6 +41,7 @@ #include "Enhancements/resolution-editor/ResolutionEditor.h" #include "Enhancements/enemyrandomizer.h" #include "Enhancements/timesplits/TimeSplits.h" +#include "Enhancements/Holiday/Holiday.hpp" // FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but // they don't work how I expect them to so I added that because it looked good when I eyeballed it @@ -2256,6 +2257,10 @@ void SohMenuBar::DrawElement() { DrawRandomizerMenu(); + ImGui::SetCursorPosY(0.0f); + + DrawHolidayMenu(); + ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index faa171231..297e63db3 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -391,6 +391,19 @@ size_t SohUtils::CopyStringToCharBuffer(char* buffer, const std::string& source, return 0; } +int SohUtils::CopyStringToCharBuffer(const std::string& inputStr, char* buffer, const int maxBufferSize) { + if (!inputStr.empty()) { + // Prevent potential horrible overflow due to implicit conversion of maxBufferSize to an unsigned. Prevents negatives. + memset(buffer, 0, std::max(0, maxBufferSize)); + // Gaurentee that this value will be greater than 0, regardless of passed variables. + const int copiedCharLen = std::min(std::max(0, maxBufferSize - 1), inputStr.length()); + memcpy(buffer, inputStr.c_str(), copiedCharLen); + return copiedCharLen; + } + + return 0; +} + bool SohUtils::IsStringEmpty(std::string str) { // Remove spaces at the beginning of the string std::string::size_type start = str.find_first_not_of(' '); diff --git a/soh/soh/util.h b/soh/soh/util.h index a37279a18..a566a5382 100644 --- a/soh/soh/util.h +++ b/soh/soh/util.h @@ -20,6 +20,7 @@ namespace SohUtils { // Copies a string into a char buffer up to maxBufferSize characters. This does NOT insert a null terminator // on the end, as this is used for in-game messages which are not null-terminated. size_t CopyStringToCharBuffer(char* buffer, const std::string& source, size_t maxBufferSize); + int CopyStringToCharBuffer(const std::string& inputStr, char* buffer, const int maxBufferSize); bool IsStringEmpty(std::string str); } // namespace SohUtils diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index dccb4d9bf..165ff4c47 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1232,14 +1232,19 @@ void Actor_Init(Actor* actor, PlayState* play) { ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { Actor_SetObjectDependency(play, actor); - actor->init(actor, play); - actor->init = NULL; + if (GameInteractor_ShouldActorInit(actor)) { + actor->init(actor, play); + actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); + GameInteractor_ExecuteOnActorInit(actor); - // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { - actor->maximumHealth = actor->colChkInfo.health; + // For enemy health bar we need to know the max health during init + if (actor->category == ACTORCAT_ENEMY) { + actor->maximumHealth = actor->colChkInfo.health; + } + } else { + actor->init = NULL; + Actor_Kill(actor); } } } @@ -2577,14 +2582,19 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { Actor_SetObjectDependency(play, actor); - actor->init(actor, play); - actor->init = NULL; + if (GameInteractor_ShouldActorInit(actor)) { + actor->init(actor, play); + actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); + GameInteractor_ExecuteOnActorInit(actor); - // For enemy health bar we need to know the max health during init - if (actor->category == ACTORCAT_ENEMY) { - actor->maximumHealth = actor->colChkInfo.health; + // For enemy health bar we need to know the max health during init + if (actor->category == ACTORCAT_ENEMY) { + actor->maximumHealth = actor->colChkInfo.health; + } + } else { + actor->init = NULL; + Actor_Kill(actor); } } actor = actor->next; diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 53b2bb943..a53626ed9 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -1594,6 +1594,9 @@ void Message_OpenText(PlayState* play, u16 textId) { Font* font = &msgCtx->font; s16 textBoxType; + bool loadFromMessageTable = true; + GameInteractor_ExecuteOnOpenText(&textId, &loadFromMessageTable); + if (msgCtx->msgMode == MSGMODE_NONE) { gSaveContext.unk_13EE = gSaveContext.unk_13EA; } @@ -1652,7 +1655,9 @@ void Message_OpenText(PlayState* play, u16 textId) { } // RANDOTODO: Use this for ice trap messages - if (CustomMessage_RetrieveIfExists(play)) { + if (!loadFromMessageTable) { + // no-op + } else if (CustomMessage_RetrieveIfExists(play)) { osSyncPrintf("Found custom message"); } else if (sTextIsCredits) { Message_FindCreditsMessage(play, textId); diff --git a/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c index ef2134f6e..8471b2303 100644 --- a/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c +++ b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c @@ -179,7 +179,7 @@ void DoorAna_Update(Actor* thisx, PlayState* play) { this->actionFunc(this, play); // Changes the grottos facing angle based on camera angle - if (!CVarGetInteger(CVAR_ENHANCEMENT("DisableGrottoRotation"), 0)) { + if (!CVarGetInteger(CVAR_ENHANCEMENT("DisableGrottoRotation"), 0) && thisx->draw == DoorAna_Draw) { this->actor.shape.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000; } } diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index c19f43dc3..7b381ef1c 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -244,15 +244,22 @@ void EnClearTag_Init(Actor* thisx, PlayState* play) { if (this->actor.params == CLEAR_TAG_LASER) { this->state = CLEAR_TAG_STATE_LASER; this->timers[CLEAR_TAG_TIMER_LASER_DEATH] = 70; - this->actor.speedXZ = 35.0f; - func_8002D908(&this->actor); - for (j = 0; j <= 0; j++) { - func_8002D7EC(&this->actor); + if (CVarGetInteger("gHoliday.AGreenSpoon.EvilGossipStone", 0)) { + this->actor.scale.x = 0.4f; + this->actor.scale.y = 0.4f; + this->actor.scale.z = 2.0f; + this->actor.speedXZ = MAX(10.0f, Actor_WorldDistXZToActor(thisx, &GET_PLAYER(gPlayState)->actor) * 0.33f); + } else { + this->actor.speedXZ = 35.0f; + func_8002D908(&this->actor); + for (j = 0; j <= 0; j++) { + func_8002D7EC(&this->actor); + } + this->actor.scale.x = 0.4f; + this->actor.scale.y = 0.4f; + this->actor.scale.z = 2.0f; + this->actor.speedXZ = 70.0f; } - this->actor.scale.x = 0.4f; - this->actor.scale.y = 0.4f; - this->actor.scale.z = 2.0f; - this->actor.speedXZ = 70.0f; this->actor.shape.rot.x = -this->actor.shape.rot.x; func_8002D908(&this->actor); @@ -570,12 +577,21 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) { } // Set laser collider properties. - this->collider.dim.radius = 23; - this->collider.dim.height = 25; - this->collider.dim.yShift = -10; - Collider_UpdateCylinder(&this->actor, &this->collider); - CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); - Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 80.0f, 100.0f, 5); + if (CVarGetInteger("gHoliday.AGreenSpoon.EvilGossipStone", 0)) { + this->collider.dim.radius = 10; + this->collider.dim.height = 25; + this->collider.dim.yShift = -10; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(play, &this->actor, 10.0f, 10.0f, 10.0f, 5); + } else { + this->collider.dim.radius = 23; + this->collider.dim.height = 25; + this->collider.dim.yShift = -10; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(play, &this->actor, 50.0f, 80.0f, 100.0f, 5); + } // Check if the laser has hit a target, timed out, or hit the ground. if (this->actor.bgCheckFlags & 9 || hasAtHit || this->timers[CLEAR_TAG_TIMER_LASER_DEATH] == 0) { diff --git a/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c index 2a37c90f6..2816c50d2 100644 --- a/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c +++ b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c @@ -78,7 +78,7 @@ void EnNutsball_Init(Actor* thisx, PlayState* play) { ActorShape_Init(&this->actor.shape, 400.0f, ActorShadow_DrawCircle, 13.0f); Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); - if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0)) { + if (CVarGetInteger(CVAR_ENHANCEMENT("RandomizedEnemies"), 0) || CVarGetInteger("gLetItSnow", 0)) { this->objBankIndex = 0; } else { this->objBankIndex = Object_GetIndex(&play->objectCtx, sObjectIDs[this->actor.params]);