//=========================================================================== // Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved. // // Component: UserController // // Description: Implementation of the usercontroller class for win32. // // History: Branched from the console UserController class. // //=========================================================================== //======================================== // System Includes //======================================== #include #include #include #define DIRECTINPUT_VERSION 0x0800 #include #include //======================================== // Project Includes //======================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WORLD_BUILDER #include
#endif #include #include // needed for the mouse reset hack. static int maxes[] = { DIMOFS_X, DIMOFS_Y, DIMOFS_Z }; #ifdef WORLD_BUILDER #ifdef CONTROLLER_DEBUG #undef CONTROLLER_DEBUG #endif enum { CLO_NO_HAPTIC, CLO_RANDOM_BUTTONS }; namespace CommandLineOptions { static bool Get( int i ) { return false; }; }; #endif //This enables the random button pressing. #define CONTROLLER_DEBUG #ifdef CONTROLLER_DEBUG #define MAX_BUTTON_TIME 3000 #define MAX_BUTTON_PRESSES 8 struct RandomButton { RandomButton() : mRandomButtonEnable( false ), mRandomButtonTiming( 0 ), mRadomizeButtonTiming( false ), mButtonTimeout( 0 ), mMaxButtons( MAX_BUTTON_PRESSES ), mCurrentButton( 0 ) { int i; for ( i = 0; i < MAX_BUTTON_PRESSES; ++i ) { mLastButton[ i ] = -1; } } bool mRandomButtonEnable; unsigned int mRandomButtonTiming; bool mRadomizeButtonTiming; int mButtonTimeout; int mLastButton[ MAX_BUTTON_PRESSES ]; unsigned int mMaxButtons; unsigned int mCurrentButton; }; RandomButton gRandomButtoners[ Input::MaxControllers ]; #endif //============================================================================= // Class: InputCode //============================================================================= // // Description: Need this for compressing ( Controller / directX key code / // direction ) triplets into one integer. Then we can re-use the // Mapper class for virtual maps. // //============================================================================= class InputCode { public: InputCode( unsigned int inputcode ) { mInputCode = inputcode; } InputCode( eControllerType controller, int dxKey, eDirectionType direction ) { mInputCode = dxKey | (direction << 24) | (controller << 28); rAssert( controller == GetController() ); rAssert( dxKey == GetDxKeyCode() ); rAssert( direction == GetDirection() ); } eControllerType GetController() const { rAssert( (mInputCode >> 28) < NUM_CONTROLLERTYPES ); return eControllerType( mInputCode >> 28 ); } int GetDxKeyCode() const { return mInputCode & 0xFFFFFF; } eDirectionType GetDirection() const { rAssert( ((mInputCode >> 24) & 0xF) <= NUM_DIRTYPES ); return eDirectionType( (mInputCode >> 24) & 0xF ); } int GetInputCode() const { return mInputCode; } private: unsigned int mInputCode; }; //****************************************************************************** // // Constructors, Destructors and Operators // //****************************************************************************** UserController::UserController( ) : m_controllerId( -1 ), mIsConnected( false ), mGameState( Input::ACTIVE_ALL ), mbInputPointsRegistered( false ), mKeyboardBack( false ), mbIsRumbleOn( false ), m_bIsWheel( false ), m_bTutorialDisabled(true) { int i = 0; for ( i = 0; i < Input::MaxMappables; i++ ) { mMappable[ i ] = 0; } for( i = 0; i < 3; i++ ) { mResetMouseCounter[ i ] = 0; } // Set up the virtual buttons. mNumButtons = VirtualInputs::GetNumber(); rAssert(mNumButtons < Input::MaxPhysicalButtons); for( i = 0; i < mNumButtons; i++ ) { // Define the button names for the virtual keys mButtonNames[ i ] = radMakeKey( VirtualInputs::GetName( i ) ); mButtonDeadZones[ i ] = 0.10f; mButtonSticky[ i ] = false; } // Set up the controllers for each type. m_pController[GAMEPAD] = new Gamepad(); m_pController[KEYBOARD] = new Keyboard(); m_pController[MOUSE] = new Mouse(); m_pController[STEERINGWHEEL] = new Gamepad(); m_pSteeringSpring = new SteeringSpring; m_pSteeringDamper = new BaseDamper; m_pConstantEffect = new ConstantEffect; m_pWheelRumble = new WheelRumble; m_pHeavyWheelRumble = new WheelRumble; // // Register with the game config manager // GetGameConfigManager()->RegisterConfig( this ); } void UserController::NotifyConnect( void ) { if ( !IsConnected( ) ) { for( int i = 0; i < NUM_CONTROLLERTYPES; i++ ) { m_pController[i]->Connect(); } for ( unsigned int i = 0; i < Input::MaxMappables, this->mMappable[ i ]; i++ ) { mMappable[ i ]->OnControllerConnect( GetControllerId( ) ); } mIsConnected = true; } } void UserController::NotifyDisconnect( void ) { if ( IsConnected( ) ) { for( int i = 0; i < NUM_CONTROLLERTYPES; i++ ) { m_pController[i]->Disconnect(); } for ( unsigned int i = 0; i < Input::MaxMappables, this->mMappable[ i ]; i++ ) { mMappable[ i ]->OnControllerDisconnect( GetControllerId( ) ); } mIsConnected = false; } } bool UserController::IsConnected() const { return mIsConnected; } void UserController::Create( int id ) { m_controllerId = id; // Now that we know our controller id, // load the default controller mappings. // LoadDefaults(); } void UserController::SetGameState( unsigned state) { mGameState = state; for ( unsigned i = 0; i < Input::MaxMappables; i++ ) { if(mMappable[ i ]) { mMappable[ i ]->SetGameState( state ); // if the game state change involved and inactive mappable being activated // wwe need to reload the current button state if(mMappable[ i ]->GetResync()) { mMappable[ i ]->InitButtons( m_controllerId, mButtonArray); mMappable[ i ]->SetResync(false); } } } } void UserController::SetButtonValue( unsigned int buttonId, float value, bool sticky ) { float fLastValue = mButtonArray[ buttonId ].GetValue( ); float fDeadZone = mButtonDeadZones[ buttonId ]; if( mButtonSticky[ buttonId ] && !sticky ) { return; // don't overwrite a sticky button. } if ( rmt::Epsilon( value, 0.0f, fDeadZone ) ) { value = 0.0f; } else { //Clamp our values. value = rmt::Clamp( value, MIN_AXIS_THRESH, MAX_AXIS_THRESH ); // Recalibrate them from [deadzone..1] to [0..1]. float sign = rmt::Sign( value ); float calibratedButtonData = rmt::Fabs( value ) - fDeadZone; calibratedButtonData *= 1.0f / ( 1.0f - fDeadZone ); calibratedButtonData *= sign; bool bChanged = !( rmt::Epsilon( fLastValue, calibratedButtonData, 0.05f ) ); if ( bChanged ) { // go with the deadzone corrected value. value = calibratedButtonData; } else { // restore the old deadzone corrected value. value = fLastValue; } } if( value > 0.0f || fLastValue > 0.0f ) { mButtonArray[ buttonId ].SetValue( value ); } // Reset the sticky flag. mButtonSticky[ buttonId ] = sticky; if( sticky && value == 0.0f ) { mButtonSticky[ buttonId ] = false; } } void UserController::OnControllerInputPointChange( unsigned int code, float value ) { // Decode the virtual button code. InputCode icode( code ); eControllerType cont = icode.GetController(); int dxKey = icode.GetDxKeyCode(); // Set the IsWheel boolean if this is a steering wheel axis input m_bIsWheel = STEERINGWHEEL == cont && m_pController[ cont ]->IsInputAxis( dxKey ); // Set the keyboard back value if( KEYBOARD == cont && dxKey == DIK_ESCAPE && value > 0 ) { mKeyboardBack = true; } // Derive the directional values float dvalues[ NUM_DIRTYPES ]; int ndirections = DeriveDirectionValues( cont, dxKey, value, dvalues ); // Check if we're remapping a key. if( mMapData.MapNext ) { Remap( cont, dxKey, dvalues, ndirections ); return; } // Look up what virtual buttons have been mapped to this input, then update them. for( int map = 0; map < NUM_MAPTYPES; map++ ) { for( int dir = 0; dir < ndirections; dir++ ) { int vButton = m_pController[ cont ]->GetMap( dxKey, (eDirectionType) dir, (eMapType) map ); if( vButton != Input::INVALID_CONTROLLERID ) { SetButtonValue( vButton, dvalues[ dir ], cont == KEYBOARD ); } if( vButton == InputManager::feBack && cont != KEYBOARD ) { mKeyboardBack = false; } } } } int UserController::DeriveDirectionValues( eControllerType type, int dxKey, float value, float* values ) { // Reset the directional values values[ DIR_UP ] = values[ DIR_DOWN ] = values[ DIR_RIGHT ] = values[ DIR_LEFT ] = 0.0f; // Calculate the values based on the type of input. if( m_pController[ type ]->IsInputAxis( dxKey ) ) { // Normalize the axis. value = ( MAX_AXIS_THRESH - MIN_AXIS_THRESH ) * value + MIN_AXIS_THRESH; // Assign the values. values[ DIR_UP ] = ( value >= 0 ) ? value : 0.0f; values[ DIR_DOWN ] = ( value >= 0 ) ? 0.0f : -value; return 2; } else if( m_pController[ type ]->IsMouseAxis( dxKey ) ) { // Translate mouse val to -1..1 axis value. value = ((Mouse*)m_pController[MOUSE])->CalculateMouseAxis( dxKey, value ); // Invert if necessary if( dxKey == DIMOFS_X && m_bInvertMouseX || dxKey == DIMOFS_Y && m_bInvertMouseY ) { value *= -1.0f; } if( dxKey == DIMOFS_X ) { value *= m_fMouseSensitivityX; } else if( dxKey == DIMOFS_Y ) { value *= m_fMouseSensitivityY; } // Assign the values. values[ DIR_UP ] = ( value >= 0 ) ? value : 0.0f; values[ DIR_DOWN ] = ( value >= 0 ) ? 0.0f : -value; // Initiate the mouse reset hack for( int i = 0; i < 3; i++ ) { if( maxes[ i ] == dxKey && value != 0.0f ) { mResetMouseCounter[ i ] = 1; } } return 2; } else if( m_pController[ type ]->IsPovHat( dxKey ) ) { ((Gamepad*)m_pController[GAMEPAD])->CalculatePOV( value, &values[DIR_UP], &values[DIR_DOWN], &values[DIR_RIGHT], &values[DIR_LEFT] ); return 4; } else // It's a button. { values[ DIR_UP ] = value; return 1; } } void UserController::Initialize( RADCONTROLLER* pIController ) { // if we are being called while initialized, something is wrong rAssert(!mbInputPointsRegistered); // Initialize the controllers. // for( int i = 0; i < NUM_CONTROLLERTYPES; i++ ) { m_pController[i]->Init( pIController[i] ); } // Register the input points now. // RegisterInputPoints(); RADCONTROLLER pSteeringWheel = m_pController[STEERINGWHEEL]->getController(); static bool bSteeringSet = false; if( pSteeringWheel /*&& !bSteeringSet*/ ) { //Get the steering spring. IRadControllerOutputPoint* p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Spring" ); if ( p_OutputPoint ) { m_pSteeringSpring->Init( p_OutputPoint ); } p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Damper" ); if ( p_OutputPoint ) { m_pSteeringDamper->Init( p_OutputPoint ); } p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Constant" ); if ( p_OutputPoint ) { m_pConstantEffect->Init( p_OutputPoint ); } p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Sine Wave" ); if ( p_OutputPoint ) { m_pWheelRumble->Init( p_OutputPoint ); m_pWheelRumble->SetResetTime( 1000 ); } p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Square Wave" ); if ( p_OutputPoint ) { m_pHeavyWheelRumble->Init( p_OutputPoint ); m_pHeavyWheelRumble->SetResetTime( 1000 ); } bSteeringSet = true; } // Set up rumbling for the gamepad. // RADCONTROLLER pGamepad = m_pController[GAMEPAD]->getController(); if( pGamepad != NULL ) { IRadControllerOutputPoint* p_OutputPoint = pGamepad->GetOutputPointByName( "LeftMotor" ); if ( p_OutputPoint ) { mRumbleEffect.SetMotor( 0, p_OutputPoint ); } p_OutputPoint = pGamepad->GetOutputPointByName( "RightMotor" ); if ( p_OutputPoint ) { mRumbleEffect.SetMotor( 1, p_OutputPoint ); } } } void UserController::ReleaseRadController( void ) { // deregister input point callbacks if( mbInputPointsRegistered ) { //unregister the inputpoints associated with the controllers. int i; for( i = 0; i < NUM_CONTROLLERTYPES; i++ ) { if( m_pController[ i ] != NULL ) { m_pController[ i ]->ReleaseInputPoints( this ); } } for( i = 0; i < mNumButtons; i++ ) { mButtonArray[ i ].SetValue(0.0f); } mbInputPointsRegistered = false; } if( m_pSteeringSpring ) m_pSteeringSpring->ShutDownEffects(); if( m_pSteeringDamper ) m_pSteeringDamper->ShutDownEffects(); if( m_pConstantEffect ) m_pConstantEffect->ShutDownEffects(); if( m_pWheelRumble ) m_pWheelRumble->ShutDownEffects(); if( m_pHeavyWheelRumble ) m_pHeavyWheelRumble->ShutDownEffects(); mRumbleEffect.ShutDownEffects(); } void UserController::SetRumble( bool bRumbleOn, bool pulse ) { if ( bRumbleOn && !mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) && m_bForceFeedback ) { StartForceEffects(); } else if ( !bRumbleOn && mbIsRumbleOn && m_bForceFeedback ) { StopForceEffects(); } if ( pulse && m_bForceFeedback ) { PulseRumble(); } if ( !bRumbleOn && mbIsRumbleOn && m_bForceFeedback ) { mRumbleEffect.ShutDownEffects(); } mbIsRumbleOn = bRumbleOn; } bool UserController::IsRumbleOn( void ) const { return mbIsRumbleOn; } void UserController::PulseRumble() { mRumbleEffect.SetEffect( RumbleEffect::PULSE, 500 ); } void UserController::ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms ) { if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) ) { mRumbleEffect.SetEffect( effect, durationms ); } } void UserController::ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain ) { if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) ) { mRumbleEffect.SetDynaEffect( effect, durationms, gain ); } } void UserController::Update( unsigned timeins ) { if(!IsConnected()) return; #ifdef CONTROLLER_DEBUG if ( gRandomButtoners[ m_controllerId ].mRandomButtonEnable || CommandLineOptions::Get( CLO_RANDOM_BUTTONS ) ) { RandomButton& rb = gRandomButtoners[ m_controllerId ]; rb.mButtonTimeout -= static_cast( timeins ); if ( rb.mButtonTimeout < 0 ) { rb.mButtonTimeout = 0; } if ( rb.mButtonTimeout == 0 ) { //Reset the last button. if ( rb.mLastButton[ rb.mCurrentButton ] != -1 ) { mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 0.0f ); mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange(); for ( unsigned j = 0; j < Input::MaxMappables; j++ ) { if ( mMappable[ j ] ) { mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] ); } } } rb.mLastButton[ rb.mCurrentButton ] = rand() % Input::MaxPhysicalButtons; mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 1.0f ); mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange(); for ( unsigned j = 0; j < Input::MaxMappables; j++ ) { if ( mMappable[ j ] ) { mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] ); } } //Increment the current button rb.mCurrentButton = (rb.mCurrentButton + 1) % rb.mMaxButtons; if ( rb.mRadomizeButtonTiming ) { rb.mRandomButtonTiming = rand() % MAX_BUTTON_TIME; } rb.mButtonTimeout = rb.mRandomButtonTiming; } } #endif // Reset any mouse inputs that have gone stale. ResetMouseAxes(); // update the logical controller button values for(unsigned int i = 0; i < Input::MaxPhysicalButtons; i++) { // always rebrodcast non-zero button values (otherwise multiple physical -> single logical // button mapping will screw up in some cases) unsigned int timesincechange = mButtonArray[ i ].TimeSinceChange(); bool bIsDown = mButtonArray[ i ].IsDown(); if((timesincechange == 0) || bIsDown) { for ( unsigned j = 0; j < Input::MaxMappables; j++ ) { if ( mMappable[ j ] ) { mMappable[ j ]->DispatchOnButton( m_controllerId, i, &mButtonArray[ i ] ); } } } } m_pSteeringSpring->Update(); m_pSteeringDamper->Update(); m_pConstantEffect->Update(); m_pWheelRumble->Update(timeins); m_pHeavyWheelRumble->Update(timeins); //I leave this out so the FE can rumble me anyway. mRumbleEffect.Update( timeins ); } void UserController::StartForceEffects() { m_pSteeringSpring->Start(); m_pSteeringDamper->Start(); m_pConstantEffect->Start(); m_pWheelRumble->Start(); m_pHeavyWheelRumble->Start(); } void UserController::StopForceEffects() { m_pSteeringSpring->Stop(); m_pSteeringDamper->Stop(); m_pConstantEffect->Stop(); m_pWheelRumble->Stop(); m_pHeavyWheelRumble->Stop(); } // Returns the value stored by input point at index. float UserController::GetInputValue( unsigned int index ) const { switch( index ) { case InputManager::LeftStickX: { float up = mButtonArray[ InputManager::CameraRight ].GetValue(); float down = mButtonArray[ InputManager::CameraLeft ].GetValue(); return ( up > down ) ? up : -down; } case InputManager::LeftStickY: { float up = mButtonArray[ InputManager::CameraMoveIn ].GetValue(); float down = mButtonArray[ InputManager::CameraMoveOut ].GetValue(); return ( up > down ) ? up : -down; } case InputManager::KeyboardEsc: { return mKeyboardBack ? 1.0f : 0.0f; } default: return mButtonArray[ index ].GetValue( ); } } float UserController::GetInputValueRT( unsigned int index ) const { // not supported. return 0.0f; } Button* UserController::GetInputButton( unsigned int index ) { return &mButtonArray[ index ]; } UserController::~UserController( void ) { unsigned int i = 0; for ( i = 0; i < Input::MaxMappables; i++ ) { if( this->mMappable[ i ] != NULL ) { mMappable[ i ]->Release( ); mMappable[ i ] = 0; } } for( int i = 0; i < NUM_CONTROLLERTYPES; i++ ) { delete m_pController[i]; m_pController[i] = NULL; } delete m_pSteeringSpring; m_pSteeringSpring = NULL; delete m_pSteeringDamper; m_pSteeringDamper = NULL; delete m_pConstantEffect; m_pConstantEffect = NULL; delete m_pWheelRumble; m_pWheelRumble = NULL; delete m_pHeavyWheelRumble; m_pHeavyWheelRumble = NULL; } int UserController::RegisterMappable( Mappable *pMappable ) { // Find a slot. unsigned int i = 0; for ( i = 0; i < Input::MaxMappables; i++ ) { if( this->mMappable[ i ] == NULL ) { break; } } // Assign the mappable. if ( i < Input::MaxMappables ) { this->mMappable[ i ] = pMappable; mMappable[ i ]->AddRef( ); mMappable[ i ]->LoadControllerMappings( m_controllerId ); mMappable[ i ]->InitButtons( m_controllerId, mButtonArray); } else { rAssertMsg( false, "Not enough mappables allocated.\n" ); return -1; } // make sure the input state get correctly set up for newly bound mappables pMappable->SetResync(true); pMappable->SetGameState(mGameState); return (int)i; } void UserController::UnregisterMappable( int handle ) { rAssert( handle < static_cast< int >( Input::MaxMappables ) ); if ( mMappable[ handle ] ) { mMappable[ handle ]->Reset( ); mMappable[ handle ]->Release( ); mMappable[ handle ] = 0; LoadControllerMappings(); } } void UserController::UnregisterMappable( Mappable* mappable) { for ( unsigned i = 0; i < Input::MaxMappables; i++ ) { if( this->mMappable[ i ] == mappable ) { mMappable[ i ]->Reset( ); mMappable[ i ]->Release( ); mMappable[ i ] = 0; LoadControllerMappings(); return; } } } void UserController::LoadControllerMappings( void ) { unsigned int i = 0; for ( i = 0; i < Input::MaxMappables; i++ ) { if( this->mMappable[ i ] != NULL ) { this->mMappable[ i ]->LoadControllerMappings( m_controllerId ); mMappable[ i ]->InitButtons( m_controllerId, mButtonArray); } } } // return the button id from the name. int UserController::GetIdByName( const char* pszName ) const { radKey key = radMakeKey(pszName); unsigned int i; for( i = 0; i < Input::MaxPhysicalButtons; i++ ) { if (mButtonNames[i] == key) { return i; } } return -1; } SteeringSpring* UserController::GetSpring() { return m_pSteeringSpring->IsInit() ? m_pSteeringSpring : NULL; } BaseDamper* UserController::GetDamper() { return m_pSteeringDamper->IsInit() ? m_pSteeringDamper : NULL; } ConstantEffect* UserController::GetConstantEffect() { return m_pConstantEffect->IsInit() ? m_pConstantEffect : NULL; } WheelRumble* UserController::GetWheelRumble() { return m_pWheelRumble->IsInit() ? m_pWheelRumble : NULL; } WheelRumble* UserController::GetHeavyWheelRumble() { return m_pHeavyWheelRumble->IsInit() ? m_pHeavyWheelRumble : NULL; } void UserController::RegisterInputPoints() { // If we've already registered, then return. if( mbInputPointsRegistered ) { return; } // Register input points for each controller. for( int type = 0; type < NUM_CONTROLLERTYPES; type++ ) { // Retrieve the controller.. If not connected, skip. RADCONTROLLER pController = m_pController[type]->getController(); if( pController == NULL ) { continue; } // Go through each input point of the controller. unsigned num = pController->GetNumberOfInputPoints(); for( unsigned ip = 0; ip < num; ip++ ) { RegisterInputPoint( (eControllerType) type, ip ); } } mbInputPointsRegistered = true; } void UserController::RegisterInputPoint( eControllerType type, int inputpoint ) { // Can only map when the controller connected.. otherwise no work. RADCONTROLLER pController = m_pController[type]->getController(); rAssert( pController != NULL ); IRadControllerInputPoint* pInputPoint = pController->GetInputPointByIndex( inputpoint ); rAssert( pInputPoint != NULL ); int dxKey = m_pController[type]->GetDICode( inputpoint ); // For banned input points, return right away if( dxKey != Input::INVALID_CONTROLLERID && !m_pController[type]->IsBannedInput( dxKey ) ) { // Register the callback. // the direction value below is unimportant & ignored. InputCode icode( type, dxKey, NUM_DIRTYPES ); pInputPoint->RegisterControllerInputPointCallback( this, icode.GetInputCode() ); m_pController[type]->AddInputPoints( pInputPoint ); } } const char* UserController::GetConfigName() const { // do this unfortunate switch cause of multiplayer possibilities :/ // maybe clean it up later. switch( m_controllerId ) { case -1: return "Control-default"; case 0: return "Controller"; case 1: return "Controller1"; case 2: return "Controller2"; case 3: return "Controller3"; default: rAssert( false ); return "Controller3"; } } int UserController::GetNumProperties() const { return Input::MaxVirtualMappings * Input::MaxPhysicalButtons + 8; } void UserController::LoadDefaults() { ClearMappings(); if ( m_controllerId == 0 ) { // The primary mapping - to the keyboard & mouse. SetMap( SLOT_PRIMARY, InputManager::MoveUp, KEYBOARD, DIK_W ); SetMap( SLOT_PRIMARY, InputManager::MoveDown, KEYBOARD, DIK_S ); SetMap( SLOT_PRIMARY, InputManager::MoveLeft, KEYBOARD, DIK_A ); SetMap( SLOT_PRIMARY, InputManager::MoveRight, KEYBOARD, DIK_D ); SetMap( SLOT_PRIMARY, InputManager::Jump, KEYBOARD, DIK_SPACE ); SetMap( SLOT_PRIMARY, InputManager::Sprint, KEYBOARD, DIK_LSHIFT ); SetMap( SLOT_PRIMARY, InputManager::Accelerate, KEYBOARD, DIK_W ); SetMap( SLOT_PRIMARY, InputManager::Reverse, KEYBOARD, DIK_S ); SetMap( SLOT_PRIMARY, InputManager::SteerLeft, KEYBOARD, DIK_A ); SetMap( SLOT_PRIMARY, InputManager::SteerRight, KEYBOARD, DIK_D ); SetMap( SLOT_PRIMARY, InputManager::Horn, KEYBOARD, DIK_LSHIFT ); SetMap( SLOT_PRIMARY, InputManager::ResetCar, KEYBOARD, DIK_SPACE ); SetMap( SLOT_PRIMARY, InputManager::CameraLeft, KEYBOARD, DIK_NUMPAD4 ); SetMap( SLOT_PRIMARY, InputManager::CameraRight, KEYBOARD, DIK_NUMPAD6 ); SetMap( SLOT_PRIMARY, InputManager::CameraMoveIn, KEYBOARD, DIK_NUMPAD8 ); SetMap( SLOT_PRIMARY, InputManager::CameraMoveOut, KEYBOARD, DIK_NUMPAD2 ); SetMap( SLOT_PRIMARY, InputManager::CameraZoom, KEYBOARD, DIK_NUMPAD5 ); SetMap( SLOT_PRIMARY, InputManager::CameraLookUp, KEYBOARD, DIK_NUMPAD0 ); SetMap( SLOT_PRIMARY, InputManager::CameraToggle, KEYBOARD, DIK_NUMPAD0 ); SetMap( SLOT_PRIMARY, InputManager::CameraCarLeft, KEYBOARD, DIK_NUMPAD4 ); SetMap( SLOT_PRIMARY, InputManager::CameraCarRight, KEYBOARD, DIK_NUMPAD6 ); SetMap( SLOT_PRIMARY, InputManager::CameraCarLookUp, KEYBOARD, DIK_NUMPAD8 ); SetMap( SLOT_PRIMARY, InputManager::CameraCarLookBack, KEYBOARD, DIK_NUMPAD2 ); } SetMap( SLOT_PRIMARY, InputManager::Attack, MOUSE, DIMOFS_BUTTON1 ); SetMap( SLOT_PRIMARY, InputManager::DoAction, MOUSE, DIMOFS_BUTTON0 ); SetMap( SLOT_PRIMARY, InputManager::GetOutCar, MOUSE, DIMOFS_BUTTON0 ); SetMap( SLOT_PRIMARY, InputManager::HandBrake, MOUSE, DIMOFS_BUTTON1 ); // The secondary mapping - to the gamepad. SetMap( SLOT_SECONDARY, InputManager::MoveUp, GAMEPAD, DIJOFS_Y, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::MoveDown, GAMEPAD, DIJOFS_Y, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::MoveLeft, GAMEPAD, DIJOFS_X, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::MoveRight, GAMEPAD, DIJOFS_X, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::Attack, GAMEPAD, DIJOFS_BUTTON0 ); SetMap( SLOT_SECONDARY, InputManager::Jump, GAMEPAD, DIJOFS_BUTTON3 ); SetMap( SLOT_SECONDARY, InputManager::Sprint, GAMEPAD, DIJOFS_BUTTON7 ); SetMap( SLOT_SECONDARY, InputManager::DoAction, GAMEPAD, DIJOFS_BUTTON4 ); SetMap( SLOT_SECONDARY, InputManager::Accelerate, GAMEPAD, DIJOFS_BUTTON7 ); SetMap( SLOT_SECONDARY, InputManager::Reverse, GAMEPAD, DIJOFS_BUTTON6 ); SetMap( SLOT_SECONDARY, InputManager::SteerLeft, GAMEPAD, DIJOFS_X, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::SteerRight, GAMEPAD, DIJOFS_X, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::GetOutCar, GAMEPAD, DIJOFS_BUTTON4 ); SetMap( SLOT_SECONDARY, InputManager::HandBrake, GAMEPAD, DIJOFS_BUTTON1 ); SetMap( SLOT_SECONDARY, InputManager::Horn, GAMEPAD, DIJOFS_BUTTON2 ); SetMap( SLOT_SECONDARY, InputManager::ResetCar, GAMEPAD, DIJOFS_BUTTON5 ); SetMap( SLOT_SECONDARY, InputManager::CameraLeft, GAMEPAD, DIJOFS_Z, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::CameraRight, GAMEPAD, DIJOFS_Z, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::CameraMoveIn, GAMEPAD, DIJOFS_RZ, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::CameraMoveOut, GAMEPAD, DIJOFS_RZ, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::CameraZoom, GAMEPAD, DIJOFS_BUTTON6 ); SetMap( SLOT_SECONDARY, InputManager::CameraLookUp, GAMEPAD, DIJOFS_BUTTON7 ); SetMap( SLOT_SECONDARY, InputManager::CameraToggle, GAMEPAD, DIJOFS_BUTTON5 ); SetMap( SLOT_SECONDARY, InputManager::CameraCarLeft, GAMEPAD, DIJOFS_Z, DIR_DOWN ); SetMap( SLOT_SECONDARY, InputManager::CameraCarRight, GAMEPAD, DIJOFS_Z, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::CameraCarLookUp, GAMEPAD, DIJOFS_RZ, DIR_UP ); SetMap( SLOT_SECONDARY, InputManager::CameraCarLookBack, GAMEPAD, DIJOFS_RZ, DIR_DOWN ); // Initialize the game settings m_bMouseLook = true; m_bInvertMouseX = false; m_bInvertMouseY = false; m_bForceFeedback = false; m_bTutorialDisabled = false; m_fMouseSensitivityX = 0.35f; m_fMouseSensitivityY = 0.5f; m_fWheelSensitivityX = 0.5f; m_fWheelSensitivityY = 0.5f; //Enable the tutorials if controls are reverted. TutorialManager* pTutorialManager = GetTutorialManager(); if( pTutorialManager ) pTutorialManager->EnableTutorialMode( m_bTutorialDisabled ); } void UserController::LoadConfig( ConfigString& config ) { ClearMappings(); char property[ ConfigString::MaxLength ]; char value[ ConfigString::MaxLength ]; int map; int virtualKey; int cont; int dxKey; int dir; // Load the mappings. while ( config.ReadProperty( property, value ) ) { if( _stricmp( property, "buttonmap" ) == 0 ) { int ret = sscanf( value, "%d, %d: ( %d %d %d )", &map, &virtualKey, &cont, &dxKey, &dir ); if( ret == 5 && map >= 0 && map < Input::MaxVirtualMappings && virtualKey >= 0 && virtualKey < (int) mNumButtons && VirtualInputs::GetType( virtualKey ) != MAP_FRONTEND && cont >= 0 && cont < NUM_CONTROLLERTYPES && m_pController[ cont ]->IsValidInput( dxKey ) && dir >= 0 && dir < NUM_DIRTYPES ) { SetMap( map, virtualKey, (eControllerType) cont, dxKey, (eDirectionType) dir ); } } else if( _stricmp( property, "mouselook" ) == 0 ) { if( strcmp( value, "yes" ) == 0 ) { m_bMouseLook = true; } else if( strcmp( value, "no" ) == 0 ) { m_bMouseLook = false; } } else if( _stricmp( property, "invertmousex" ) == 0 ) { if( strcmp( value, "yes" ) == 0 ) { m_bInvertMouseX = true; } else if( strcmp( value, "no" ) == 0 ) { m_bInvertMouseX = false; } } else if( _stricmp( property, "invertmousey" ) == 0 ) { if( strcmp( value, "yes" ) == 0 ) { m_bInvertMouseY = true; } else if( strcmp( value, "no" ) == 0 ) { m_bInvertMouseY = false; } } else if( _stricmp( property, "useforcefeedback" ) == 0 ) { if( strcmp( value, "yes" ) == 0 ) { m_bForceFeedback = true; } else if( strcmp( value, "no" ) == 0 ) { m_bForceFeedback= false; } } else if( _stricmp( property, "disabletutorials" ) == 0 ) { if( strcmp( value, "yes" ) == 0 ) { m_bTutorialDisabled = true; } else if( strcmp( value, "no" ) == 0 ) { m_bTutorialDisabled = false; } } else if( _stricmp( property, "mousesensitivityx" ) == 0 ) { float val = (float) atof( value ); if( val > 0 ) { m_fMouseSensitivityX = val; } } else if( _stricmp( property, "mousesensitivityy" ) == 0 ) { float val = (float) atof( value ); if( val > 0 ) { m_fMouseSensitivityY = val; } } else if( _stricmp( property, "wheelsensitivityx" ) == 0 ) { float val = (float) atof( value ); if( val > 0 ) { m_fWheelSensitivityX = val; } } else if( _stricmp( property, "wheelsensitivityy" ) == 0 ) { float val = (float) atof( value ); if( val > 0 ) { m_fWheelSensitivityY = val; } } } } void UserController::SaveConfig( ConfigString& config ) { char value[ ConfigString::MaxLength ]; eControllerType cont; int dxKey; eDirectionType dir; // For each of our mappings... for( int map = 0; map < Input::MaxVirtualMappings; map++ ) { // For each virtual button... for( int vk = 0; vk < (int) mNumButtons; vk++ ) { // Save any mapped key. if( VirtualInputs::GetType( vk ) != MAP_FRONTEND && GetMap( map, vk, cont, dxKey, dir ) ) { sprintf( value, "%d, %d: ( %d %d %d )", map, vk, cont, dxKey, dir ); config.WriteProperty( "buttonmap", value ); } } } // Save the other controller settings config.WriteProperty( "mouselook", m_bMouseLook ? "yes" : "no" ); config.WriteProperty( "invertmousex", m_bInvertMouseX ? "yes" : "no" ); config.WriteProperty( "invertmousey", m_bInvertMouseY ? "yes" : "no" ); config.WriteProperty( "useforcefeedback", m_bForceFeedback ? "yes" : "no" ); config.WriteProperty( "disabletutorials", m_bTutorialDisabled ? "yes" : "no" ); sprintf( value, "%f", m_fMouseSensitivityX ); config.WriteProperty( "mousesensitivityx", value ); sprintf( value, "%f", m_fMouseSensitivityY ); config.WriteProperty( "mousesensitivityy", value ); sprintf( value, "%f", m_fWheelSensitivityX ); config.WriteProperty( "wheelsensitivityx", value ); sprintf( value, "%f", m_fWheelSensitivityY ); config.WriteProperty( "wheelsensitivityy", value ); } void UserController::RemapButton( int map, int VirtualButton, ButtonMappedCallback* callback ) { rAssert( map >= 0 && map < Input::MaxVirtualMappings ); rAssert( VirtualButton >= 0 && VirtualButton < mNumButtons ); rAssert( VirtualInputs::GetType( VirtualButton ) != MAP_FRONTEND ); // Save the data. Button gets mapped asynchronously in OnControllerInputPointChange() mMapData.MapNext = true; mMapData.map = map; mMapData.virtualButton = VirtualButton; mMapData.callback = callback; } void UserController::SetMap( int map, int virtualKey, eControllerType cont, int dxKey, eDirectionType dir ) { // Check over the inputs. rAssert( map >= 0 && map < Input::MaxVirtualMappings ); rAssert( virtualKey >= 0 && virtualKey < mNumButtons ); rAssert( m_pController[ cont ]->IsValidInput( dxKey ) ); rAssert( VirtualInputs::GetType( virtualKey ) != MAP_FRONTEND ); // Create the compressed key code InputCode icode( cont, dxKey, dir ); // Clear the previous key that was used for this virtual button. eControllerType oldcont; int olddxKey; eDirectionType olddir; if( GetMap( map, virtualKey, oldcont, olddxKey, olddir ) ) { m_pController[ oldcont ]->ClearMap( olddxKey, olddir, virtualKey ); } // Clear any previous virtual button mapped to this controller/key/direction eMapType maptype = VirtualInputs::GetType( virtualKey ); int old_vb = m_pController[ cont ]->GetMap( dxKey, dir, maptype ); if( old_vb != Input::INVALID_CONTROLLERID ) { for( int i = 0; i < Input::MaxVirtualMappings; i++ ) { if( mVirtualMap[ i ].GetLogicalIndex( old_vb ) == icode.GetInputCode() ) { mVirtualMap[ i ].SetAssociation( old_vb, Input::INVALID_CONTROLLERID ); } } } // Save the new mapping mVirtualMap[ map ].SetAssociation( virtualKey, icode.GetInputCode() ); // Propagate the mapping to the controller m_pController[ cont ]->SetMap( dxKey, dir, virtualKey ); } bool UserController::GetMap( int map, int virtualKey, eControllerType& type, int& dxKey, eDirectionType& dir ) const { // Check over the inputs. rAssert( map >= 0 && map < Input::MaxVirtualMappings ); rAssert( virtualKey >= 0 && virtualKey < mNumButtons ); // Load the mapping int code = mVirtualMap[ map ].GetLogicalIndex( virtualKey ); if( code != Input::INVALID_CONTROLLERID ) { InputCode icode = code; type = icode.GetController(); dxKey = icode.GetDxKeyCode(); dir = icode.GetDirection(); } return code != Input::INVALID_CONTROLLERID; } const char* UserController::GetMap( int map, int virtualKey, int& numDirs, eControllerType& cont, eDirectionType& dir ) const { int dxKey; // Retrieve the mapping for the key. // If the controller is not plugged in, return null. if( !GetMap( map, virtualKey, cont, dxKey, dir ) || m_pController[ cont ]->getController() == NULL ) { return NULL; } // figure out how many directions there are for this input if( m_pController[ cont ]->IsInputAxis( dxKey ) || m_pController[ cont ]->IsMouseAxis( dxKey ) ) { numDirs = 2; } else if( m_pController[ cont ]->IsPovHat( dxKey ) ) { numDirs = 4; } else { numDirs = 1; } // return the name of the input point return m_pController[ cont ]->GetInputName( dxKey ); } void UserController::Remap( eControllerType cont, int dxKey, float* dvalues, int ndirections ) { if( cont == KEYBOARD && m_pController[ cont ]->GetMap( dxKey, DIR_UP, MAP_FRONTEND ) == InputManager::feBack ) { // If the user pressed back on the keyboard, cancel the mapping. mMapData.MapNext = false; mMapData.callback->OnButtonMapped( NULL, cont, 1, NUM_DIRTYPES ); } else if( cont == MOUSE && ( dxKey == DIMOFS_X || dxKey == DIMOFS_Y ) ) { // Not allowed to map these. } else { // Find out which direction the user is pressing in. for( int dir = 0; dir < ndirections; dir++ ) { if( dvalues[ dir ] >= MAPPING_DEADZONE ) { SetMap( mMapData.map, mMapData.virtualButton, cont, dxKey, (eDirectionType) dir ); // Call back the mapper. mMapData.MapNext = false; mMapData.callback->OnButtonMapped( m_pController[ cont ]->GetInputName( dxKey ), cont, ndirections, (eDirectionType) dir ); break; } } } } void UserController::ClearMappings() { for( int i = 0; i < Input::MaxVirtualMappings; i++ ) { mVirtualMap[ i ].ClearAssociations(); } for( int j = 0; j < NUM_CONTROLLERTYPES; j++ ) { m_pController[ j ]->ClearMappedButtons(); } // Make sure these are always loaded. LoadFEMappings(); } void UserController::LoadFEMappings() { switch ( m_controllerId ) { case 0: { m_pController[KEYBOARD]->SetMap( DIK_ESCAPE, DIR_UP, InputManager::feBack ); m_pController[KEYBOARD]->SetMap( DIK_UP, DIR_UP, InputManager::feMoveUp ); m_pController[KEYBOARD]->SetMap( DIK_DOWN, DIR_UP, InputManager::feMoveDown ); m_pController[KEYBOARD]->SetMap( DIK_LEFT, DIR_UP, InputManager::feMoveLeft ); m_pController[KEYBOARD]->SetMap( DIK_RIGHT, DIR_UP, InputManager::feMoveRight ); m_pController[KEYBOARD]->SetMap( DIK_RETURN, DIR_UP, InputManager::feSelect ); m_pController[KEYBOARD]->SetMap( DIK_NUMPAD8, DIR_UP, InputManager::feMoveUp ); m_pController[KEYBOARD]->SetMap( DIK_NUMPAD2, DIR_UP, InputManager::feMoveDown ); m_pController[KEYBOARD]->SetMap( DIK_NUMPAD4, DIR_UP, InputManager::feMoveLeft ); m_pController[KEYBOARD]->SetMap( DIK_NUMPAD6, DIR_UP, InputManager::feMoveRight ); m_pController[KEYBOARD]->SetMap( DIK_NUMPADENTER, DIR_UP, InputManager::feSelect ); m_pController[KEYBOARD]->SetMap( DIK_F1, DIR_UP, InputManager::feFunction1 ); m_pController[KEYBOARD]->SetMap( DIK_F2, DIR_UP, InputManager::feFunction2 ); break; } case 3: { //P4 m_pController[KEYBOARD]->SetMap( DIK_F4, DIR_UP, InputManager::P1_KBD_Start ); m_pController[KEYBOARD]->SetMap( DIK_O, DIR_UP, InputManager::P1_KBD_Gas ); m_pController[KEYBOARD]->SetMap( DIK_L, DIR_UP, InputManager::P1_KBD_Brake ); m_pController[KEYBOARD]->SetMap( DIK_RETURN, DIR_UP, InputManager::P1_KBD_EBrake ); m_pController[KEYBOARD]->SetMap( DIK_RSHIFT, DIR_UP, InputManager::P1_KBD_Nitro ); m_pController[KEYBOARD]->SetMap( DIK_K, DIR_UP, InputManager::P1_KBD_Left ); m_pController[KEYBOARD]->SetMap( DIK_SEMICOLON, DIR_UP, InputManager::P1_KBD_Right ); break; } } m_pController[MOUSE]->SetMap( DIMOFS_BUTTON1, DIR_UP, InputManager::feBack ); m_pController[MOUSE]->SetMap( DIMOFS_X, DIR_UP, InputManager::feMouseRight ); m_pController[MOUSE]->SetMap( DIMOFS_X, DIR_DOWN, InputManager::feMouseLeft ); m_pController[MOUSE]->SetMap( DIMOFS_Y, DIR_UP, InputManager::feMouseUp ); m_pController[MOUSE]->SetMap( DIMOFS_Y, DIR_DOWN, InputManager::feMouseDown ); m_pController[MOUSE]->SetMap( DIMOFS_Z, DIR_UP, InputManager::feMoveRight ); m_pController[MOUSE]->SetMap( DIMOFS_Z, DIR_DOWN, InputManager::feMoveLeft ); m_pController[GAMEPAD]->SetMap( DIJOFS_Y, DIR_UP, InputManager::feMoveUp ); m_pController[GAMEPAD]->SetMap( DIJOFS_Y, DIR_DOWN, InputManager::feMoveDown ); m_pController[GAMEPAD]->SetMap( DIJOFS_X, DIR_UP, InputManager::feMoveRight ); m_pController[GAMEPAD]->SetMap( DIJOFS_X, DIR_DOWN, InputManager::feMoveLeft ); m_pController[GAMEPAD]->SetMap( DIJOFS_BUTTON0, DIR_UP, InputManager::feSelect ); m_pController[GAMEPAD]->SetMap( DIJOFS_BUTTON1, DIR_UP, InputManager::feBack ); m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_UP, InputManager::feMoveUp ); m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_DOWN, InputManager::feMoveDown ); m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_RIGHT, InputManager::feMoveRight ); m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_LEFT, InputManager::feMoveLeft ); } void UserController::ResetMouseAxes() { for( int i = 0; i < 3; i++ ) { if( mResetMouseCounter[ i ] > 1 ) { // Reset this mouse axis. InputCode icode( MOUSE, maxes[ i ], NUM_DIRTYPES ); OnControllerInputPointChange( icode.GetInputCode(), 0.0f ); mResetMouseCounter[ i ] = 0; } else if( mResetMouseCounter[ i ] > 0 ) { mResetMouseCounter[ i ]++; } } }