mirror of
https://github.com/zeldaret/oot.git
synced 2024-12-02 15:55:59 +00:00
152 lines
5.7 KiB
C
152 lines
5.7 KiB
C
|
/**
|
||
|
* @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));
|
||
|
}
|