/** * @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; }