summaryrefslogtreecommitdiff
path: root/src/code/sys_rumble.c
diff options
context:
space:
mode:
authorTharo <17233964+Thar0@users.noreply.github.com>2022-09-27 17:40:26 +0100
committerGitHub <noreply@github.com>2022-09-27 12:40:26 -0400
commit4f65d08eb5826c9c3520fd398b2e76ab09168193 (patch)
treed7ea67a3b14cf7387d23a604d99e3ee0e7f77467 /src/code/sys_rumble.c
parent82e04ede5f3fef20fb95f6a16b88b8a684f49954 (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/sys_rumble.c')
-rw-r--r--src/code/sys_rumble.c151
1 files changed, 151 insertions, 0 deletions
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));
+}