diff options
| author | Tharo <17233964+Thar0@users.noreply.github.com> | 2022-09-27 17:40:26 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-27 12:40:26 -0400 |
| commit | 4f65d08eb5826c9c3520fd398b2e76ab09168193 (patch) | |
| tree | d7ea67a3b14cf7387d23a604d99e3ee0e7f77467 /src/code | |
| parent | 82e04ede5f3fef20fb95f6a16b88b8a684f49954 (diff) | |
Rumble doc (#1375)
* Rumble doc
* Fixes, suggested changes
* Improve padmgr retrace callback related code
* Name some rumble-adjacent things, further suggested changes
* Further suggested changes
* Suggested changes
Diffstat (limited to 'src/code')
| -rw-r--r-- | src/code/code_800A9F30.c | 91 | ||||
| -rw-r--r-- | src/code/code_800D2E30.c | 119 | ||||
| -rw-r--r-- | src/code/game.c | 4 | ||||
| -rw-r--r-- | src/code/padmgr.c | 2 | ||||
| -rw-r--r-- | src/code/sys_rumble.c | 151 | ||||
| -rw-r--r-- | src/code/z_actor.c | 4 | ||||
| -rw-r--r-- | src/code/z_construct.c | 8 | ||||
| -rw-r--r-- | src/code/z_debug.c | 2 | ||||
| -rw-r--r-- | src/code/z_demo.c | 2 | ||||
| -rw-r--r-- | src/code/z_game_over.c | 33 | ||||
| -rw-r--r-- | src/code/z_kankyo.c | 4 | ||||
| -rw-r--r-- | src/code/z_play.c | 4 | ||||
| -rw-r--r-- | src/code/z_rumble.c | 135 |
13 files changed, 320 insertions, 239 deletions
diff --git a/src/code/code_800A9F30.c b/src/code/code_800A9F30.c deleted file mode 100644 index b8f4e1ce8..000000000 --- a/src/code/code_800A9F30.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "global.h" - -UnkRumbleStruct D_80160FD0; - -void func_800A9F30(PadMgr* a, s32 b) { - func_800D2E30(&D_80160FD0); - PadMgr_RumbleSet(a, D_80160FD0.rumbleEnable); -} - -void func_800A9F6C(f32 a, u8 b, u8 c, u8 d) { - s32 temp1; - s32 temp2; - - if (1000000.0f < a) { - temp1 = 1000; - } else { - temp1 = sqrtf(a); - } - - if ((temp1 < 1000) && (b != 0) && (d != 0)) { - temp2 = b - (temp1 * 255) / 1000; - if (temp2 > 0) { - D_80160FD0.unk_10A = temp2; - D_80160FD0.unk_10B = c; - D_80160FD0.unk_10C = d; - } - } -} - -void func_800AA000(f32 a, u8 b, u8 c, u8 d) { - s32 temp1; - s32 temp2; - s32 i; - - if (1000000.0f < a) { - temp1 = 1000; - } else { - temp1 = sqrtf(a); - } - - if (temp1 < 1000 && b != 0 && d != 0) { - temp2 = b - (temp1 * 255) / 1000; - - for (i = 0; i < 0x40; i++) { - if (D_80160FD0.unk_04[i] == 0) { - if (temp2 > 0) { - D_80160FD0.unk_04[i] = temp2; - D_80160FD0.unk_44[i] = c; - D_80160FD0.unk_84[i] = d; - } - break; - } - } - } -} - -void func_800AA0B4(void) { - func_800D3140(&D_80160FD0); - - gPadMgr.retraceCallback = func_800A9F30; - gPadMgr.retraceCallbackValue = 0; - - if (1) {} -} - -void func_800AA0F0(void) { - PadMgr* padmgr = &gPadMgr; - - if ((padmgr->retraceCallback == func_800A9F30) && (padmgr->retraceCallbackValue == 0)) { - padmgr->retraceCallback = NULL; - padmgr->retraceCallbackValue = 0; - } - - func_800D3178(&D_80160FD0); -} - -u32 func_800AA148(void) { - return gPadMgr.pakType[0] == 1; -} - -void func_800AA15C(void) { - D_80160FD0.unk_104 = 2; -} - -void func_800AA16C(void) { - D_80160FD0.unk_104 = 0; -} - -void func_800AA178(u32 a) { - D_80160FD0.unk_105 = !!a; -} diff --git a/src/code/code_800D2E30.c b/src/code/code_800D2E30.c deleted file mode 100644 index a182926f1..000000000 --- a/src/code/code_800D2E30.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "global.h" - -void func_800D2E30(UnkRumbleStruct* arg0) { - static u8 D_8012DBB0 = 1; - s32 i; - s32 unk_a3; - s32 index = -1; - - for (i = 0; i < 4; i++) { - arg0->rumbleEnable[i] = 0; - } - - if (arg0->unk_105 == 0) { - if (D_8012DBB0 != 0) { - for (i = 0; i < 4; i++) { - gPadMgr.pakType[i] = 0; - } - } - D_8012DBB0 = arg0->unk_105; - return; - } - - D_8012DBB0 = arg0->unk_105; - - if (arg0->unk_104 == 2) { - for (i = 0; i < 4; ++i) { - gPadMgr.pakType[i] = 0; - } - - for (i = 0; i < 0x40; i++) { - arg0->unk_C4[i] = 0; - arg0->unk_84[i] = 0; - arg0->unk_44[i] = 0; - arg0->unk_04[i] = 0; - } - arg0->unk_106 = arg0->unk_108 = arg0->unk_10A = arg0->unk_10B = arg0->unk_10C = arg0->unk_10D = 0; - arg0->unk_104 = 1; - } - if (arg0->unk_104 != 0) { - for (i = 0; i < 0x40; i++) { - if (arg0->unk_04[i] != 0) { - if (arg0->unk_44[i] > 0) { - arg0->unk_44[i]--; - } else { - unk_a3 = arg0->unk_04[i] - arg0->unk_84[i]; - if (unk_a3 > 0) { - arg0->unk_04[i] = unk_a3; - } else { - arg0->unk_04[i] = 0; - } - } - - unk_a3 = arg0->unk_C4[i] + arg0->unk_04[i]; - arg0->unk_C4[i] = unk_a3; - if (index == -1) { - index = i; - arg0->rumbleEnable[0] = (unk_a3 >= 0x100); - } else if (arg0->unk_04[index] < arg0->unk_04[i]) { - index = i; - arg0->rumbleEnable[0] = (unk_a3 >= 0x100); - } - } - } - if (arg0->unk_10A != 0) { - if (arg0->unk_10B > 0) { - arg0->unk_10B--; - } else { - unk_a3 = arg0->unk_10A - arg0->unk_10C; - if (unk_a3 > 0) { - arg0->unk_10A = unk_a3; - } else { - arg0->unk_10A = 0; - } - } - unk_a3 = arg0->unk_10D + arg0->unk_10A; - arg0->unk_10D = unk_a3; - arg0->rumbleEnable[0] = (unk_a3 >= 0x100); - } - if (arg0->unk_10A != 0) { - unk_a3 = arg0->unk_10A; - } else { - if (index == -1) { - unk_a3 = 0; - } else { - unk_a3 = arg0->unk_04[index]; - } - } - if (unk_a3 == 0) { - if ((++arg0->unk_108) >= 6) { - arg0->unk_106 = 0; - arg0->unk_108 = 5; - } - } else { - arg0->unk_108 = 0; - if ((++arg0->unk_106) >= 0x1C21) { - arg0->unk_104 = 0; - } - } - } else { - for (i = 0; i < 0x40; i++) { - arg0->unk_C4[i] = 0; - arg0->unk_84[i] = 0; - arg0->unk_44[i] = 0; - arg0->unk_04[i] = 0; - } - - arg0->unk_106 = arg0->unk_108 = arg0->unk_10A = arg0->unk_10B = arg0->unk_10C = arg0->unk_10D = 0; - } -} - -void func_800D3140(UnkRumbleStruct* arg0) { - bzero(arg0, sizeof(UnkRumbleStruct)); - arg0->unk_104 = 2; - arg0->unk_105 = 1; -} - -void func_800D3178(UnkRumbleStruct* arg0) { - bzero(arg0, sizeof(UnkRumbleStruct)); -} diff --git a/src/code/game.c b/src/code/game.c index 4d49db966..1138d815f 100644 --- a/src/code/game.c +++ b/src/code/game.c @@ -410,7 +410,7 @@ void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* g ViMode_Init(&sViMode); } SpeedMeter_Init(&D_801664D0); - func_800AA0B4(); + Rumble_Init(); osSendMesg(&gameState->gfxCtx->queue, NULL, OS_MESG_BLOCK); endTime = osGetTime(); @@ -431,7 +431,7 @@ void GameState_Destroy(GameState* gameState) { if (gameState->destroy != NULL) { gameState->destroy(gameState); } - func_800AA0F0(); + Rumble_Destroy(); SpeedMeter_Destroy(&D_801664D0); func_800ACE90(&D_801664F0); func_800AD950(&D_80166500); diff --git a/src/code/padmgr.c b/src/code/padmgr.c index b8830d5a2..eb8032038 100644 --- a/src/code/padmgr.c +++ b/src/code/padmgr.c @@ -361,7 +361,7 @@ void PadMgr_HandleRetrace(PadMgr* padMgr) { // Execute retrace callback if (padMgr->retraceCallback != NULL) { - padMgr->retraceCallback(padMgr, padMgr->retraceCallbackValue); + padMgr->retraceCallback(padMgr, padMgr->retraceCallbackArg); } // Wait for controller data diff --git a/src/code/sys_rumble.c b/src/code/sys_rumble.c new file mode 100644 index 000000000..8991f26cb --- /dev/null +++ b/src/code/sys_rumble.c @@ -0,0 +1,151 @@ +/** + * @file sys_rumble.c + * + * This file implements a manager for storing and processing rumble pak requests made by the game state. Despite some + * parts of the system appearing to accommodate all four controller ports, only controller 1 will rumble according to + * the processed requests. + * This file is half of the system that runs on the padmgr thread alongside controller communications. The rest of the + * system that receives the requests from the game state runs on the graph thread and is implemented in `z_rumble.c`. + * + * @see RumbleMgr + * @see z_rumble.c + * + * @note Original filename is likely sys_vibrate.c or similar as it is ordered after sys_ucode.c + */ +#include "global.h" + +/** + * Rumble manager update, runs on Vertical Retrace on the padmgr thread. + */ +void RumbleMgr_Update(RumbleMgr* rumbleMgr) { + static u8 sWasEnabled = true; + s32 i; + s32 strength; + s32 strongestIndex = -1; + + // Clear enable status for all controllers + for (i = 0; i < MAXCONTROLLERS; i++) { + rumbleMgr->rumbleEnable[i] = false; + } + + if (!rumbleMgr->updateEnabled) { + if (sWasEnabled) { + // If it was previously enabled, reset pak type + for (i = 0; i < MAXCONTROLLERS; i++) { + gPadMgr.pakType[i] = CONT_PAK_NONE; + } + } + sWasEnabled = rumbleMgr->updateEnabled; + return; + } + + sWasEnabled = rumbleMgr->updateEnabled; + + if (rumbleMgr->state == RUMBLE_STATE_RESET) { + // Reset + for (i = 0; i < MAXCONTROLLERS; i++) { + gPadMgr.pakType[i] = CONT_PAK_NONE; + } + + for (i = 0; i < RUMBLE_MAX_REQUESTS; i++) { + rumbleMgr->reqAccumulators[i] = 0; + rumbleMgr->reqDecreaseRates[i] = 0; + rumbleMgr->reqDurations[i] = 0; + rumbleMgr->reqStrengths[i] = 0; + } + + rumbleMgr->onTimer = rumbleMgr->offTimer = rumbleMgr->overrideStrength = rumbleMgr->overrideDuration = + rumbleMgr->overrideDecreaseRate = rumbleMgr->overrideAccumulator = 0; + + rumbleMgr->state = RUMBLE_STATE_RUNNING; + } + + if (rumbleMgr->state != RUMBLE_STATE_CLEAR) { + // Search for index with largest strength + for (i = 0; i < RUMBLE_MAX_REQUESTS; i++) { + if (rumbleMgr->reqStrengths[i] != 0) { + // Non-empty request slot + if (rumbleMgr->reqDurations[i] > 0) { + rumbleMgr->reqDurations[i]--; + } else { + // After duration, decrease the strength by the decrease rate + strength = rumbleMgr->reqStrengths[i] - rumbleMgr->reqDecreaseRates[i]; + rumbleMgr->reqStrengths[i] = MAX(strength, 0); + } + + // Increment accumulator by the strength + strength = rumbleMgr->reqAccumulators[i] + rumbleMgr->reqStrengths[i]; + rumbleMgr->reqAccumulators[i] = strength; + + if (strongestIndex == -1) { + strongestIndex = i; + // Rumble is enabled on the controller only when there is overflow of the accumulator, overflow + // will happen more often for larger request strengths making it feel stronger to the player + rumbleMgr->rumbleEnable[0] = strength > 255; + } else if (rumbleMgr->reqStrengths[i] > rumbleMgr->reqStrengths[strongestIndex]) { + strongestIndex = i; + rumbleMgr->rumbleEnable[0] = strength > 255; + } + } + } + + if (rumbleMgr->overrideStrength != 0) { + // Set override + if (rumbleMgr->overrideDuration > 0) { + rumbleMgr->overrideDuration--; + } else { + // Once the duration is over, start decrementing the strength + strength = rumbleMgr->overrideStrength - rumbleMgr->overrideDecreaseRate; + rumbleMgr->overrideStrength = MAX(strength, 0); + } + // Increment accumulator, set rumble enabled on overflow + strength = rumbleMgr->overrideAccumulator + rumbleMgr->overrideStrength; + rumbleMgr->overrideAccumulator = strength; + rumbleMgr->rumbleEnable[0] = strength > 255; + } + + if (rumbleMgr->overrideStrength != 0) { + strength = rumbleMgr->overrideStrength; + } else { + strength = (strongestIndex == -1) ? 0 : rumbleMgr->reqStrengths[strongestIndex]; + } + + if (strength == 0) { + // No rumble + if ((++rumbleMgr->offTimer) > 5) { + // After 5 VIs with no rumble, reset the rumble on timer + rumbleMgr->onTimer = 0; + rumbleMgr->offTimer = 5; + } + } else { + // Rumble + rumbleMgr->offTimer = 0; + if ((++rumbleMgr->onTimer) > 7200) { // 2 minutes at 60 VI/s, 2 minutes 24 seconds at 50 VI/s + // Clear all requests if rumble has been on for too long + rumbleMgr->state = RUMBLE_STATE_CLEAR; + } + } + } else { + // Clear all requests + for (i = 0; i < RUMBLE_MAX_REQUESTS; i++) { + rumbleMgr->reqAccumulators[i] = 0; + rumbleMgr->reqDecreaseRates[i] = 0; + rumbleMgr->reqDurations[i] = 0; + rumbleMgr->reqStrengths[i] = 0; + } + + // Clear override request + rumbleMgr->onTimer = rumbleMgr->offTimer = rumbleMgr->overrideStrength = rumbleMgr->overrideDuration = + rumbleMgr->overrideDecreaseRate = rumbleMgr->overrideAccumulator = 0; + } +} + +void RumbleMgr_Init(RumbleMgr* rumbleMgr) { + bzero(rumbleMgr, sizeof(RumbleMgr)); + rumbleMgr->state = RUMBLE_STATE_RESET; + rumbleMgr->updateEnabled = true; +} + +void RumbleMgr_Destroy(RumbleMgr* rumbleMgr) { + bzero(rumbleMgr, sizeof(RumbleMgr)); +} diff --git a/src/code/z_actor.c b/src/code/z_actor.c index 5e331cbe4..ccffed09b 100644 --- a/src/code/z_actor.c +++ b/src/code/z_actor.c @@ -3523,9 +3523,9 @@ void func_80033E1C(PlayState* play, s16 arg1, s16 arg2, s16 arg3) { void func_80033E88(Actor* actor, PlayState* play, s16 arg2, s16 arg3) { if (arg2 >= 5) { - func_800AA000(actor->xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + Rumble_Request(actor->xyzDistToPlayerSq, 255, 20, 150); } else { - func_800AA000(actor->xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + Rumble_Request(actor->xyzDistToPlayerSq, 180, 20, 100); } func_80033DB8(play, arg2, arg3); diff --git a/src/code/z_construct.c b/src/code/z_construct.c index c886bc73a..7375b1460 100644 --- a/src/code/z_construct.c +++ b/src/code/z_construct.c @@ -595,9 +595,11 @@ void func_80111070(void) { VREG(87) = 64; VREG(88) = 66; VREG(89) = 0; - VREG(90) = 126; - VREG(91) = 124; - VREG(92) = -63; + R_GAME_OVER_RUMBLE_STRENGTH = 126; + R_GAME_OVER_RUMBLE_DURATION = 124; + //! @bug This is eventually cast to a u8 after some scaling in `GameOver_Update`, negative numbers typically + //! become large (fast) decrease rates + R_GAME_OVER_RUMBLE_DECREASE_RATE = -63; } void func_80112098(PlayState* play) { diff --git a/src/code/z_debug.c b/src/code/z_debug.c index f268da0d3..45e8dc4df 100644 --- a/src/code/z_debug.c +++ b/src/code/z_debug.c @@ -229,7 +229,7 @@ void func_8006390C(Input* input) { if (iREG(0)) { iREG(0) = 0; - func_800AA000(0, iREG(1), iREG(2), iREG(3)); + Rumble_Request(0.0f, iREG(1), iREG(2), iREG(3)); } break; diff --git a/src/code/z_demo.c b/src/code/z_demo.c index c01ee8408..e1c4f7db3 100644 --- a/src/code/z_demo.c +++ b/src/code/z_demo.c @@ -465,7 +465,7 @@ void Cutscene_Command_FadeBGM(PlayState* play, CutsceneContext* csCtx, CsCmdMusi // Command 9: ? void Cutscene_Command_09(PlayState* play, CutsceneContext* csCtx, CsCmdUnknown9* cmd) { if (csCtx->frames == cmd->startFrame) { - func_800AA000(0.0f, cmd->unk_06, cmd->unk_07, cmd->unk_08); + Rumble_Request(0.0f, cmd->unk_06, cmd->unk_07, cmd->unk_08); } } diff --git a/src/code/z_game_over.c b/src/code/z_game_over.c index 388b7ec29..5f72a5aa8 100644 --- a/src/code/z_game_over.c +++ b/src/code/z_game_over.c @@ -20,9 +20,9 @@ void GameOver_Update(PlayState* play) { GameOverContext* gameOverCtx = &play->gameOverCtx; s16 i; s16 j; - s32 v90; - s32 v91; - s32 v92; + s32 rumbleStrength; + s32 rumbleDuration; + s32 rumbleDecreaseRate; switch (gameOverCtx->state) { case GAMEOVER_DEATH_START: @@ -74,13 +74,15 @@ void GameOver_Update(PlayState* play) { Environment_InitGameOverLights(play); gGameOverTimer = 20; + if (1) {} - v90 = VREG(90); - v91 = VREG(91); - v92 = VREG(92); + rumbleStrength = R_GAME_OVER_RUMBLE_STRENGTH; + rumbleDuration = R_GAME_OVER_RUMBLE_DURATION; + rumbleDecreaseRate = R_GAME_OVER_RUMBLE_DECREASE_RATE; - func_800AA000(0.0f, ((v90 > 0x64) ? 0xFF : (v90 * 0xFF) / 0x64), (CLAMP_MAX(v91 * 3, 0xFF)), - ((v92 > 0x64) ? 0xFF : (v92 * 0xFF) / 0x64)); + Rumble_Request(0.0f, ((rumbleStrength > 100) ? 255 : (rumbleStrength * 255) / 100), + (CLAMP_MAX(rumbleDuration * 3, 255)), + ((rumbleDecreaseRate > 100) ? 255 : (rumbleDecreaseRate * 255) / 100)); gameOverCtx->state = GAMEOVER_DEATH_WAIT_GROUND; break; @@ -94,7 +96,7 @@ void GameOver_Update(PlayState* play) { if (gGameOverTimer == 0) { play->pauseCtx.state = 8; gameOverCtx->state++; - func_800AA15C(); + Rumble_Reset(); } break; @@ -108,14 +110,15 @@ void GameOver_Update(PlayState* play) { case GAMEOVER_REVIVE_RUMBLE: gGameOverTimer = 50; gameOverCtx->state++; - if (1) {} - v90 = VREG(90); - v91 = VREG(91); - v92 = VREG(92); + if (1) {} + rumbleStrength = R_GAME_OVER_RUMBLE_STRENGTH; + rumbleDuration = R_GAME_OVER_RUMBLE_DURATION; + rumbleDecreaseRate = R_GAME_OVER_RUMBLE_DECREASE_RATE; - func_800AA000(0.0f, ((v90 > 0x64) ? 0xFF : (v90 * 0xFF) / 0x64), (CLAMP_MAX(v91 * 3, 0xFF)), - ((v92 > 0x64) ? 0xFF : (v92 * 0xFF) / 0x64)); + Rumble_Request(0.0f, ((rumbleStrength > 100) ? 255 : (rumbleStrength * 255) / 100), + (CLAMP_MAX(rumbleDuration * 3, 255)), + ((rumbleDecreaseRate > 100) ? 255 : (rumbleDecreaseRate * 255) / 100)); break; case GAMEOVER_REVIVE_WAIT_GROUND: diff --git a/src/code/z_kankyo.c b/src/code/z_kankyo.c index 3eef3457a..ec4941255 100644 --- a/src/code/z_kankyo.c +++ b/src/code/z_kankyo.c @@ -437,7 +437,7 @@ void Environment_Init(PlayState* play2, EnvironmentContext* envCtx, s32 unused) } gCustomLensFlareOn = false; - func_800AA15C(); + Rumble_Reset(); } u8 Environment_SmoothStepToU8(u8* pvalue, u8 target, u8 scale, u8 step, u8 minStep) { @@ -883,7 +883,7 @@ void Environment_Update(PlayState* play, EnvironmentContext* envCtx, LightContex if ((((void)0, gSaveContext.gameMode) != GAMEMODE_NORMAL) && (((void)0, gSaveContext.gameMode) != GAMEMODE_END_CREDITS)) { - func_800AA16C(); + Rumble_ClearRequests(); } if (pauseCtx->state == 0) { diff --git a/src/code/z_play.c b/src/code/z_play.c index b191dd1cf..f501a93cf 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -852,7 +852,7 @@ void Play_Update(PlayState* this) { PLAY_LOG(3580); this->gameplayFrames++; - func_800AA178(1); + Rumble_SetUpdateEnabled(true); if (this->actorCtx.freezeFlashTimer && (this->actorCtx.freezeFlashTimer-- < 5)) { osSyncPrintf("FINISH=%d\n", this->actorCtx.freezeFlashTimer); @@ -902,7 +902,7 @@ void Play_Update(PlayState* this) { PLAY_LOG(3662); } } else { - func_800AA178(0); + Rumble_SetUpdateEnabled(false); } PLAY_LOG(3672); diff --git a/src/code/z_rumble.c b/src/code/z_rumble.c new file mode 100644 index 000000000..49a54e7aa --- /dev/null +++ b/src/code/z_rumble.c @@ -0,0 +1,135 @@ +/** + * @file z_rumble.c + * + * This file implements an interface for the game state to set up, manage and request use of the rumble pak. Despite + * some parts of the system appearing to accommodate all four controller ports, only controller 1 can be instructed + * to rumble. + * This file is half of the system that runs on the graph thread alongside the game state. The rest of the system that + * processes the requests runs on the padmgr thread and is implemented in `sys_rumble.c`. + * + * @see sys_rumble.c + * + * @note Original filename is likely z_vibrate.c or similar as it is ordered after z_ss_sram.c and before z_view.c + */ +#include "global.h" + +RumbleMgr sRumbleMgr; + +/** + * Padmgr callback to update the state of rumble on Vertical Retrace. + * + * Unlike every other function in this file, this runs on the padmgr thread. + */ +void Rumble_Update(PadMgr* padMgr, void* arg) { + RumbleMgr_Update(&sRumbleMgr); + PadMgr_RumbleSet(padMgr, sRumbleMgr.rumbleEnable); +} + +/** + * Forces the rumble state to use the supplied parameters. + * The parameters are the same as in `Rumble_Request`. + * + * @see Rumble_Request + */ +void Rumble_Override(f32 distSq, u8 sourceStrength, u8 duration, u8 decreaseRate) { + s32 dist; + s32 strength; + + if (distSq > SQ(1000)) { + dist = 1000; + } else { + dist = sqrtf(distSq); + } + + if (dist < 1000 && sourceStrength != 0 && decreaseRate != 0) { + // Decrease the strength linearly with distance + strength = sourceStrength - (dist * 255) / 1000; + + if (strength > 0) { + // Note: sRumbleMgr is a shared resource between the graph and padmgr threads, no locking is done + // to ensure that the entire request is written before it is possibly used. + sRumbleMgr.overrideStrength = strength; + sRumbleMgr.overrideDuration = duration; + sRumbleMgr.overrideDecreaseRate = decreaseRate; + } + } +} + +/** + * Submits a request to the rumble manager with the properties given in the arguments. If there is no free request slot + * the request is silently dropped. + * + * @param distSq + * Squared distance, usually taken to be from an apparent source to the player in world coordinates. + * @param sourceStrength + * The strength of the rumble at 0 distance from the source. + * The rumble source strength decreases linearly with distance, a distance of 0 results in the full source strength + * while a distance of 1000 or greater is discarded. A source strength of 0 is discarded. A minimum source strength + * of 1 drops to 0 at 3 units of distance from the source. A maximum source strength of 255 drops to 0 at 1000 + * units of distance from the source. + * Note that, once the request has been submitted, if the distance to the source changes in subsequent frames while + * the rumble request is still running, the request will not be updated with the new distance. + * @param duration + * The duration for which the rumble will sustain full strength. It is measured in Vertical Retraces rather than + * game frames. There are ~60 Retraces/s on NTSC and 50 Retraces/s on PAL. + * @param decreaseRate + * The amount by which to lower the strength every Vertical Retrace once duration has hit 0. + */ +void Rumble_Request(f32 distSq, u8 sourceStrength, u8 duration, u8 decreaseRate) { + s32 dist; + s32 strength; + s32 i; + + if (distSq > SQ(1000)) { + dist = 1000; + } else { + dist = sqrtf(distSq); + } + + if (dist < 1000 && sourceStrength != 0 && decreaseRate != 0) { + // Decrease the strength linearly with distance + strength = sourceStrength - (dist * 255) / 1000; + + for (i = 0; i < RUMBLE_MAX_REQUESTS; i++) { + // Search for an empty slot + if (sRumbleMgr.reqStrengths[i] == 0) { + if (strength > 0) { + // Note: sRumbleMgr is a shared resource between the graph and padmgr threads, no locking is done + // to ensure that the entire request is written before it is possibly used. + sRumbleMgr.reqStrengths[i] = strength; + sRumbleMgr.reqDurations[i] = duration; + sRumbleMgr.reqDecreaseRates[i] = decreaseRate; + } + break; + } + } + } +} + +void Rumble_Init(void) { + RumbleMgr_Init(&sRumbleMgr); + PADMGR_SET_RETRACE_CALLACK(&gPadMgr, Rumble_Update, NULL); +} + +void Rumble_Destroy(void) { + PadMgr* padmgr = &gPadMgr; + + PADMGR_UNSET_RETRACE_CALLACK(padmgr, Rumble_Update, NULL); + RumbleMgr_Destroy(&sRumbleMgr); +} + +s32 Rumble_Controller1HasRumblePak(void) { + return gPadMgr.pakType[0] == CONT_PAK_RUMBLE; +} + +void Rumble_Reset(void) { + sRumbleMgr.state = RUMBLE_STATE_RESET; +} + +void Rumble_ClearRequests(void) { + sRumbleMgr.state = RUMBLE_STATE_CLEAR; +} + +void Rumble_SetUpdateEnabled(u32 enable) { + sRumbleMgr.updateEnabled = !!enable; +} |
