/* * UNPUBLISHED -- Rights reserved under the copyright laws of the * United States. Use of a copyright notice is precautionary only and * does not imply publication or disclosure. * * THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION * OF VICARIOUS VISIONS, INC. ANY DUPLICATION, MODIFICATION, * DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR * EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC. */ #include "../cgame/cg_local.h" #include "../server/exe_headers.h" #include "win_local.h" #include "win_input.h" //MB #include "../client/cl_data.h" #include "../game/q_shared.h" extern qboolean G_ActivePlayerNormal(void); struct rumblestate_t { int timeToStop; // Right motor speed on Xbox, action type on Gamecube int arg1; // Left motor speed on Xbox, secondary action type on Gamecube int arg2; }; struct rumblestate_special_t { int code; int arg1; int arg2; }; struct rumblescript_t { int nextStateAt; int controller; int currentState; int usedStates; int numStates; bool autoDelete; rumblestate_t *states; }; struct rumblestatus_t { bool changed; bool killed; bool paused; int timePaused; }; #define MAX_RUMBLE_STATES 10 #define MAX_RUMBLE_SCRIPTS 10 #define MAX_RUMBLE_CONTROLLERS 4 // In rumblestate, highest speed for each side takes precidence // Number of rumble states is fairly small, so a plain array will work fine static rumblestatus_t rumbleStatus[MAX_RUMBLE_CONTROLLERS]; static rumblescript_t rumbleScripts[MAX_RUMBLE_SCRIPTS]; cvar_t* in_useRumble = NULL; /***** FIXME Some functions that would be found in a client manager *****/ /***** BEGIN FILLER *****/ // Always return 0 because we have only one client (right now anyway) int ActiveClientNum(void) { return 0; } // The active controller will always be number 0 for now int ActiveController(void) { return 0; } /***** END FILLER *****/ void IN_enableRumble( void ) { if (ActiveClientNum() == 0) { Cvar_Set( "in_useRumble", "1"); } else { Cvar_Set( "in_useRumble2", "1"); } } void IN_disableRumble( void ) { if (ActiveClientNum() == 0) { Cvar_Set( "in_useRumble", "0"); } else { Cvar_Set( "in_useRumble2", "0"); } } bool IN_usingRumble( void ) { if (ActiveClientNum() == 0) { return Cvar_VariableIntegerValue( "in_useRumble"); } else { return Cvar_VariableIntegerValue( "in_useRumble2"); } return true; } // Creates a rumble script with numStates // Returns -1 on no more room, otherwise an identifier to use for scripts int IN_CreateRumbleScript(int controller, int numStates, bool deleteWhenFinished) { if (!IN_usingRumble()) return -1; if (controller <= -1 || controller >= MAX_RUMBLE_CONTROLLERS) return -1; assert (numStates > 0 && numStates < MAX_RUMBLE_STATES); int i; for (i = 0; i < MAX_RUMBLE_SCRIPTS; i++) { if (rumbleScripts[i].states == 0) break; } if (i == MAX_RUMBLE_SCRIPTS) return -1; // Ran out of scripts rumbleScripts[i].autoDelete = deleteWhenFinished; rumbleScripts[i].controller = controller; rumbleScripts[i].currentState = 0; rumbleScripts[i].nextStateAt = 0; rumbleScripts[i].numStates = numStates; rumbleScripts[i].usedStates = 0; rumbleScripts[i].states = new rumblestate_t[numStates]; memset(rumbleScripts[i].states, 0, sizeof(rumblestate_t) * numStates); return i; } // A negative time will last until you kill it explicitly // Returns index, used to kill or change a state in a script int IN_AddRumbleStateFull(int whichScript, int arg1, int arg2, int timeInMs) { if (!IN_usingRumble()) return -1; assert(whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); assert(rumbleScripts[whichScript].usedStates < rumbleScripts[whichScript].numStates); // Get the current state rumblescript_t *curScript = &rumbleScripts[whichScript]; rumblestate_t *curState = &curScript->states[curScript->usedStates]; curState->arg1 = arg1; curState->arg2 = arg2; curState->timeToStop = timeInMs; return curScript->usedStates++; } int IN_AddRumbleState(int whichScript, int leftSpeed, int rightSpeed, int timeInMs) { return IN_AddRumbleStateFull(whichScript, leftSpeed, rightSpeed, timeInMs); } int IN_AddRumbleStateSpecial(int whichScript, int action, int arg1, int arg2) { if (!IN_usingRumble()) return -1; assert(whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); assert(rumbleScripts[whichScript].usedStates < rumbleScripts[whichScript].numStates); // Get the current state rumblescript_t *curScript = &rumbleScripts[whichScript]; rumblestate_special_t *curState = (rumblestate_special_t*)&curScript->states[curScript->usedStates]; curState->code = action; curState->arg1 = arg1; curState->arg2 = arg2; return curScript->usedStates++; } int IN_AddEffectFade4(int whichScript, int startLeft, int startRight, int endLeft, int endRight, int timeInMs) { const int fadeSmoothness = 50; // number of ms between updates, smaller is smoother int e = IN_AddRumbleState(whichScript, startLeft, startRight, fadeSmoothness); // Lasts for fadeSmoothness ms if (startLeft < endLeft) // Fade increases { IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_LEFT, e, (endLeft - startLeft) * fadeSmoothness / timeInMs); } else { IN_AddRumbleStateSpecial(whichScript, IN_CMD_DEC_LEFT, e, (startLeft - endLeft) * fadeSmoothness / timeInMs); } if (startRight < endRight) { IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_RIGHT, e, (endRight - startRight) * fadeSmoothness / timeInMs); } else { IN_AddRumbleStateSpecial(whichScript, IN_CMD_DEC_RIGHT, e, (startRight - endRight) * fadeSmoothness / timeInMs); } return IN_AddRumbleStateSpecial(whichScript, IN_CMD_GOTO_XTIMES, e, timeInMs / fadeSmoothness); } int IN_AddEffectFadeExp6(int whichScript, int startLeft, int startRight, int endLeft, int endRight, char factor, int timeInMs) { const int fadeSmoothness = 10; // number of ms between updates, smaller is smoother int state = IN_AddRumbleState(whichScript, startLeft, startRight, fadeSmoothness); // Lasts for fadeSmoothness ms if (startLeft < endLeft) // Fade increases { IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_LEFT, state, (endLeft - startLeft) * fadeSmoothness / timeInMs - (factor / 2) * ( 1 - timeInMs / fadeSmoothness) ); } else { IN_AddRumbleStateSpecial(whichScript, IN_CMD_DEC_LEFT, state, (startLeft - endLeft) * fadeSmoothness / timeInMs - (factor / 2) * ( 1 - timeInMs / fadeSmoothness) ); } if (startRight < endRight) { IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_RIGHT, state, (endRight - startRight) * fadeSmoothness / timeInMs - (factor / 2) * ( 1 - timeInMs / fadeSmoothness) ); } else { IN_AddRumbleStateSpecial(whichScript, IN_CMD_DEC_RIGHT, state, (startRight - endRight) * fadeSmoothness / timeInMs - (factor / 2) * ( 1 - timeInMs / fadeSmoothness) ); } IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_ARG2, state + 1, factor); IN_AddRumbleStateSpecial(whichScript, IN_CMD_INC_ARG2, state + 2, factor); return IN_AddRumbleStateSpecial(whichScript, IN_CMD_GOTO_XTIMES, state, timeInMs / fadeSmoothness); } // Kills a rumble state based on index void IN_KillRumbleState(int whichScript, int index) { if (!IN_usingRumble()) return; assert( whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); assert( index < rumbleScripts[whichScript].numStates ); rumbleScripts[whichScript].states[index].timeToStop = 0; rumbleStatus[rumbleScripts[whichScript].controller].changed = true; } // Stops the script, if script has autodelete on then it will get deleted, otherwise it will only stop void IN_KillRumbleScript(int whichScript) { if (!IN_usingRumble()) return; assert (whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); rumbleScripts[whichScript].nextStateAt = 0; if (rumbleScripts[whichScript].autoDelete) { if (rumbleScripts[whichScript].states) delete [] rumbleScripts[whichScript].states; rumbleScripts[whichScript].states = 0; } rumbleStatus[rumbleScripts[whichScript].controller].changed = true; } // Stops Rumbling for specific controller void IN_KillRumbleScripts(int controller) { if (!IN_usingRumble()) return; if (controller <= -1 || controller >= MAX_RUMBLE_CONTROLLERS) return; if (rumbleStatus[controller].killed == true) return; for (int i = 0; i < MAX_RUMBLE_SCRIPTS; i++) { if (rumbleScripts[i].controller == controller) IN_KillRumbleScript(i); } rumbleStatus[controller].killed = IN_RumbleAdjust(controller, 0, 0); } // Stops Rumbling on all controllers void IN_KillRumbleScripts( void ) { if (!IN_usingRumble()) return; for (int i = 0; i < MAX_RUMBLE_SCRIPTS; i++) IN_KillRumbleScript(i); for (int j = 0; j < MAX_RUMBLE_CONTROLLERS; j++) { if (!rumbleStatus[j].killed) { rumbleStatus[j].killed = IN_RumbleAdjust(j, 0, 0); } } } void IN_DeleteRumbleScript(int whichScript) { if (!IN_usingRumble()) return; assert (whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); if (rumbleScripts[whichScript].states) delete [] rumbleScripts[whichScript].states; rumbleScripts[whichScript].nextStateAt = 0; rumbleScripts[whichScript].states = 0; rumbleStatus[rumbleScripts[whichScript].controller].changed = true; } int IN_RunSpecialScript(int whichScript) { rumblestate_special_t *sp = (rumblestate_special_t*)&rumbleScripts[whichScript].states[rumbleScripts[whichScript].currentState]; switch (sp->code) { // updates the current state pointer // uses arg1 case IN_CMD_GOTO: rumbleScripts[whichScript].currentState = sp->arg1; return rumbleScripts[whichScript].states[sp->arg1].timeToStop; break; // does a goto, and decreases count of arg2, until 0 case IN_CMD_GOTO_XTIMES: if (--sp->arg2 >= 0) { rumbleScripts[whichScript].currentState = sp->arg1; return rumbleScripts[whichScript].states[sp->arg1].timeToStop; } else // Go onto next cmd { if (!IN_AdvanceToNextState(whichScript)) return -2; // Done return -1; } break; // Decreasae Arg2 of a State, sp->arg1 = state, sp->arg2 = amount to decrease arg2 of state by case IN_CMD_DEC_ARG2: { rumblestate_special_t *temp = (rumblestate_special_t*)&rumbleScripts[whichScript].states[sp->arg1]; temp->arg2 -= sp->arg2; } break; // Increase Arg2 of a State, sp->arg1 = state, sp->arg2 = amount to increase arg2 of state by case IN_CMD_INC_ARG2: { rumblestate_special_t *temp = (rumblestate_special_t*)&rumbleScripts[whichScript].states[sp->arg1]; temp->arg2 += sp->arg2; } break; // Decreasae Arg1 of a State, sp->arg1 = state, sp->arg2 = amount to decrease arg1 of state by case IN_CMD_DEC_ARG1: { rumblestate_special_t *temp = (rumblestate_special_t*)&rumbleScripts[whichScript].states[sp->arg1]; temp->arg1 -= sp->arg2; } break; // Increase Arg2 of a State, sp->arg1 = state, sp->arg2 = amount to increase arg1 of state by case IN_CMD_INC_ARG1: { rumblestate_special_t *temp = (rumblestate_special_t*)&rumbleScripts[whichScript].states[sp->arg1]; temp->arg1 += sp->arg2; } break; case IN_CMD_DEC_LEFT: rumbleScripts[whichScript].states[sp->arg1].arg2 -= sp->arg2; if (rumbleScripts[whichScript].states[sp->arg1].arg2 < 0) rumbleScripts[whichScript].states[sp->arg1].arg2 = 0; if (rumbleScripts[whichScript].currentState >= rumbleScripts[whichScript].usedStates - 1) return -2; // Done return rumbleScripts[whichScript].states[++rumbleScripts[whichScript].currentState].timeToStop; break; case IN_CMD_DEC_RIGHT: rumbleScripts[whichScript].states[sp->arg1].arg1 -= sp->arg2; if (rumbleScripts[whichScript].states[sp->arg1].arg1 < 0) rumbleScripts[whichScript].states[sp->arg1].arg1 = 0; if (rumbleScripts[whichScript].currentState >= rumbleScripts[whichScript].usedStates - 1) return -2; // Done return rumbleScripts[whichScript].states[++rumbleScripts[whichScript].currentState].timeToStop; break; case IN_CMD_INC_LEFT: rumbleScripts[whichScript].states[sp->arg1].arg2 += sp->arg2; if (rumbleScripts[whichScript].states[sp->arg1].arg2 > 65534) rumbleScripts[whichScript].states[sp->arg1].arg2 = 65534; if (rumbleScripts[whichScript].currentState >= rumbleScripts[whichScript].usedStates - 1) return -2; // Done return rumbleScripts[whichScript].states[++rumbleScripts[whichScript].currentState].timeToStop; break; case IN_CMD_INC_RIGHT: rumbleScripts[whichScript].states[sp->arg1].arg1 += sp->arg2; if (rumbleScripts[whichScript].states[sp->arg1].arg1 > 65534) rumbleScripts[whichScript].states[sp->arg1].arg1 = 65534; if (rumbleScripts[whichScript].currentState >= rumbleScripts[whichScript].usedStates - 1) return -2; // Done return rumbleScripts[whichScript].states[++rumbleScripts[whichScript].currentState].timeToStop; break; } return 0; } int IN_Time() { //mb return ClientManager::ActiveClient().cg.time; return cg.time; } void IN_ExecuteRumbleScript(int whichScript) { if (!IN_usingRumble()) return; assert (whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS); // Can't execute an empty script??? assert (rumbleScripts[whichScript].usedStates > 0); rumbleScripts[whichScript].currentState = 0; int cmd = rumbleScripts[whichScript].states[rumbleScripts[whichScript].currentState].timeToStop; if (cmd < 0) { cmd = IN_RunSpecialScript(whichScript); } rumbleScripts[whichScript].nextStateAt = IN_Time() + cmd; rumbleStatus[rumbleScripts[whichScript].controller].changed = true; rumbleStatus[rumbleScripts[whichScript].controller].killed = false; } void IN_PauseRumbling(int controller) { if (!IN_usingRumble()) return; if (controller <= -1 || controller >= MAX_RUMBLE_CONTROLLERS) return; if (rumbleStatus[controller].paused == true) return; rumbleStatus[controller].timePaused = IN_Time(); rumbleStatus[controller].paused = IN_RumbleAdjust(controller, 0, 0); } void IN_UnPauseRumbling(int controller) { if (!IN_usingRumble()) return; if (controller <= -1 || controller >= MAX_RUMBLE_CONTROLLERS) return; // can't unpause a control that wasn't paused if (rumbleStatus[controller].paused == false) return; int cur_time = IN_Time(); for (int i = 0; i < MAX_RUMBLE_SCRIPTS; i++) { if (rumbleScripts[i].controller == controller) { if (rumbleScripts[i].nextStateAt == 0) continue; // update the time to stop based on how long it was paused rumbleScripts[i].nextStateAt += (cur_time - rumbleStatus[controller].timePaused); } } rumbleStatus[controller].paused = false; rumbleStatus[controller].changed = true; rumbleStatus[controller].killed = false; } void IN_TogglePauseRumbling(int controller) { if (!IN_usingRumble()) return; if (controller <= -1 || controller >= MAX_RUMBLE_CONTROLLERS) return; if (rumbleStatus[controller].paused) IN_UnPauseRumbling(controller); else IN_PauseRumbling(controller); } // Pauses rumbling on all controllers void IN_PauseRumbling( void ) { if (!IN_usingRumble()) return; for (int i = 0; i < MAX_RUMBLE_CONTROLLERS; i++) IN_PauseRumbling(i); } // UnPauses rumbling on all controllers void IN_UnPauseRumbling( void ) { if (!IN_usingRumble()) return; for (int i = 0; i < MAX_RUMBLE_CONTROLLERS; i++) IN_UnPauseRumbling(i); } // Toggles Pausing on all controllers void IN_TogglePauseRumbling( void ) { if (!IN_usingRumble()) return; for (int i = 0; i < MAX_RUMBLE_CONTROLLERS; i++) IN_TogglePauseRumbling(i); } // Returns false when the end of the script is reached bool IN_AdvanceToNextState(int whichScript) { assert( whichScript >= 0 && whichScript < MAX_RUMBLE_SCRIPTS ); if (rumbleScripts[whichScript].currentState >= rumbleScripts[whichScript].usedStates - 1) { // Script is at its end, so kill it( which deletes only if autodelete IN_KillRumbleScript(whichScript); return false; } // Advance a state rumbleScripts[whichScript].currentState++; int cmd = rumbleScripts[whichScript].states[rumbleScripts[whichScript].currentState].timeToStop; while (cmd < 0) { cmd = IN_RunSpecialScript(whichScript); if (cmd == -1) return true; if (cmd == -2) return false; } rumbleScripts[whichScript].nextStateAt = IN_Time() + cmd; return true; } // Max rumble takes precidence // Other possibility is some kind of sum of all the speeds // Call this once a frame, to update the controller based on the rumble states void IN_UpdateRumbleFromStates() { //if (!IN_usingRumble()) return; /*mb extern int G_ShouldBeRumbling(); if (!G_ShouldBeRumbling()) return; */ int usingRumble[2]; usingRumble[0] = Cvar_VariableIntegerValue("in_useRumble"); usingRumble[1] = Cvar_VariableIntegerValue("in_useRumble2"); int i; int value[MAX_RUMBLE_CONTROLLERS][2]; int cur_time = IN_Time(); memset(value, 0, sizeof(int)*MAX_RUMBLE_CONTROLLERS*2); for (i = 0; i < MAX_RUMBLE_SCRIPTS; i++) { // If rumble is paused on current controller than skip this rumble state if ( rumbleStatus[rumbleScripts[i].controller].paused) continue; //*mb ClientManager::ActivateByControllerId(rumbleScripts[i].controller); if ( !usingRumble[ActiveClientNum()] ) { IN_KillRumbleScript(i); continue; } /*mb if (!ClientManager::ActiveGentity() || !G_ActivePlayerNormal()) { IN_KillRumbleScript(i); continue; } */ // Unset state so skip if ( rumbleScripts[i].nextStateAt == 0) continue; // Time is up on this rumble state if ( rumbleScripts[i].nextStateAt < cur_time) { // If timeToStop is < cur_time and > 0 then end this state otherwise (negative number) always rumble if (rumbleScripts[i].nextStateAt > 0) { rumbleStatus[rumbleScripts[i].controller].changed = true; rumbleStatus[rumbleScripts[i].controller].killed = false; if (!IN_AdvanceToNextState(i)) // Returns false if reached the end of script continue; } } rumblescript_t *curScript = &rumbleScripts[i]; if (value[curScript->controller][0] < curScript->states[curScript->currentState].arg2) value[curScript->controller][0] = curScript->states[curScript->currentState].arg2; if (value[curScript->controller][1] < curScript->states[curScript->currentState].arg1) value[curScript->controller][1] = curScript->states[curScript->currentState].arg1; } // Go through the 4 controller ports for (i = 0; i < MAX_RUMBLE_CONTROLLERS; i++) { // paused, so do nothing for this controller if ( rumbleStatus[i].paused) continue; // Only update the actual hardware if a state has changed if (!rumbleStatus[i].changed) continue; IN_RumbleAdjust(i, value[i][0], value[i][1]); // State has changed rumbleStatus[i].changed = false; } } /* ================== IN_RumbleInit ================== */ void IN_RumbleInit (void) { memset(&rumbleStatus, 0, sizeof(rumblestatus_t)*MAX_RUMBLE_CONTROLLERS); memset(&rumbleScripts, 0, sizeof(rumblescript_t)*MAX_RUMBLE_SCRIPTS); in_useRumble = Cvar_Get( "in_useRumble", "1", 0 ); Cvar_Get("in_useRumble2", "1", 0); } /* ================== IN_RumbleShutdown ================== */ void IN_RumbleShutdown (void) { for (int i = 0; i < MAX_RUMBLE_SCRIPTS; i++) { if (rumbleScripts[i].states) delete [] rumbleScripts[i].states; rumbleScripts[i].states = 0; rumbleScripts[i].nextStateAt = 0; } } /* ================== IN_RumbleFrame ================== */ void IN_RumbleFrame (void) { // Check to see if we need to pause rumbling if(cl_paused->integer && !rumbleStatus[IN_GetMainController()].paused) { IN_PauseRumbling(IN_GetMainController()); } else if(!cl_paused->integer && rumbleStatus[IN_GetMainController()].paused) { IN_UnPauseRumbling(IN_GetMainController()); } // Update the states IN_UpdateRumbleFromStates(); }