summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormckinlee <mckinlee@ymail.com>2025-10-15 12:37:41 -0400
committerGitHub <noreply@github.com>2025-10-15 12:37:41 -0400
commitbbebb77f624061cbc6d4b15590c7a1d9eaa7529f (patch)
tree76d50caa5a5bb5a4de1cc12c77499255a267e200
parentc9e28354f10a2cd8c58eba1aa0aa82fb4f14d744 (diff)
[Enhancement] Remote Bombchu (#1283)
* RemoteBombchu enhancement, steering math needs some work * Updated steering logic to rotate around the bombchu’s up axis * feedback update --------- Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
-rw-r--r--mm/2s2h/BenGui/BenMenu.cpp5
-rw-r--r--mm/2s2h/Enhancements/Camera/FreeLook.cpp7
-rw-r--r--mm/2s2h/Enhancements/Equipment/RemoteBombchu.cpp92
3 files changed, 104 insertions, 0 deletions
diff --git a/mm/2s2h/BenGui/BenMenu.cpp b/mm/2s2h/BenGui/BenMenu.cpp
index c1c8d5563..2cfc503e8 100644
--- a/mm/2s2h/BenGui/BenMenu.cpp
+++ b/mm/2s2h/BenGui/BenMenu.cpp
@@ -930,6 +930,11 @@ void BenMenu::AddEnhancements() {
.CVar("gEnhancements.PlayerActions.ArrowCycle")
.Options(CheckboxOptions().Tooltip(
"While aiming the bow, use R to cycle between Normal, Fire, Ice and Light arrows."));
+ AddWidget(path, "Remote Bombchu Control", WIDGET_CVAR_CHECKBOX)
+ .CVar("gEnhancements.PlayerActions.RemoteBombchu")
+ .Options(CheckboxOptions().Tooltip(
+ "Allows you to control the direction of the Bombchu while it is moving. Press B to detonate. Press A to "
+ "stop controlling the Bombchu."));
AddWidget(path, "Bombchu Drops", WIDGET_CVAR_CHECKBOX)
.CVar("gEnhancements.Equipment.ChuDrops")
.Options(
diff --git a/mm/2s2h/Enhancements/Camera/FreeLook.cpp b/mm/2s2h/Enhancements/Camera/FreeLook.cpp
index b4b1fa359..29a43dae9 100644
--- a/mm/2s2h/Enhancements/Camera/FreeLook.cpp
+++ b/mm/2s2h/Enhancements/Camera/FreeLook.cpp
@@ -17,6 +17,9 @@ extern CameraSetting sCameraSettings[];
extern s32 sCameraInterfaceFlags;
}
+// Check if bombchu remote control is active
+extern bool IsBombchuFocused();
+
// Static Data Used For Free Camera
static bool sCanFreeLook = false;
@@ -151,6 +154,10 @@ bool Camera_CanFreeLook(Camera* camera) {
if (gPlayState != nullptr && Player_InCsMode(gPlayState)) {
sCanFreeLook = false;
}
+ // Disable freecam during bombchu control
+ if (IsBombchuFocused()) {
+ sCanFreeLook = false;
+ }
return sCanFreeLook;
}
diff --git a/mm/2s2h/Enhancements/Equipment/RemoteBombchu.cpp b/mm/2s2h/Enhancements/Equipment/RemoteBombchu.cpp
new file mode 100644
index 000000000..64175b4f9
--- /dev/null
+++ b/mm/2s2h/Enhancements/Equipment/RemoteBombchu.cpp
@@ -0,0 +1,92 @@
+#include "public/bridge/consolevariablebridge.h"
+#include "2s2h/GameInteractor/GameInteractor.h"
+#include "2s2h/ShipInit.hpp"
+
+extern "C" {
+#include "variables.h"
+#include "src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h"
+
+void EnBomChu_Move(EnBomChu*, PlayState*);
+void EnBomChu_Explode(EnBomChu*, PlayState*);
+void EnBomChu_UpdateRotation(EnBomChu*);
+}
+
+#define CVAR_NAME "gEnhancements.PlayerActions.RemoteBombchu"
+#define CVAR CVarGetInteger(CVAR_NAME, 0)
+
+static bool focused = false;
+static EnBomChu* activeBombchu = nullptr;
+
+bool IsBombchuFocused() {
+ return focused;
+}
+
+static void ReleaseBombchuFocus() {
+ Player* player = GET_PLAYER(gPlayState);
+ Camera_SetFocalActor(Play_GetCamera(gPlayState, player->subCamId), &player->actor);
+ player->stateFlags1 &= ~PLAYER_STATE1_20;
+ focused = false;
+}
+
+void RegisterRemoteBombchu() {
+ // Link can deploy multiple bombchus at once. Only assume control of the last placed one.
+ COND_ID_HOOK(OnActorInit, ACTOR_EN_BOM_CHU, CVAR, [](Actor* actor) { activeBombchu = (EnBomChu*)actor; });
+
+ COND_ID_HOOK(OnActorDestroy, ACTOR_EN_BOM_CHU, CVAR, [](Actor* actor) {
+ Player* player = GET_PLAYER(gPlayState);
+
+ if (actor == (Actor*)activeBombchu) {
+ activeBombchu = nullptr;
+
+ if (focused) {
+ ReleaseBombchuFocus();
+ }
+ }
+ });
+
+ COND_ID_HOOK(OnActorUpdate, ACTOR_EN_BOM_CHU, CVAR, [](Actor* actor) {
+ if (actor != (Actor*)activeBombchu) {
+ return;
+ }
+
+ Player* player = GET_PLAYER(gPlayState);
+ Input* input = &gPlayState->state.input[0];
+
+ if (activeBombchu->actionFunc == EnBomChu_Move) {
+ if (!focused) {
+ Camera_SetFocalActor(Play_GetCamera(gPlayState, CutsceneManager_GetCurrentSubCamId(actor->csId)),
+ actor);
+ player->stateFlags1 |= PLAYER_STATE1_20;
+ focused = true;
+ }
+
+ f32 turnRate = 1500.0f;
+ f32 stickX = input->cur.stick_x;
+ if (fabsf(stickX) > 10.0f) {
+ s16 turnAngle = (s16)(turnRate * (stickX / 85.0f));
+
+ Matrix_RotateAxisF(BINANG_TO_RAD(-turnAngle), &activeBombchu->axisUp, MTXMODE_NEW);
+ Vec3f newAxisForwards;
+ Matrix_MultVec3f(&activeBombchu->axisForwards, &newAxisForwards);
+ Math_Vec3f_Copy(&activeBombchu->axisForwards, &newAxisForwards);
+ Math3D_Vec3f_Cross(&activeBombchu->axisUp, &activeBombchu->axisForwards, &activeBombchu->axisLeft);
+
+ EnBomChu_UpdateRotation(activeBombchu);
+ activeBombchu->actor.shape.rot.x = -activeBombchu->actor.world.rot.x;
+ activeBombchu->actor.shape.rot.y = activeBombchu->actor.world.rot.y;
+ activeBombchu->actor.shape.rot.z = activeBombchu->actor.world.rot.z;
+ }
+
+ if (input->press.button & BTN_B) {
+ EnBomChu_Explode(activeBombchu, gPlayState);
+ }
+
+ if (input->press.button & BTN_A) {
+ activeBombchu = nullptr;
+ ReleaseBombchuFocus();
+ }
+ }
+ });
+}
+
+static RegisterShipInitFunc initFunc(RegisterRemoteBombchu, { CVAR_NAME });