summaryrefslogtreecommitdiffstats
path: root/game/code/input
diff options
context:
space:
mode:
authorSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
committerSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
commiteb4b3404aa00220d659e532151dab13d642c17a3 (patch)
tree7e1107c4995489a26c4007e41b53ea8d00ab2134 /game/code/input
downloadThe-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.gz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.bz2
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.lz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.xz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.zst
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.zip
Diffstat (limited to 'game/code/input')
-rw-r--r--game/code/input/FEMouse.cpp338
-rw-r--r--game/code/input/FEMouse.h109
-rw-r--r--game/code/input/Gamepad.cpp567
-rw-r--r--game/code/input/Gamepad.h107
-rw-r--r--game/code/input/Keyboard.cpp215
-rw-r--r--game/code/input/Keyboard.h58
-rw-r--r--game/code/input/Mouse.cpp399
-rw-r--r--game/code/input/Mouse.h80
-rw-r--r--game/code/input/MouseCursor.cpp149
-rw-r--r--game/code/input/MouseCursor.h57
-rw-r--r--game/code/input/RealController.cpp210
-rw-r--r--game/code/input/RealController.h131
-rw-r--r--game/code/input/SteeringWheel.cpp170
-rw-r--r--game/code/input/SteeringWheel.h31
-rw-r--r--game/code/input/allinput.cpp20
-rw-r--r--game/code/input/basedamper.cpp185
-rw-r--r--game/code/input/basedamper.h59
-rw-r--r--game/code/input/button.cpp14
-rw-r--r--game/code/input/button.h71
-rw-r--r--game/code/input/constanteffect.cpp163
-rw-r--r--game/code/input/constanteffect.h59
-rw-r--r--game/code/input/controller.h100
-rw-r--r--game/code/input/forceeffect.cpp195
-rw-r--r--game/code/input/forceeffect.h104
-rw-r--r--game/code/input/inputPointList.h176
-rw-r--r--game/code/input/inputmanager.cpp731
-rw-r--r--game/code/input/inputmanager.h414
-rw-r--r--game/code/input/mappable.cpp251
-rw-r--r--game/code/input/mappable.h138
-rw-r--r--game/code/input/mapper.cpp39
-rw-r--r--game/code/input/mapper.h40
-rw-r--r--game/code/input/rumbleeffect.cpp464
-rw-r--r--game/code/input/rumbleeffect.h116
-rw-r--r--game/code/input/rumblegc.cpp49
-rw-r--r--game/code/input/rumbleps2.cpp44
-rw-r--r--game/code/input/rumblewin32.cpp68
-rw-r--r--game/code/input/rumblexbox.cpp51
-rw-r--r--game/code/input/steeringspring.cpp187
-rw-r--r--game/code/input/steeringspring.h59
-rw-r--r--game/code/input/usercontroller.cpp726
-rw-r--r--game/code/input/usercontroller.h139
-rw-r--r--game/code/input/usercontrollerWin32.cpp1443
-rw-r--r--game/code/input/usercontrollerWin32.h294
-rw-r--r--game/code/input/virtualinputs.cpp178
-rw-r--r--game/code/input/virtualinputs.hpp31
-rw-r--r--game/code/input/wheeldefines.h53
-rw-r--r--game/code/input/wheelrumble.cpp214
-rw-r--r--game/code/input/wheelrumble.h67
48 files changed, 9563 insertions, 0 deletions
diff --git a/game/code/input/FEMouse.cpp b/game/code/input/FEMouse.cpp
new file mode 100644
index 0000000..4a31ea6
--- /dev/null
+++ b/game/code/input/FEMouse.cpp
@@ -0,0 +1,338 @@
+#include <input/FEMouse.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <gameflow/gameflow.h>
+#include <contexts/contextenum.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <main/win32platform.h>
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+const char* DYNAMIC_RESOURCES_DIR = "art\\frontend\\dynaload\\images\\";
+#endif
+
+const float WIN_TO_P3D_POINT_SCALE_FACTOR = 2.0f;
+
+/******************************************************************************
+ Construction/Destruction
+*****************************************************************************/
+
+FEMouse::FEMouse()
+: m_bMoved( false ),
+ m_bClickable( true ),
+ m_bSelectable( true ),
+ m_bMovable( true ),
+ m_oldPositionX(0),
+ m_oldPositionY(0),
+ m_hotSpotType( HOTSPOT_BUTTON ),
+ m_horzDir( NO_HORZ_MOVEMENT ),
+ m_bClickAndStop(false),
+ m_bInGame( false ),
+ m_bInGameOverride( false ),
+ m_inGamePosX( 0 ),
+ m_inGamePosY( 0 ),
+ m_buttonDownSelection( 0 )
+{
+ m_pCursor = new MouseCursor();
+ memset( m_button, 0, sizeof(m_button));
+}
+
+FEMouse::~FEMouse()
+{
+ delete m_pCursor;
+ m_pCursor = NULL;
+}
+
+void FEMouse::InitMouseCursor( tDrawable* pCursor )
+{
+ m_pCursor->Set( pCursor );
+}
+
+eFEMouseHorzDir FEMouse::OnSliderHorizontalClickDrag() const
+{
+ if (IsLeftButtonDown() &&
+ m_hotSpotType == HOTSPOT_SLIDER &&
+ m_horzDir != NO_HORZ_MOVEMENT &&
+ m_bMoved == true )
+ {
+ return m_horzDir;
+ }
+ else return NO_HORZ_MOVEMENT;
+}
+
+bool FEMouse::DidWeMove( int newX, int newY ) const
+{
+ return ( (newX != m_oldPositionX) || (newY != m_oldPositionY) );
+}
+
+void FEMouse::Move( int mouseX, int mouseY, long screenWidth, long screenHeight )
+{
+ if( !m_bMovable ) return;
+ m_bMoved = true;
+
+ // let us know what direction it moved horizontally.
+ if( m_oldPositionX < mouseX ) m_horzDir = MDIR_RIGHT;
+ else if( m_oldPositionX > mouseX ) m_horzDir = MDIR_LEFT;
+ else m_horzDir = NO_HORZ_MOVEMENT;
+
+
+ m_oldPositionX = mouseX;
+ m_oldPositionY = mouseY;
+
+ // Normalize the windows mouse coordinates that are in pixel units, to 2d world units.
+ // From ( -1 to 1 ) on the major axis and ( -height/width to -height/width ) on the minor.
+ // We must scale the incoming coordinates and offset them to the center.
+ // We must also handle the difference in width and height, to compensate for the aspect
+ // ratio of the display.
+
+ const float ASPECT = (float)screenWidth/(float)screenHeight; // The screen aspect ratio.
+
+ float deltaX = ( (mouseX/( screenWidth/WIN_TO_P3D_POINT_SCALE_FACTOR )) - 1.0f );
+ float deltaY = ( 1.0f - (mouseY/(screenHeight/WIN_TO_P3D_POINT_SCALE_FACTOR)))/ASPECT;
+
+ m_pCursor->SetPos( deltaX/WIN_TO_P3D_POINT_SCALE_FACTOR, deltaY/WIN_TO_P3D_POINT_SCALE_FACTOR );
+ //rDebugPrintf("Mouse moved to X: %g Y: %g \n", deltaX, deltaY);
+}
+
+void FEMouse::Update()
+{
+ bool ingame = m_bInGame && ! m_bInGameOverride;
+ bool bLoading = (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY) ||
+ (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT) ||
+ (GetGameFlow()->GetCurrentContext() == CONTEXT_EXIT);
+ if( ingame || bLoading ) return;
+ if( !m_pCursor->IsVisible() ) return;
+
+ CGuiWindow* pCurrentWindow = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow();
+ CGuiMenu* pCurrentMenu = pCurrentWindow->HasMenu();
+ if( !pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade()) )//m_bSelectable )
+ {
+
+ // Dusit here,
+ // Took out the update only on mouse moved test.
+ // This is a problem when a new front-end screen item appears underneath
+ // the mouse cursor, but the mouse hasn't moved yet, so it can't click
+ // on the front-end item. So we either need to get notified when that
+ // happens and manually update the mouse focus via callback, or just
+ // do it every frame. I think it's reasonable to do it every frame; otherwise, if
+ // the call is really expensive, we'd end up with low framerate while mouse is
+ // moving and good framerate when mouse is stationary (which is not
+ // a good optimization).
+ //
+ //if( m_bMoved ) //Only update the hotspot checking if the mouse moved!
+ //{
+ //
+ m_hotSpotType = pCurrentWindow->CheckCursorAgainstHotspots( m_pCursor->XPos()*WIN_TO_P3D_POINT_SCALE_FACTOR,
+ m_pCursor->YPos()*WIN_TO_P3D_POINT_SCALE_FACTOR);
+ if( m_hotSpotType == HOTSPOT_NONE )
+ {
+ // if the user is still holding onto the button, don't tamper with its state.
+ // we deal with that case later (just below).
+ if( m_button[ BUTTON_LEFT ] != BUTTON_CLICKHOLD )
+ {
+ m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
+ }
+ }
+ m_bMoved = false;
+ rAssert( !m_bMoved );
+
+ if( pCurrentMenu )
+ {
+ if( m_button[ BUTTON_LEFT ] == BUTTON_CLICKHOLD )
+ {
+ // deal with highlighting the hotspot...
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ if( m_buttonDownSelection == pCurrentMenu->GetSelection() && m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
+ }
+ break;
+ case HOTSPOT_NONE:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ break;
+ default: break;
+ }
+ }
+ else
+ {
+ // If nothing is being held down we should eliminate the highlighting.
+ // This fixes the problem where if you hold down LMB then right-click to
+ // escape to previous screen, when you go back to the first screen, the
+ // highlighting will still be in effect.
+ //
+ if( m_bClickable && m_bSelectable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ }
+ }
+ }
+ m_pCursor->Render();
+}
+
+void FEMouse::ButtonDown( eFEMouseButton buttonType )
+{
+ // don't allow clicking when selectable not on
+ CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
+ if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
+ {
+ return;
+ }
+
+ m_button[buttonType] = BUTTON_CLICKHOLD;
+
+ // what follows concerns only left mouse button
+ if( buttonType != BUTTON_LEFT )
+ {
+ return;
+ }
+
+ // remember what menu item it was that we pressed down on...
+ m_buttonDownSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
+
+ // deal with highlighting the hotspot...
+ if( pCurrentMenu )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
+ }
+ break;
+ case HOTSPOT_NONE:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void FEMouse::ButtonUp( enum eFEMouseButton buttonType )
+{
+ // don't allow clicking when selectable not on
+ CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
+ if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
+ {
+ return;
+ }
+
+ m_button[ buttonType ] = BUTTON_CLICKED;
+
+ // what follows concerns only left mouse button
+ if( buttonType != BUTTON_LEFT )
+ {
+ return;
+ }
+
+ /////////////////////////////////////////////////////////////////
+ // Deal with the consequence of left-clicking a menu button
+
+ //
+ // if we release mouse button on something other than the item we
+ // were on when we pressed the mouse button, we don't do anything
+ //
+ int currSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
+ if( currSelection != m_buttonDownSelection )
+ {
+ return;
+ }
+
+ // at this point, the hotspot we're currently on matches the
+ // one we were at when we pressed the left mouse button... so click!
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ userInputHandler->Select();
+ m_bSelectable = false; // need to disable selectable IMMEDIATELY (not when screen exits)
+ break;
+ case HOTSPOT_ARROWLEFT:
+ userInputHandler->Left();
+ break;
+ case HOTSPOT_ARROWRIGHT:
+ userInputHandler->Right();
+ break;
+ case HOTSPOT_ARROWUP:
+ userInputHandler->Up();
+ break;
+ case HOTSPOT_ARROWDOWN:
+ userInputHandler->Down();
+ break;
+ case HOTSPOT_LTRIGGER:
+ userInputHandler->L1();
+ break;
+ case HOTSPOT_RTRIGGER:
+ userInputHandler->R1();
+ break;
+ default: break;
+ }
+
+ // reset left button state
+ m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
+}
+
+void FEMouse::SetInGameMode( bool ingame )
+{
+ m_bInGame = ingame;
+ SetupInGameMode();
+}
+
+void FEMouse::SetInGameOverride( bool override )
+{
+ m_bInGameOverride = override;
+ SetupInGameMode();
+}
+
+void FEMouse::SetupInGameMode()
+{
+ bool ingame = m_bInGame && ! m_bInGameOverride;
+
+ m_pCursor->SetVisible( !ingame );
+
+ if( ingame )
+ {
+ RECT windowRect;
+ GetWindowRect( Win32Platform::GetInstance()->GetHwnd(), &windowRect );
+
+ // Save the old cursor position.
+ POINT oldPos;
+ GetCursorPos( & oldPos );
+ m_inGamePosX = oldPos.x;
+ m_inGamePosY = oldPos.y;
+
+ // Center the cursor.
+ SetCursorPos( ( windowRect.left + windowRect.right ) / 2,
+ ( windowRect.top + windowRect.bottom ) / 2 );
+
+ // Set up the clipping rectangle.
+ windowRect.top += 30;
+ windowRect.bottom -= 10;
+ windowRect.left += 10;
+ windowRect.right -= 10;
+ ClipCursor( &windowRect );
+ }
+ else
+ {
+ // Remove the clipping rectangle.
+ ClipCursor( NULL );
+
+ // Restore the old cursor position.
+ if( m_inGamePosX != -1 )
+ {
+ SetCursorPos( m_inGamePosX, m_inGamePosY );
+ m_inGamePosX = -1;
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/input/FEMouse.h b/game/code/input/FEMouse.h
new file mode 100644
index 0000000..1298f80
--- /dev/null
+++ b/game/code/input/FEMouse.h
@@ -0,0 +1,109 @@
+/******************************************************************************
+ File: FEMouse.h
+ Desc: Interface for the Frontend Mouse class.
+ - Uses the windows mouse functionality.
+
+ Date: June 13, 2003
+ History:
+*****************************************************************************/
+
+#ifndef FEMOUSE_H
+#define FEMOUSE_H
+
+#include <input/MouseCursor.h>
+
+enum eFEMouseButton
+{
+ BUTTON_LEFT,
+ BUTTON_RIGHT,
+ NUM_BUTTON_TYPES
+};
+
+enum eFEMouseHorzDir
+{
+ MDIR_LEFT,
+ MDIR_RIGHT,
+ NUM_MOUSE_HORZ_DIRECTIONS,
+ NO_HORZ_MOVEMENT
+};
+
+enum eFEHotspotType
+{
+ HOTSPOT_BUTTON,
+ HOTSPOT_ARROWLEFT,
+ HOTSPOT_ARROWRIGHT,
+ HOTSPOT_ARROWUP,
+ HOTSPOT_ARROWDOWN,
+ HOTSPOT_SLIDER,
+ HOTSPOT_LTRIGGER,
+ HOTSPOT_RTRIGGER,
+ NUM_HOTSPOT_TYPES,
+ HOTSPOT_NONE
+};
+
+enum eFEButtonStates
+{
+ BUTTON_IDLE,
+ BUTTON_CLICKHOLD,
+ BUTTON_CLICKED,
+ NUM_BUTTON_STATES
+};
+
+class FEMouse
+{
+public:
+ FEMouse();
+ ~FEMouse();
+
+ MouseCursor* getCursor() const { return m_pCursor; }
+ bool Moved() const { return m_bMoved; }
+ bool IsClickable() const { return m_bClickable; }
+ eFEMouseHorzDir MovedHorizontally() const { return m_horzDir; }
+ bool IsLeftButtonDown() const { return (m_button[BUTTON_LEFT]==BUTTON_CLICKHOLD); }
+ eFEHotspotType LeftButtonDownOn() const
+ {
+ return ((m_button[BUTTON_LEFT]==BUTTON_CLICKHOLD) ? m_hotSpotType : HOTSPOT_NONE);
+ }
+ bool IsRightButtonDown() const { return (m_button[BUTTON_RIGHT]==BUTTON_CLICKHOLD); }
+
+ void InitMouseCursor( tDrawable* pCursor );
+ void SetClickable( bool bClickable ) { m_bClickable = bClickable; }
+ void SetSelectable( bool bSelectable ) { m_bSelectable = bSelectable; }
+ void SetClickStopMode( bool bClickAndStop ) { m_bClickAndStop = bClickAndStop; }
+ void ButtonDown( eFEMouseButton buttonType );
+ void ButtonUp( eFEMouseButton buttonType );
+
+ eFEMouseHorzDir OnSliderHorizontalClickDrag() const; //Is on a slider clicking and dragging horizontally.
+ bool DidWeMove( int newX, int newY ) const;
+ void Move( int mouseX, int mouseY, long screenWidth, long screenHeight );
+ void Update();
+
+ void SetInGameMode( bool ingame );
+ void SetInGameOverride( bool override );
+
+private:
+ void SetupInGameMode();
+
+private:
+ MouseCursor* m_pCursor;
+ bool m_bMoved;
+ bool m_bClickable;
+ bool m_bSelectable;
+ bool m_bMovable;
+ bool m_bClickAndStop; // if this is on, if a button is clicked, mouse processing stops.
+ char m_button[ NUM_BUTTON_TYPES ];
+ eFEHotspotType m_hotSpotType;
+ eFEMouseHorzDir m_horzDir;
+
+ int m_oldPositionX, //We save the last position of the mouse.
+ m_oldPositionY;
+
+ bool m_bInGame;
+ bool m_bInGameOverride;
+ int m_inGamePosX;
+ int m_inGamePosY;
+
+ int m_buttonDownSelection;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/Gamepad.cpp b/game/code/input/Gamepad.cpp
new file mode 100644
index 0000000..5bf4445
--- /dev/null
+++ b/game/code/input/Gamepad.cpp
@@ -0,0 +1,567 @@
+//**********************************************************
+// C++ Class Name : Gamepad
+// ---------------------------------------------------------
+// Filetype: (SOURCE)
+// Filepath: D:/simpsonsrr2/game/code/input/Gamepad.cpp
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#include <input/Gamepad.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//==============================================================================
+// Gamepad constructor
+//==============================================================================
+//
+// Description: Creates an empty gamepad.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+
+Gamepad::Gamepad()
+: RealController( GAMEPAD )
+{
+ ClearMappedButtons();
+}
+
+//==============================================================================
+// Gamepad destructor
+//==============================================================================
+//
+// Description: Destroy the gamepad.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+
+Gamepad::~Gamepad()
+{
+}
+
+//==============================================================================
+// Gamepad::IsInputAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool Gamepad::IsInputAxis( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_X:
+ case DIJOFS_Y:
+ case DIJOFS_Z:
+ case DIJOFS_RX:
+ case DIJOFS_RY:
+ case DIJOFS_RZ:
+ case DIJOFS_SLIDER(0):
+ case DIJOFS_SLIDER(1):
+ GetButtonEnum( dxKey );
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+//==============================================================================
+// Gamepad::IsPovHat
+//==============================================================================
+//
+// Description: Identifies any keys that are pov hats (go in 4 directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a pov hat
+//
+//==============================================================================
+
+bool Gamepad::IsPovHat( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_POV(0):
+ case DIJOFS_POV(1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Gamepad::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Gamepad::IsValidInput( int dxKey ) const
+{
+ return GetButtonEnum( dxKey ) != NUM_GAMEPAD_BUTTONS;
+}
+
+//==============================================================================
+// Gamepad::CalculatePOV
+//==============================================================================
+//
+// Description: Takes a POV input point value and calculates the corresponding
+// up/down/left/right directional values.
+//
+// Parameters: POV value
+//
+// Return: Up/Down/Left/Right values.
+//
+//==============================================================================
+
+void Gamepad::CalculatePOV( float povValue, float* up, float* down, float* right, float* left ) const
+{
+ // Reset all the directional values to 0.
+ *up = *down = *left = *right = 0.0f;
+
+ // Convert the POV value to directional values.
+ // A value of 1 means that the pov is untouched.
+ if( povValue < 1)
+ {
+ // Convert the POV value (0.0f - 1.0f) into radians (0.0f - 2PI) for use with radmath Trig functions.
+ float angle = ((povValue * 360)/180)*rmt::PI;
+
+ // Calculate the corresponding x and y axis values.
+ float xValue = rmt::Sin(angle);
+ float yValue = rmt::Cos(angle);
+
+ // Now assign the value to all the directions.
+ // Note that we are making the POV a digital button with values of only 0 and 1.
+ // This is so that walking diagonally is doable, instead of annoyingly slow.
+ *right = ( xValue > 0.1f ) ? 1.0f : 0.0f;
+ *left = ( xValue < -0.1f ) ? 1.0f : 0.0f;
+ *up = ( yValue > 0.1f ) ? 1.0f : 0.0f;
+ *down = ( yValue < -0.1f ) ? 1.0f : 0.0f;
+ }
+}
+
+//==============================================================================
+// Gamepad::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if mapped successfully
+//
+//==============================================================================
+
+bool Gamepad::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ gbutton ][ dir ] = virtualButton;
+ }
+
+ return gbutton != NUM_GAMEPAD_BUTTONS;
+}
+
+//==============================================================================
+// Gamepad::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ gbutton ][ dir ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Gamepad::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Gamepad::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ return m_ButtonMap[ map ][ gbutton ][ dir ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Gamepad::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Gamepad::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the gamepad. Note: this method is really slow, but it only
+// should get called once per game.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ // Get/set each input point.
+ // We unfortunately have to special case each direct input code.
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+
+ if( strcmp( type, "XAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_X;
+ }
+ else if( strcmp( type, "YAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Y;
+ }
+ else if( strcmp( type, "ZAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Z;
+ }
+ else if( strcmp( type, "RxAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RX;
+ }
+ else if( strcmp( type, "RyAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RY;
+ }
+ else if( strcmp( type, "RzAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RZ;
+ }
+ else if( strcmp( type, "Slider" ) == 0 )
+ {
+ // figure out which slider it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Slider", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_SLIDER(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "POV" ) == 0 )
+ {
+ // figure out which pov it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "POV", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_POV(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ // figure out which button it is
+ for( int j = 0; j < 32; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Button", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_BUTTON(j);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+//==============================================================================
+// Gamepad::GetButtonEnum
+//==============================================================================
+//
+// Description: Returns the gamepad button enum for the given input, or
+// NUM_GAMEPAD_BUTTONS if it's not a valid gamepad input.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+eGamepadButton Gamepad::GetButtonEnum( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_X:
+ {
+ return GAMEPAD_X;
+ }
+ case DIJOFS_Y:
+ {
+ return GAMEPAD_Y;
+ }
+ case DIJOFS_Z:
+ {
+ return GAMEPAD_Z;
+ }
+ case DIJOFS_RX:
+ {
+ return GAMEPAD_RX;
+ }
+ case DIJOFS_RY:
+ {
+ return GAMEPAD_RY;
+ }
+ case DIJOFS_RZ:
+ {
+ return GAMEPAD_RZ;
+ }
+ case DIJOFS_SLIDER(0):
+ {
+ return GAMEPAD_SLIDER0;
+ }
+ case DIJOFS_SLIDER(1):
+ {
+ return GAMEPAD_SLIDER1;
+ }
+ case DIJOFS_POV(0):
+ {
+ return GAMEPAD_POV0;
+ }
+ case DIJOFS_POV(1):
+ {
+ return GAMEPAD_POV1;
+ }
+ case DIJOFS_BUTTON0:
+ {
+ return GAMEPAD_BUTTON0;
+ }
+ case DIJOFS_BUTTON1:
+ {
+ return GAMEPAD_BUTTON1;
+ }
+ case DIJOFS_BUTTON2:
+ {
+ return GAMEPAD_BUTTON2;
+ }
+ case DIJOFS_BUTTON3:
+ {
+ return GAMEPAD_BUTTON3;
+ }
+ case DIJOFS_BUTTON4:
+ {
+ return GAMEPAD_BUTTON4;
+ }
+ case DIJOFS_BUTTON5:
+ {
+ return GAMEPAD_BUTTON5;
+ }
+ case DIJOFS_BUTTON6:
+ {
+ return GAMEPAD_BUTTON6;
+ }
+ case DIJOFS_BUTTON7:
+ {
+ return GAMEPAD_BUTTON7;
+ }
+ case DIJOFS_BUTTON8:
+ {
+ return GAMEPAD_BUTTON8;
+ }
+ case DIJOFS_BUTTON9:
+ {
+ return GAMEPAD_BUTTON9;
+ }
+ case DIJOFS_BUTTON10:
+ {
+ return GAMEPAD_BUTTON10;
+ }
+ case DIJOFS_BUTTON11:
+ {
+ return GAMEPAD_BUTTON11;
+ }
+ case DIJOFS_BUTTON12:
+ {
+ return GAMEPAD_BUTTON12;
+ }
+ case DIJOFS_BUTTON13:
+ {
+ return GAMEPAD_BUTTON13;
+ }
+ case DIJOFS_BUTTON14:
+ {
+ return GAMEPAD_BUTTON14;
+ }
+ case DIJOFS_BUTTON15:
+ {
+ return GAMEPAD_BUTTON15;
+ }
+ case DIJOFS_BUTTON16:
+ {
+ return GAMEPAD_BUTTON16;
+ }
+ case DIJOFS_BUTTON17:
+ {
+ return GAMEPAD_BUTTON17;
+ }
+ case DIJOFS_BUTTON18:
+ {
+ return GAMEPAD_BUTTON18;
+ }
+ case DIJOFS_BUTTON19:
+ {
+ return GAMEPAD_BUTTON19;
+ }
+ case DIJOFS_BUTTON20:
+ {
+ return GAMEPAD_BUTTON20;
+ }
+ case DIJOFS_BUTTON21:
+ {
+ return GAMEPAD_BUTTON21;
+ }
+ case DIJOFS_BUTTON22:
+ {
+ return GAMEPAD_BUTTON22;
+ }
+ case DIJOFS_BUTTON23:
+ {
+ return GAMEPAD_BUTTON23;
+ }
+ case DIJOFS_BUTTON24:
+ {
+ return GAMEPAD_BUTTON24;
+ }
+ case DIJOFS_BUTTON25:
+ {
+ return GAMEPAD_BUTTON25;
+ }
+ case DIJOFS_BUTTON26:
+ {
+ return GAMEPAD_BUTTON26;
+ }
+ case DIJOFS_BUTTON27:
+ {
+ return GAMEPAD_BUTTON27;
+ }
+ case DIJOFS_BUTTON28:
+ {
+ return GAMEPAD_BUTTON28;
+ }
+ case DIJOFS_BUTTON29:
+ {
+ return GAMEPAD_BUTTON29;
+ }
+ case DIJOFS_BUTTON30:
+ {
+ return GAMEPAD_BUTTON30;
+ }
+ case DIJOFS_BUTTON31:
+ {
+ return GAMEPAD_BUTTON31;
+ }
+ default:
+ {
+ return NUM_GAMEPAD_BUTTONS;
+ }
+ };
+} \ No newline at end of file
diff --git a/game/code/input/Gamepad.h b/game/code/input/Gamepad.h
new file mode 100644
index 0000000..4e972de
--- /dev/null
+++ b/game/code/input/Gamepad.h
@@ -0,0 +1,107 @@
+//**********************************************************
+// C++ Class Name : Gamepad
+// ---------------------------------------------------------
+// Filetype: (HEADER)
+// Filepath: D:/simpsonsrr2/game/code/input/Gamepad.h
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#ifndef GAMEPAD_H
+#define GAMEPAD_H
+
+#include <input/RealController.h>
+
+// We need the eGamepadButtons enum to provide a sequential equivalent to DIJOFS_X...
+// so that we can use them to access our arrays.
+enum eGamepadButton
+{
+ GAMEPAD_X,
+ GAMEPAD_Y,
+ GAMEPAD_Z,
+ GAMEPAD_RX,
+ GAMEPAD_RY,
+ GAMEPAD_RZ,
+ GAMEPAD_SLIDER0,
+ GAMEPAD_SLIDER1,
+ GAMEPAD_POV0,
+ GAMEPAD_POV1,
+ GAMEPAD_BUTTON0,
+ GAMEPAD_BUTTON1,
+ GAMEPAD_BUTTON2,
+ GAMEPAD_BUTTON3,
+ GAMEPAD_BUTTON4,
+ GAMEPAD_BUTTON5,
+ GAMEPAD_BUTTON6,
+ GAMEPAD_BUTTON7,
+ GAMEPAD_BUTTON8,
+ GAMEPAD_BUTTON9,
+ GAMEPAD_BUTTON10,
+ GAMEPAD_BUTTON11,
+ GAMEPAD_BUTTON12,
+ GAMEPAD_BUTTON13,
+ GAMEPAD_BUTTON14,
+ GAMEPAD_BUTTON15,
+ GAMEPAD_BUTTON16,
+ GAMEPAD_BUTTON17,
+ GAMEPAD_BUTTON18,
+ GAMEPAD_BUTTON19,
+ GAMEPAD_BUTTON20,
+ GAMEPAD_BUTTON21,
+ GAMEPAD_BUTTON22,
+ GAMEPAD_BUTTON23,
+ GAMEPAD_BUTTON24,
+ GAMEPAD_BUTTON25,
+ GAMEPAD_BUTTON26,
+ GAMEPAD_BUTTON27,
+ GAMEPAD_BUTTON28,
+ GAMEPAD_BUTTON29,
+ GAMEPAD_BUTTON30,
+ GAMEPAD_BUTTON31,
+ NUM_GAMEPAD_BUTTONS
+};
+
+class Gamepad : public RealController
+{
+public:
+ Gamepad () ;
+ virtual ~Gamepad();
+
+ virtual bool IsInputAxis( int dxKey ) const;
+ virtual bool IsPovHat( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ void CalculatePOV( float povVal, float* up, float* down, float* right, float* left ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+private:
+ virtual void MapInputToDICode();
+
+ eGamepadButton GetButtonEnum( int dxKey ) const;
+
+private:
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_GAMEPAD_BUTTONS ][ NUM_DIRTYPES ];
+};
+#endif
diff --git a/game/code/input/Keyboard.cpp b/game/code/input/Keyboard.cpp
new file mode 100644
index 0000000..f366e57
--- /dev/null
+++ b/game/code/input/Keyboard.cpp
@@ -0,0 +1,215 @@
+#include <input/Keyboard.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//--------------------------------------------------------
+// Constructor/Destructor
+//--------------------------------------------------------
+
+Keyboard::Keyboard()
+: RealController( KEYBOARD )
+{
+ ClearMappedButtons();
+}
+
+Keyboard::~Keyboard()
+{
+}
+
+//==============================================================================
+// Keyboard::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Keyboard::IsValidInput( int dxKey ) const
+{
+ return dxKey >= DIK_ESCAPE && dxKey <= DIK_MEDIASELECT;
+}
+
+//==============================================================================
+// Keyboard::IsBannedInput
+//==============================================================================
+//
+// Description: Returns true if the key is banned for mapping.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key is banned for mapping
+//
+//==============================================================================
+
+bool Keyboard::IsBannedInput( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIK_LMENU:
+ case DIK_RMENU:
+ case DIK_LWIN:
+ case DIK_RWIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Keyboard::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - for keyboard this must be DIR_UP
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if mapped successfully
+//
+//==============================================================================
+
+bool Keyboard::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+ rAssert( dir == DIR_UP );
+
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ dxKey ] = virtualButton;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//==============================================================================
+// Keyboard::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+ rAssert( dir == DIR_UP );
+
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ dxKey ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Keyboard::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - for keyboard only DIR_UP is used.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Keyboard::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+
+ if( dir == DIR_UP )
+ {
+ return m_ButtonMap[ map ][ dxKey ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Keyboard::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Keyboard::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the keyboard
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+ }
+
+ // Reverse the di -> index map into what we want.
+ for( int di = DIK_ESCAPE; di <= NUM_KEYBOARD_BUTTONS; di++ )
+ {
+ int inputpoint = VirtualKeyToIndex[ di ];
+
+ if( inputpoint >= 0 &&
+ inputpoint < m_numInputPoints )
+ {
+ m_InputToDICode[ inputpoint ] = di;
+ }
+ }
+ }
+}
diff --git a/game/code/input/Keyboard.h b/game/code/input/Keyboard.h
new file mode 100644
index 0000000..0600dfb
--- /dev/null
+++ b/game/code/input/Keyboard.h
@@ -0,0 +1,58 @@
+//**********************************************************
+// C++ Class Name : Keyboard
+// ---------------------------------------------------------
+// Filetype: (HEADER)
+// Filepath: D:/simpsonsrr2/game/code/input/Keyboard.h
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+
+#include <input/RealController.h>
+
+const NUM_KEYBOARD_BUTTONS = 256;
+
+class Keyboard : public RealController
+{
+public:
+ Keyboard();
+ virtual ~Keyboard();
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ // Returns true if the key is banned for mapping.
+ virtual bool IsBannedInput( int dxKey ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+private:
+ // Sets up a input point index -> direct input keycode map.
+ virtual void MapInputToDICode();
+
+private:
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_KEYBOARD_BUTTONS ];
+};
+
+#endif
diff --git a/game/code/input/Mouse.cpp b/game/code/input/Mouse.cpp
new file mode 100644
index 0000000..454f111
--- /dev/null
+++ b/game/code/input/Mouse.cpp
@@ -0,0 +1,399 @@
+#include <input/Mouse.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+
+Mouse::Mouse()
+: RealController(MOUSE),
+ m_fSensitivityX(10.0f),
+ m_fSensitivityY(10.0f)
+{
+ memset( &m_absPosition, 0, sizeof( m_absPosition ) );
+ memset( &m_relPosition, 0, sizeof( m_relPosition ) );
+
+ ClearMappedButtons();
+}
+
+Mouse::~Mouse()
+{
+}
+
+//==============================================================================
+// Mouse::IsMouseAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool Mouse::IsMouseAxis( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIMOFS_X:
+ case DIMOFS_Y:
+ case DIMOFS_Z:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Mouse::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Mouse::IsValidInput( int dxKey ) const
+{
+ return GetButtonEnum( dxKey ) != NUM_MOUSE_BUTTONS;
+}
+
+//==============================================================================
+// Mouse::CalculateMouseAxis
+//==============================================================================
+//
+// Description: Calculates a direct input axis value for a mouse axis based on
+// its current mouse value. Mouse values can cover -1000..1000,
+// and this gets mapped to an axis value of -1..1.
+//
+// Parameters: mouseAxis - the axis that's changed.
+// value - the value of the mouse axis
+//
+// Return: input point value of -1..1
+//
+//==============================================================================
+
+float Mouse::CalculateMouseAxis( int mouseAxis, float value )
+{
+ const float MAX_THRESH = 1.0f;
+ const float MIN_THRESH = -1.0f;
+
+ if( value == 0.0f )
+ {
+ return 0.0f;
+ }
+
+ switch( mouseAxis )
+ {
+ case DIMOFS_X:
+ {
+ //take the delta
+ m_relPosition.x = (value - m_absPosition.x) / m_fSensitivityX;
+
+ //re-center the cursor pos.
+ m_absPosition.x = value;
+
+ if( m_relPosition.x > MAX_THRESH ) m_relPosition.x = MAX_THRESH;
+ else if( m_relPosition.x < MIN_THRESH ) m_relPosition.x = MIN_THRESH;
+
+ return m_relPosition.x;
+ }
+ break;
+ case DIMOFS_Y:
+ case DIMOFS_Z:
+ {
+ //take the delta
+ m_relPosition.y = (value - m_absPosition.y) / m_fSensitivityY;
+
+ //re-center the cursor pos.
+ m_absPosition.y = value;
+
+ if( m_relPosition.y > MAX_THRESH ) m_relPosition.y = MAX_THRESH;
+ else if( m_relPosition.y < MIN_THRESH ) m_relPosition.y = MIN_THRESH;
+
+ return -m_relPosition.y;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0.0f;
+}
+
+//==============================================================================
+// Mouse::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if button was mapped successfully
+//
+//==============================================================================
+
+bool Mouse::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ mbutton ][ dir ] = virtualButton;
+ }
+
+ return mbutton != NUM_MOUSE_BUTTONS;
+}
+
+//==============================================================================
+// Mouse::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ mbutton ][ dir ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Mouse::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Mouse::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ return m_ButtonMap[ map ][ mbutton ][ dir ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Mouse::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Mouse::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the mouse
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ // Get/set each input point.
+ // We unfortunately have to special case each direct input code.
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+ const char *name = m_radController->GetInputPointByIndex( i )->GetName();
+
+ if( strcmp( type, "RelXAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_X;
+ }
+ else if( strcmp( type, "RelYAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_Y;
+ }
+ else if( strcmp( type, "RelZAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_Z;
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ int button;
+
+ if( sscanf( name, "Button %d", &button ) == 1 )
+ {
+ switch( button )
+ {
+ case 0:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON0;
+ break;
+ }
+ case 1:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON1;
+ break;
+ }
+ case 2:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON2;
+ break;
+ }
+ case 3:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON3;
+ break;
+ }
+ case 4:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON4;
+ break;
+ }
+ case 5:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON5;
+ break;
+ }
+ case 6:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON6;
+ break;
+ }
+ case 7:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON7;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//==============================================================================
+// Mouse::GetButtonEnum
+//==============================================================================
+//
+// Description: Returns the mouse button enum for the given input, or
+// NUM_MOUSE_BUTTONS if it's not a valid mouse input.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+eMouseButton Mouse::GetButtonEnum( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIMOFS_X:
+ {
+ return MOUSE_X;
+ }
+ case DIMOFS_Y:
+ {
+ return MOUSE_Y;
+ }
+ case DIMOFS_Z:
+ {
+ return MOUSE_Z;
+ }
+ case DIMOFS_BUTTON0:
+ {
+ return MOUSE_BUTTON0;
+ }
+ case DIMOFS_BUTTON1:
+ {
+ return MOUSE_BUTTON1;
+ }
+ case DIMOFS_BUTTON2:
+ {
+ return MOUSE_BUTTON2;
+ }
+ case DIMOFS_BUTTON3:
+ {
+ return MOUSE_BUTTON3;
+ }
+ case DIMOFS_BUTTON4:
+ {
+ return MOUSE_BUTTON4;
+ }
+ case DIMOFS_BUTTON5:
+ {
+ return MOUSE_BUTTON5;
+ }
+ case DIMOFS_BUTTON6:
+ {
+ return MOUSE_BUTTON6;
+ }
+ case DIMOFS_BUTTON7:
+ {
+ return MOUSE_BUTTON7;
+ }
+ default:
+ {
+ return NUM_MOUSE_BUTTONS;
+ }
+ }
+}
diff --git a/game/code/input/Mouse.h b/game/code/input/Mouse.h
new file mode 100644
index 0000000..7f33402
--- /dev/null
+++ b/game/code/input/Mouse.h
@@ -0,0 +1,80 @@
+/******************************************************************************
+ File: Mouse.h
+ Desc: Interface for the Mouse class.
+ - Responsible for managing relative axis info
+ for the mouse and other mouse related stuff.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#ifndef MOUSE_H
+#define MOUSE_H
+
+#include <input/RealController.h>
+
+struct MouseCoord
+{
+ float x, y;
+};
+
+// We need the eMouseButtons enum to provide a sequential equivalent to DIMOFS_X...
+// so that we can use them to access our arrays.
+enum eMouseButton
+{
+ MOUSE_X,
+ MOUSE_Y,
+ MOUSE_Z,
+ MOUSE_BUTTON0,
+ MOUSE_BUTTON1,
+ MOUSE_BUTTON2,
+ MOUSE_BUTTON3,
+ MOUSE_BUTTON4,
+ MOUSE_BUTTON5,
+ MOUSE_BUTTON6,
+ MOUSE_BUTTON7,
+ NUM_MOUSE_BUTTONS
+};
+
+class Mouse : public RealController
+{
+public:
+ Mouse();
+ virtual ~Mouse();
+
+ float XPos() const { return m_relPosition.x; }
+ float YPos() const { return m_relPosition.y; }
+ void setSensitivityX( float fSensitivityX ) { m_fSensitivityX = fSensitivityX; }
+ void setSensitivityY( float fSensitivityY ) { m_fSensitivityY = fSensitivityY; }
+ float getSensitivityX() const { return m_fSensitivityX; }
+ float getSensitivityY() const { return m_fSensitivityY; }
+
+ virtual bool IsMouseAxis( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ float CalculateMouseAxis( int mouseAxis, float value );
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+
+private:
+ virtual void MapInputToDICode();
+
+ eMouseButton GetButtonEnum( int dxKey ) const;
+
+private:
+ MouseCoord m_relPosition;
+ MouseCoord m_absPosition;
+ float m_fSensitivityX;
+ float m_fSensitivityY;
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_MOUSE_BUTTONS ][ NUM_DIRTYPES ];
+};
+#endif
diff --git a/game/code/input/MouseCursor.cpp b/game/code/input/MouseCursor.cpp
new file mode 100644
index 0000000..bcedf16
--- /dev/null
+++ b/game/code/input/MouseCursor.cpp
@@ -0,0 +1,149 @@
+#include <input/MouseCursor.h>
+
+// THIS CURSOR STUFF IS JUST FOR TESTING!.
+static void StreamLine(pddiPrimStream* pstream, rmt::Vector a, rmt::Vector b, tColour colora, tColour colorb)
+{
+ pddiVector pa;
+ pddiVector pb;
+
+ pa.x = a.x;
+ pa.y = a.y;
+ pa.z = a.z;
+
+ pb.x = b.x;
+ pb.y = b.y;
+ pb.z = b.z;
+
+ pstream->Vertex(&pa, colora);
+ pstream->Vertex(&pb, colorb);
+}
+static void StreamLine(pddiPrimStream* pstream, rmt::Vector a, rmt::Vector b, tColour color)
+{
+ StreamLine(pstream, a, b, color, color);
+}
+
+static pddiShader* simpleShader = NULL;
+
+static void DrawSquare(rmt::Vector v1, rmt::Vector v2, rmt::Vector v3, rmt::Vector v4,
+ tColour color = tColour(255, 255, 255),
+ pddiPrimType primType = PDDI_PRIM_LINES)
+{
+ if(simpleShader == NULL)
+ {
+ simpleShader = p3d::device->NewShader("simple");
+ simpleShader->AddRef();
+ simpleShader->SetInt(PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA);
+ }
+
+ pddiPrimStream* pStream;
+ pStream = p3d::pddi->BeginPrims(simpleShader, primType, PDDI_V_C, 8);
+ if(pStream)
+ {
+ StreamLine(pStream, v1, v2, color);
+ StreamLine(pStream, v2, v4, color);
+ StreamLine(pStream, v4, v3, color);
+ StreamLine(pStream, v3, v1, color);
+ }
+ p3d::pddi->EndPrims(pStream);
+}
+
+/*
+* DrawSquare:
+* the square is defined as:
+*
+* v1 ---------+
+* | |
+* | |
+* | |
+* | |
+* +--------- v2
+*
+*/
+static void DrawSquare(rmt::Vector v1, rmt::Vector v2,
+ tColour color = tColour(255, 255, 255),
+ pddiPrimType primType = PDDI_PRIM_LINES)
+{
+ DrawSquare(
+ rmt::Vector(v1.x, v1.y, 0),
+ rmt::Vector(v2.x, v1.y, 0),
+ rmt::Vector(v1.x, v2.y, 0),
+ rmt::Vector(v2.x, v2.y, 0),
+ color, primType);
+}
+
+/******************************************************************************
+ Some global constants/defines for this file.
+ *****************************************************************************/
+
+
+/******************************************************************************
+ Initialization of static member variables.
+ *****************************************************************************/
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+
+MouseCursor::MouseCursor()
+: m_bVisible(false),
+ m_position(),
+ m_pCursorIcon(NULL),
+ m_width(10),
+ m_height(10)
+{
+}
+
+MouseCursor::~MouseCursor()
+{
+ tRefCounted::Release( m_pCursorIcon );
+}
+
+void MouseCursor::Set( tDrawable* pCursorIcon )
+{
+ if( !m_pCursorIcon )
+ tRefCounted::Assign(m_pCursorIcon, pCursorIcon);
+}
+
+void MouseCursor::Render()
+{
+ if( !m_bVisible ) return;
+
+ rmt::Vector v1(-0.1f, -0.1f, 0.0f);
+ rmt::Vector v2(-0.1f, 0.1f, 0.0f);
+ rmt::Vector v3(0.1f, 0.1f, 0.0f);
+ rmt::Vector v4(0.1f, -0.1f, 0.0f);
+
+ pddiProjectionMode pMode = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ // Offsets are hardcoded right now for the type of cursor that it is.
+ static float XOFFSET = 0.020f;
+ static float YOFFSET = 0.035f;
+ p3d::stack->Translate( m_position.x-XOFFSET, m_position.y-YOFFSET, m_position.z );
+
+ if (m_pCursorIcon)
+ {
+ static float scaleFactor = 0.5f;
+ p3d::stack->Scale(scaleFactor, scaleFactor, 1.0f);
+ //p3d::stack->Scale(0.0007f, 0.0007f, 0.0007f);
+ m_pCursorIcon->Display();
+ }
+ else
+ {
+ float scale = 0.2f;
+ p3d::stack->Scale(scale, scale, scale);
+ pddiColour c = pddiColour(255, 0, 0); // 0-255
+ //This should be replaced with a DrawCursor function.
+ DrawSquare(v2, v3, v1, v4, c);
+ }
+
+ p3d::pddi->Clear(PDDI_BUFFER_DEPTH | PDDI_BUFFER_STENCIL);
+
+ p3d::stack->Pop();
+
+ //Restore things we've messed with
+ p3d::pddi->SetProjectionMode(pMode);
+} \ No newline at end of file
diff --git a/game/code/input/MouseCursor.h b/game/code/input/MouseCursor.h
new file mode 100644
index 0000000..43e2119
--- /dev/null
+++ b/game/code/input/MouseCursor.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ File: MouseCursor.h
+ Desc: Interface for the Mouse cursor.
+ - Responsible for drawing the 3D/2D cursor.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#ifndef MOUSECURSOR_H
+#define MOUSECURSOR_H
+
+class MouseCursor
+{
+public:
+ MouseCursor();
+ ~MouseCursor();
+
+ void SetPos( float x, float y, float z = 10.0f )
+ {
+ m_position.x = x;
+ m_position.y = y;
+ m_position.z = z;
+ }
+
+ void SetVisible( bool bVisible )
+ {
+ m_bVisible = bVisible;
+ }
+
+ void SetSize( int width, int height )
+ {
+ m_width = width;
+ m_height = height;
+ }
+
+ float XPos() const { return m_position.x; }
+ float YPos() const { return m_position.y; }
+ float ZPos() const { return m_position.z; }
+
+ bool IsVisible() const { return m_bVisible; }
+
+ int Width() const { return m_width; }
+ int Height() const { return m_height; }
+
+ void Set( tDrawable* pCursorIcon );
+ void Render();
+
+private:
+ tDrawable* m_pCursorIcon;
+ rmt::Vector m_position;
+ bool m_bVisible;
+ int m_width;
+ int m_height;
+};
+
+
+#endif
diff --git a/game/code/input/RealController.cpp b/game/code/input/RealController.cpp
new file mode 100644
index 0000000..e7bf1e4
--- /dev/null
+++ b/game/code/input/RealController.cpp
@@ -0,0 +1,210 @@
+/******************************************************************************
+ File: RealController.cpp
+ Desc: Defines the RealController class.
+ Author: Neil Haran
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#include <input/RealController.h>
+#include <input/usercontrollerWin32.h>
+#include <input/inputmanager.h>
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+RealController::RealController( eControllerType type )
+: m_radController(NULL),
+ m_eControllerType(type),
+ m_bConnected(false),
+ m_InputToDICode(NULL),
+ m_numInputPoints(0)
+{
+}
+
+RealController::~RealController()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ }
+}
+
+//--------------------------------------------------------
+// Init
+//--------------------------------------------------------
+void RealController::Init( IRadController* pRadController )
+{
+// rAssert(!m_radController);
+ if( !m_radController )
+ {
+ m_radController = pRadController;
+
+ MapInputToDICode();
+ }
+}
+
+void RealController::Release()
+{
+ if( m_radController )
+ m_radController = NULL;
+}
+
+//==============================================================================
+// RealController::IsInputAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool RealController::IsInputAxis( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsMouseAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are mouse axes. (special to mouse)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a mouse axis
+//
+//==============================================================================
+
+bool RealController::IsMouseAxis( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsPovHat
+//==============================================================================
+//
+// Description: Identifies any keys that are pov hats (go in 4 directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a pov hat
+//
+//==============================================================================
+
+bool RealController::IsPovHat( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsBannedInput
+//==============================================================================
+//
+// Description: Returns true if the key is banned for mapping.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key is banned for mapping
+//
+//==============================================================================
+
+bool RealController::IsBannedInput( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::GetInputName
+//==============================================================================
+//
+// Description: Returns the name of the input
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: name
+//
+//==============================================================================
+
+const char* RealController::GetInputName( int dxKey ) const
+{
+ if( IsValidInput( dxKey ) )
+ {
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ if( GetDICode( i ) == dxKey )
+ {
+ IRadControllerInputPoint* pInputPoint = m_radController->GetInputPointByIndex( i );
+ rAssert( pInputPoint != NULL );
+
+ return pInputPoint->GetName();
+ }
+ }
+ }
+
+ rAssert( false );
+ return NULL;
+}
+
+void RealController::AddInputPoints( IRadControllerInputPoint* pInputPoint )
+{
+ rAssert(pInputPoint);
+
+ // Check if the input point is already in the list.
+ for( INPUTPOINTITER iter = m_inputPointList.begin(); iter != m_inputPointList.end(); iter++ )
+ {
+ if( *iter == pInputPoint )
+ {
+ return;
+ }
+ }
+
+ // Add the point.
+ m_inputPointList.push_back( pInputPoint );
+}
+
+void RealController::ReleaseInputPoints( UserController* parent )
+{
+ INPUTPOINTITER iter;
+
+ for( iter = m_inputPointList.begin(); iter != m_inputPointList.end(); iter++ )
+ {
+ IRadControllerInputPoint* pInputPoint = *iter;
+ if( pInputPoint )
+ {
+ pInputPoint->UnRegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( parent ) );
+ }
+ }
+ // after unregistering, we don't need this point anymore.
+ m_inputPointList.clear();
+}
+
+//==============================================================================
+// RealController::GetDICode
+//==============================================================================
+//
+// Description: Returns the direct input keycode associated with an input point,
+// or Input::INVALID_CONTROLLERID if there is no controller or input code.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+int RealController::GetDICode( int inputpoint ) const
+{
+ rAssert( inputpoint >= 0 && inputpoint < m_numInputPoints );
+
+ if( m_InputToDICode != NULL && inputpoint >= 0 && inputpoint < m_numInputPoints )
+ {
+ return m_InputToDICode[ inputpoint ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
diff --git a/game/code/input/RealController.h b/game/code/input/RealController.h
new file mode 100644
index 0000000..2377069
--- /dev/null
+++ b/game/code/input/RealController.h
@@ -0,0 +1,131 @@
+/******************************************************************************
+ File: RealController.h
+ Desc: Interface for the RealController class.
+ - RealController essentially is a physical controller class.
+ - a wrapper around IRadController.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+
+#ifndef REALCONTROLLER_H
+#define REALCONTROLLER_H
+
+//========================================
+// System Includes
+//========================================
+#include <radcontroller.hpp>
+#include <list>
+using namespace std;
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <input/controller.h>
+#include <input/mapper.h>
+#include <input/virtualinputs.hpp>
+
+class UserController;
+/*****************************************
+ Some typedefs for convienince
+ ****************************************/
+typedef ref< IRadController > RADCONTROLLER;
+typedef list< IRadControllerInputPoint* > RADINPUTPOINTLIST;
+typedef list< IRadControllerInputPoint* >::iterator INPUTPOINTITER;
+
+//=============================================================================
+// Enumerations
+//=============================================================================
+
+// Types of real controllers.
+enum eControllerType
+{
+ GAMEPAD = 0, // gamepad/joystick
+ KEYBOARD,
+ MOUSE,
+ STEERINGWHEEL,
+ NUM_CONTROLLERTYPES
+};
+
+// Directions which inputs can process.
+enum eDirectionType
+{
+ DIR_UP = 0,
+ DIR_DOWN,
+ DIR_RIGHT,
+ DIR_LEFT,
+ NUM_DIRTYPES
+};
+
+//=============================================================================
+// Class: RealController
+//=============================================================================
+//
+// Description: The base class for every device plugged in to receive input
+// from the user. There is one instance for each keyboard, mouse,
+// gamepad...
+//
+//=============================================================================
+
+class RealController
+{
+public:
+ RealController( eControllerType type );
+ virtual ~RealController();
+
+ RADCONTROLLER getController() const { return m_radController; }
+ bool IsConnected() const { return m_bConnected; }
+ void Connect() { m_bConnected = true; }
+ void Disconnect() { m_bConnected = false; }
+ eControllerType ControllerType() const { return m_eControllerType; }
+
+ void Init( IRadController* pController );
+ void Release();
+
+ // Return true if the dxKey is one of the following types of inputs.
+ virtual bool IsInputAxis( int dxKey ) const;
+ virtual bool IsMouseAxis( int dxKey ) const;
+ virtual bool IsPovHat( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const = 0;
+
+ // Returns true if the key is banned for mapping.
+ virtual bool IsBannedInput( int dxKey ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton ) = 0;
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const = 0;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton ) = 0;
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons() = 0;
+
+ // Returns the name of the input.
+ const char* GetInputName( int dxKey ) const;
+
+ // Store & release registered input points.
+ void AddInputPoints( IRadControllerInputPoint* pInputPoint );
+ void ReleaseInputPoints( UserController* parent );
+
+ // Returns the direct input code representing a given input point for
+ // the controller, or Input::INVALID_CONTROLLERID if the i.p. doesn't exist.
+ int GetDICode( int inputpoint ) const;
+
+protected:
+ // Sets up an input point index -> direct input keycode map for the
+ // controller in m_InputToDICode. Called automatically by Init().
+ virtual void MapInputToDICode() = 0;
+
+protected:
+ RADCONTROLLER m_radController;
+ bool m_bConnected;
+ eControllerType m_eControllerType;
+ RADINPUTPOINTLIST m_inputPointList;
+ int* m_InputToDICode;
+ int m_numInputPoints;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/SteeringWheel.cpp b/game/code/input/SteeringWheel.cpp
new file mode 100644
index 0000000..7504633
--- /dev/null
+++ b/game/code/input/SteeringWheel.cpp
@@ -0,0 +1,170 @@
+#include <input/SteeringWheel.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//--------------------------------------------------------
+// Constructor/Destructor
+//--------------------------------------------------------
+
+SteeringWheel::SteeringWheel()
+: RealController( STEERINGWHEEL ),
+ m_bPedalInverted(false),
+ m_InputToDICode( NULL )
+{
+}
+
+SteeringWheel::~SteeringWheel()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ }
+}
+
+//==============================================================================
+// SteeringWheel::Init
+//==============================================================================
+//
+// Description: Initializes the wheel and figures out what special type it is.
+//
+// Parameters: controller - the rad controller for the steering wheel
+//
+// Return: None.
+//
+//==============================================================================
+
+void SteeringWheel::Init( IRadController* pController )
+{
+ // Clear the properties
+ m_bPedalInverted = false;
+
+ if( pController != NULL )
+ {
+ // Check for the wingman - a bad bad wheel.
+ m_bPedalInverted = (_stricmp( pController->GetType(), "Logitech WingMan Formula Force GP USB") == 0);
+ }
+
+ // Connect
+ RealController::Init( pController );
+}
+
+//==============================================================================
+// SteeringWheel::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the keyboard
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void SteeringWheel::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ unsigned num = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ num ];
+
+ for( unsigned i = 0; i < num; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+
+ if( strcmp( type, "XAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_X;
+ }
+ else if( strcmp( type, "YAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Y;
+ }
+ else if( strcmp( type, "Slider" ) == 0 )
+ {
+ // figure out which slider it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Slider", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_SLIDER(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ // figure out which button it is
+ for( int j = 0; j < 32; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Button", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_BUTTON(j);
+ break;
+ }
+ }
+ }
+ // Now make the input code controller independent.
+ m_InputToDICode[ i ] = GetIndependentDICode( m_InputToDICode[ i ] );
+ }
+ }
+}
+
+//==============================================================================
+// SteeringWheel::GetDICode
+//==============================================================================
+//
+// Description: Returns the direct input keycode associated with an input point,
+// or -1 if there is no controller or input code.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+int SteeringWheel::GetDICode( int inputpoint ) const
+{
+ return m_InputToDICode == NULL ? Input::INVALID_CONTROLLERID : m_InputToDICode[ inputpoint ];
+}
+
+//==============================================================================
+// SteeringWheel::GetIndependentDICode
+//==============================================================================
+//
+// Description: Same as gamepad
+//
+// Parameters: The direct input code for the specific controller.
+//
+// Return: The inpendent & real direct input code they should have assigned
+// it. Often this is the same code.
+//
+//==============================================================================
+
+int SteeringWheel::GetIndependentDICode( int diCode ) const
+{
+ switch( diCode )
+ {
+ case DIJOFS_SLIDER(0):
+ {
+ return m_bPedalInverted ? DIJOFS_Y : DIJOFS_SLIDER(0);
+ }
+ default:
+ {
+ return diCode;
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/input/SteeringWheel.h b/game/code/input/SteeringWheel.h
new file mode 100644
index 0000000..7742958
--- /dev/null
+++ b/game/code/input/SteeringWheel.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+ File: SteeringWheel.h
+ Desc: Interface for the SteeringWheel class.
+
+ Date: May 16, 2003
+ History:
+*****************************************************************************/
+#ifndef STEERINGWHEEL_H
+#define STEERINGWHEEL_H
+
+#include <input/RealController.h>
+
+class SteeringWheel : public RealController
+{
+public:
+ SteeringWheel();
+ virtual ~SteeringWheel();
+
+ virtual void Init( IRadController* pController );
+ virtual int GetDICode( int inputpoint ) const;
+ bool IsPedalInverted() const { return m_bPedalInverted; }
+
+private:
+ virtual void MapInputToDICode();
+ int GetIndependentDICode( int diCode ) const;
+
+private:
+ int* m_InputToDICode;
+ bool m_bPedalInverted;
+};
+#endif
diff --git a/game/code/input/allinput.cpp b/game/code/input/allinput.cpp
new file mode 100644
index 0000000..5967c24
--- /dev/null
+++ b/game/code/input/allinput.cpp
@@ -0,0 +1,20 @@
+#include <input/button.cpp>
+#include <input/inputmanager.cpp>
+#include <input/mappable.cpp>
+#include <input/mapper.cpp>
+#include <input/usercontroller.cpp>
+#include <input/rumbleeffect.cpp>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+#include <input/steeringspring.cpp>
+#include <input/basedamper.cpp>
+#include <input/forceeffect.cpp>
+#include <input/constanteffect.cpp>
+#include <input/wheelrumble.cpp>
+#endif
+
+#ifdef RAD_PS2
+#include <input/rumbleps2.cpp>
+#elif defined( RAD_GAMECUBE )
+#include <input/rumblegc.cpp>
+#endif
diff --git a/game/code/input/basedamper.cpp b/game/code/input/basedamper.cpp
new file mode 100644
index 0000000..edafbb0
--- /dev/null
+++ b/game/code/input/basedamper.cpp
@@ -0,0 +1,185 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: BaseDamper.cpp
+//
+// Description: Implement BaseDamper
+//
+// History: 10/21/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/BaseDamper.h>
+#include <input/wheeldefines.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BaseDamper::BaseDamper
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BaseDamper::BaseDamper()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_conditon.lOffset = 0;
+ m_conditon.lDeadBand = 0;
+ m_conditon.lPositiveCoefficient = 127;
+ m_conditon.lNegativeCoefficient = 127;
+ m_conditon.dwPositiveSaturation = 127;
+ m_conditon.dwNegativeSaturation = 127;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONDITION);
+ mForceEffect.lpvTypeSpecificParams = &m_conditon;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_DAMPER;
+ mForceEffect.duration = LG_DURATION_INFINITE;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.condition[0].offset = 0;
+ mForceEffect.p.condition[0].deadband = 0;
+ mForceEffect.p.condition[0].saturationNeg = 127;
+ mForceEffect.p.condition[0].saturationPos = 127;
+ mForceEffect.p.condition[0].coefficientNeg = 127;
+ mForceEffect.p.condition[0].coefficientPos = 127;
+#endif
+}
+
+//==============================================================================
+// BaseDamper::~BaseDamper
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BaseDamper::~BaseDamper()
+{
+}
+
+//=============================================================================
+// BaseDamper::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::OnInit()
+{
+}
+
+//=============================================================================
+// BaseDamper::SetCenterPoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s8 point, u8 deadband )
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::SetCenterPoint( s8 point, u8 deadband )
+{
+#ifdef RAD_WIN32
+ m_conditon.lOffset = point;
+ m_conditon.lDeadBand = deadband;
+#else
+ mForceEffect.p.condition[0].offset = point;
+ mForceEffect.p.condition[0].deadband = deadband;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// BaseDamper::SetDamperStrength
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 strength )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void BaseDamper::SetDamperStrength( u16 strength )
+#else
+void BaseDamper::SetDamperStrength( u8 strength )
+#endif
+{
+#ifdef RAD_WIN32
+ m_conditon.dwPositiveSaturation = strength;
+ m_conditon.dwNegativeSaturation = strength;
+#else
+ mForceEffect.p.condition[0].saturationNeg = strength;
+ mForceEffect.p.condition[0].saturationPos = strength;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// BaseDamper::SetDamperCoefficient
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 coeff )
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::SetDamperCoefficient( s16 coeff )
+{
+#ifdef RAD_WIN32
+ m_conditon.lPositiveCoefficient = coeff;
+ m_conditon.lNegativeCoefficient = coeff;
+#else
+ mForceEffect.p.condition[0].coefficientNeg = coeff;
+ mForceEffect.p.condition[0].coefficientPos = coeff;
+#endif
+
+ mEffectDirty = true;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/input/basedamper.h b/game/code/input/basedamper.h
new file mode 100644
index 0000000..66b0dec
--- /dev/null
+++ b/game/code/input/basedamper.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: basedamper.h
+//
+// Description: Blahblahblah
+//
+// History: 10/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BASEDAMPER_H
+#define BASEDAMPER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BaseDamper : public ForceEffect
+{
+public:
+ BaseDamper();
+ virtual ~BaseDamper();
+
+ void OnInit();
+
+ void SetCenterPoint( s8 degrees, u8 deadband ); //Where 0 is straight up.
+#ifdef RAD_WIN32
+ void SetDamperStrength( u16 strength );
+#else
+ void SetDamperStrength( u8 strength );
+#endif
+ void SetDamperCoefficient( s16 coeff );
+private:
+
+#ifdef RAD_WIN32
+ DICONDITION m_conditon;
+#endif
+
+ //Prevent wasteful constructor creation.
+ BaseDamper( const BaseDamper& basedamper );
+ BaseDamper& operator=( const BaseDamper& basedamper );
+};
+
+
+#endif //BASEDAMPER_H
diff --git a/game/code/input/button.cpp b/game/code/input/button.cpp
new file mode 100644
index 0000000..7e530a6
--- /dev/null
+++ b/game/code/input/button.cpp
@@ -0,0 +1,14 @@
+#include <input/button.h>
+
+unsigned int Button::mTickCount = 0;
+
+Button::Button( void ) :
+ mfValue( 0.0f ),
+ mTickCountAtChange( 0 )
+{
+}
+
+Button::~Button( void )
+{
+}
+
diff --git a/game/code/input/button.h b/game/code/input/button.h
new file mode 100644
index 0000000..7c76288
--- /dev/null
+++ b/game/code/input/button.h
@@ -0,0 +1,71 @@
+#ifndef BUTTON_H_
+#define BUTTON_H_
+
+// An input point, either physical (if storeig in a UserController), or logical (if stored in a Mappable
+// (even joysticks are treated as "buttons" by the game)
+class Button
+{
+public:
+ Button( void );
+ ~Button( void );
+
+ // test the properties of the button
+ inline float GetValue( void ) const;
+ inline void SetValue( float fValue );
+ inline bool IsDown( void ) const;
+ inline bool IsUp( void ) const;
+
+ // returns number of input ticks since the value of this button changed
+ inline unsigned int TimeSinceChange( void ) const;
+
+ // force the change flag to be set without resetting the value
+ inline void ForceChange(void);
+
+ inline static void Tick(int timeinms) { mTickCount += timeinms; }
+
+protected:
+ float mfValue;
+ unsigned int mTickCountAtChange;
+
+ // counter for current number of input ticks (for tracking
+ static unsigned int mTickCount;
+};
+
+// Inline member functions
+float Button::GetValue( void ) const
+{
+ return mfValue;
+}
+
+void Button::ForceChange(void)
+{
+ mTickCountAtChange = mTickCount;
+}
+
+void Button::SetValue( float fValue )
+{
+ mTickCountAtChange = mTickCount;
+ mfValue = fValue;
+}
+
+bool Button::IsDown( void ) const
+{
+ return mfValue != 0.0f;
+}
+
+bool Button::IsUp( void ) const
+{
+ return mfValue == 0.0f;
+}
+
+unsigned int Button::TimeSinceChange( void ) const
+{
+ return mTickCount - mTickCountAtChange;
+}
+
+// nbrooke 15/10/02 :I devirtualized the Button class (there was a lot of hierarchy
+// and virtual functions considering there was only one class avtually used my any other code)
+// but didn't feel like changing a bunch of stuff that was using IButton, thus the typedef
+typedef Button IButton;
+
+#endif \ No newline at end of file
diff --git a/game/code/input/constanteffect.cpp b/game/code/input/constanteffect.cpp
new file mode 100644
index 0000000..c0797a6
--- /dev/null
+++ b/game/code/input/constanteffect.cpp
@@ -0,0 +1,163 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: constanteffect.cpp
+//
+// Description: Implement ConstantEffect
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/constanteffect.h>
+#include <input/wheeldefines.h>
+#include <input/inputmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ConstantEffect::ConstantEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ConstantEffect::ConstantEffect()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = 0;
+ m_diEnvelope.dwSize = sizeof(DIENVELOPE);
+ m_diEnvelope.dwAttackLevel = 0;
+ m_diEnvelope.dwAttackTime = 0;
+ m_diEnvelope.dwFadeLevel = 0;
+ m_diEnvelope.dwFadeTime = 0;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = 500;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
+ mForceEffect.lpvTypeSpecificParams = &m_diConstant;
+ mForceEffect.dwStartDelay = 0;
+
+#else
+ mForceEffect.type = LG_TYPE_CONSTANT;
+ mForceEffect.duration = 500;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.constant.magnitude = 0;
+ mForceEffect.p.constant.direction = 0;
+ mForceEffect.p.constant.envelope.attackTime = 0;
+ mForceEffect.p.constant.envelope.fadeTime = 0;
+ mForceEffect.p.constant.envelope.attackLevel = 0;
+ mForceEffect.p.constant.envelope.fadeLevel = 0;
+#endif
+}
+
+//=============================================================================
+// ConstantEffect::~ConstantEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ConstantEffect::~ConstantEffect()
+{
+}
+
+//=============================================================================
+// ConstantEffect::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::OnInit()
+{
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = 0;
+#else
+ mForceEffect.p.constant.magnitude = 0;
+#endif
+}
+
+//=============================================================================
+// ConstantEffect::SetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 magnitude )
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::SetMagnitude( s16 magnitude )
+{
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = magnitude;
+#else
+ mForceEffect.p.constant.magnitude = magnitude;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// ConstantEffect::SetDirection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u16 direction )
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::SetDirection( u16 direction )
+{
+#ifdef RAD_WIN32
+ LONG rglDirection[2] = { direction, 0 };
+ mForceEffect.rglDirection = rglDirection;
+#else
+ mForceEffect.p.constant.direction = direction;
+#endif
+
+ mEffectDirty = true;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/constanteffect.h b/game/code/input/constanteffect.h
new file mode 100644
index 0000000..ed9ff9e
--- /dev/null
+++ b/game/code/input/constanteffect.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: constanteffect.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CONSTANTEFFECT_H
+#define CONSTANTEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ConstantEffect : public ForceEffect
+{
+public:
+ ConstantEffect();
+ virtual ~ConstantEffect();
+
+ void OnInit();
+
+ void SetMagnitude( s16 magnitude );
+ void SetDirection( u16 direction );
+
+private:
+
+#ifdef RAD_WIN32
+ DICONSTANTFORCE m_diConstant;
+ DIENVELOPE m_diEnvelope;
+#endif
+
+ //Prevent wasteful constructor creation.
+ ConstantEffect( const ConstantEffect& constanteffect );
+ ConstantEffect& operator=( const ConstantEffect& constanteffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //CONSTANTEFFECT_H
diff --git a/game/code/input/controller.h b/game/code/input/controller.h
new file mode 100644
index 0000000..d98482a
--- /dev/null
+++ b/game/code/input/controller.h
@@ -0,0 +1,100 @@
+#ifndef CONTROLLER_HPP
+#define CONTROLLER_HPP
+
+namespace Input
+{
+ // Platform specific controller topology.
+ #ifdef RAD_XBOX
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+ #ifdef RAD_PS2
+ const static unsigned int MaxPorts = 2;
+ const static unsigned int MaxSlots = 4;
+ #endif
+ #ifdef RAD_GAMECUBE
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+ #if defined( RAD_WIN32 )
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+
+ #ifdef RAD_PS2
+ enum USBID
+ {
+ USB0 = (MaxPorts * MaxSlots),
+ USB1 = (MaxPorts * MaxSlots) + 1
+ };
+ static const unsigned int MaxUSB = 2;
+
+ // Platform specific max controllers.
+ const static unsigned int MaxControllers = (MaxPorts * MaxSlots) + MaxUSB;
+
+ #else //NOT PS2
+ // Platform specific max controllers.
+ const static unsigned int MaxControllers = (MaxPorts * MaxSlots);
+ #endif
+
+
+ #ifdef RAD_WIN32
+ const static unsigned int NumExtraButtonsForSuperSprint = 7;
+ #endif
+
+ // Maximum number of physical buttons (in a UserController)
+ #ifdef RAD_WIN32
+ const static unsigned int MaxPhysicalButtons = 42 + NumExtraButtonsForSuperSprint;
+ #else
+ const static unsigned int MaxPhysicalButtons = 40;
+ #endif
+
+ // Maximum number of logical buttons (in a UserController)
+ #ifdef RAD_WIN32
+ const static unsigned int MaxLogicalButtons = 42 + NumExtraButtonsForSuperSprint;
+ #else
+ const static unsigned int MaxLogicalButtons = 40;
+ #endif
+
+ // Maximum number of logical controllers for a physical device
+ const static unsigned int MaxMappables = 16;
+
+ // Maximum number of control mappings for a logical controller
+ const static unsigned int MaxMappings = 2;
+
+ // Maximum number of physical keys that can be assigned to a virtual key
+ // for a controller.
+ #ifdef RAD_WIN32
+ const static unsigned int MaxVirtualMappings = 2;
+ #endif
+
+ // Maximum number of rumble motors
+ #ifdef RAD_GAMECUBE
+ const static unsigned int MaxOutputMotor = 1;
+ #else
+ const static unsigned int MaxOutputMotor = 2;
+ #endif
+
+ // Control system state (for keeping input roped in in certain gameplay states)
+ enum ActiveState
+ {
+ ACTIVE_NONE = 0,
+ ACTIVE_GAMEPLAY = 1 << 0,
+ ACTIVE_FRONTEND = 1 << 1,
+ ACTIVE_SS_GAME = 1 << 2,
+ ACTIVE_FIRST_PERSON = 1 << 3,
+ ACTIVE_ANIM_CAM = 1 << 4,
+ DEACTIVE_ANIM_CAM = 0xfffffffe,
+ ACTIVE_ALL = 0xffffffff
+ };
+
+ // handy constant for a bunk controller / input
+ enum
+ {
+ INVALID_CONTROLLERID = -1
+ };
+};
+
+
+
+#endif
diff --git a/game/code/input/forceeffect.cpp b/game/code/input/forceeffect.cpp
new file mode 100644
index 0000000..70fb63c
--- /dev/null
+++ b/game/code/input/forceeffect.cpp
@@ -0,0 +1,195 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: forceeffect.cpp
+//
+// Description: Implement ForceEffect
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/forceeffect.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ForceEffect::ForceEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ForceEffect::ForceEffect() :
+mOutputPoint( NULL ),
+#ifdef RAD_WIN32
+m_effectTime(0),
+m_currentTime(0),
+#endif
+mEffectDirty( true )
+{
+#ifdef RAD_WIN32
+ ZeroMemory( &mForceEffect, sizeof(mForceEffect) );
+ ZeroMemory( &m_rglDirection, sizeof(m_rglDirection) );
+
+ mForceEffect.dwSize = sizeof(DIEFFECT);
+
+ m_rgdwAxes[0] = DIJOFS_X;
+ m_rgdwAxes[1] = DIJOFS_Y;
+#endif
+}
+
+//=============================================================================
+// ForceEffect::~ForceEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ForceEffect::~ForceEffect()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Stop();
+ mOutputPoint->Release();
+ mOutputPoint = NULL;
+ }
+}
+
+//=============================================================================
+// ForceEffect::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* outputPoint )
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Init( IRadControllerOutputPoint* outputPoint )
+{
+#ifdef RAD_WIN32
+ rAssertMsg( outputPoint != NULL, "Attempt to set the outputPoint with a NULL pointer." );
+ // mOutputPoint is dirty.
+ if ( mOutputPoint )
+ {
+ ShutDownEffects();
+ }
+#endif
+ mOutputPoint = outputPoint;
+ mOutputPoint->AddRef();
+
+ OnInit();
+
+ mOutputPoint->UpdateEffect( &mForceEffect );
+}
+
+//=============================================================================
+// ForceEffect::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Start()
+{
+ //Clear out current effects.
+ OnInit();
+ mEffectDirty = true;
+
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Start();
+ }
+}
+
+//=============================================================================
+// ForceEffect::Stop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Stop()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Stop();
+ }
+}
+
+//=============================================================================
+// ForceEffect::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void ForceEffect::Update(unsigned timeins)
+#else
+void ForceEffect::Update()
+#endif
+{
+ if ( mEffectDirty && mOutputPoint )
+ {
+ mOutputPoint->UpdateEffect( &mForceEffect );
+ mEffectDirty = false;
+ }
+}
+#ifdef RAD_WIN32
+void ForceEffect::ShutDownEffects()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->ReleaseEffect();
+ mOutputPoint->Release();
+ mOutputPoint = NULL;
+ }
+}
+
+void ForceEffect::SetResetTime( DWORD dwMilliSeconds )
+{
+ m_effectTime = dwMilliSeconds;
+}
+#endif
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/forceeffect.h b/game/code/input/forceeffect.h
new file mode 100644
index 0000000..050c284
--- /dev/null
+++ b/game/code/input/forceeffect.h
@@ -0,0 +1,104 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: forceeffect.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FORCEEFFECT_H
+#define FORCEEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#ifdef RAD_GAMECUBE
+#include <dolphin/lg.h>
+#endif
+
+#ifdef RAD_PS2
+#include <liblgdev.h>
+#endif
+
+#ifdef RAD_WIN32
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#endif
+
+#if defined(RAD_PS2) || defined(RAD_WIN32)
+typedef char s8;
+typedef unsigned char u8;
+typedef short s16;
+typedef unsigned short u16;
+typedef unsigned int u32;
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ForceEffect
+{
+public:
+ ForceEffect();
+ virtual ~ForceEffect();
+
+#ifdef RAD_WIN32
+ void SetForceID( u8 forceID ) { m_index = forceID; }
+ u8 GetForceID() const { return m_index; }
+
+#endif
+
+ void Init( IRadControllerOutputPoint* outputPoint );
+ bool IsInit() const { return ( mOutputPoint != NULL ); };
+
+ void Start();
+ void Stop();
+
+#ifdef RAD_WIN32
+ virtual void Update(unsigned timeins = 0);
+ void ShutDownEffects();
+ void SetResetTime( DWORD dwMilliSeconds );
+#else
+ void Update();
+#endif
+
+protected:
+ IRadControllerOutputPoint* mOutputPoint;
+#ifdef RAD_WIN32
+ DIEFFECT mForceEffect;
+ DWORD m_rgdwAxes[2];
+ LONG m_rglDirection[2];
+ u8 m_index; // the index ID of this force.
+ DWORD m_currentTime;
+ DWORD m_effectTime;
+#else
+ LGForceEffect mForceEffect;
+#endif
+ bool mEffectDirty;
+
+ virtual void OnInit() = 0;
+
+ //Prevent wasteful constructor creation.
+ ForceEffect( const ForceEffect& forceeffect );
+ ForceEffect& operator=( const ForceEffect& forceeffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //FORCEEFFECT_H
diff --git a/game/code/input/inputPointList.h b/game/code/input/inputPointList.h
new file mode 100644
index 0000000..2d581d5
--- /dev/null
+++ b/game/code/input/inputPointList.h
@@ -0,0 +1,176 @@
+#if !defined(AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_)
+#define AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+/******************************************************************************
+ List
+
+ Date: 19-11-01
+
+ Name: Jeff Giles
+ Ezeikeil@Hotmail.com
+
+ Notes: A Simple, doubly linked list which returns NULL when run off
+ either end.
+
+
+******************************************************************************/
+template <class type>
+class List
+{
+ //*Node struct*******************************************************************
+ // Notes: The workhorse of the Linked List
+ //*****************************************************************************
+ struct Node
+ {
+ type * thedata; //pointer to the data
+ Node * nextnode; //pointer to the next node in the list
+ Node * lastnode; //pointer to the previous node in the list
+
+ //Ctors
+ Node(): thedata(0),nextnode(0),lastnode(0) {};
+ Node(type* data, Node* next): thedata(data),nextnode(next),lastnode(0) {};
+ };
+
+public:
+ //*Ctor/Dtor*****************************************************************
+ List(): myHead(0),myTail(0), numNodes(0) {};
+ virtual ~List(){};
+
+ //*GetSize*********************************************************************
+ // Notes: returns the current number of nodes
+ //*****************************************************************************
+ int GetSize()
+ {
+ return numNodes;
+ }
+
+ //*PushFront*******************************************************************
+ // Notes: Pushes an element onto the top of the list
+ //*****************************************************************************
+ void PushFront(type * data)
+ {
+ myHead = new Node(data, myHead);
+
+ if (myTail == NULL)
+ myTail = myHead;
+
+ if(myHead->nextnode !=NULL) //redirect the node following myHead
+ myHead->nextnode->lastnode = myHead; //to point to the new head
+
+ numNodes++;
+ }
+
+ //*PushBack******************************************************************
+ // Notes: Pushes an element onto the bottom of the list
+ //***************************************************************************
+ void PushBack(type * data)
+ {
+ if (myHead == NULL) //then the list must be empty
+ PushFront(data);
+ else
+ {
+ Node * temp = new Node;
+ temp->thedata = data;
+ temp->lastnode = myTail;
+
+ myTail= temp; //redirect myTail
+ myTail->lastnode->nextnode = temp; //redirect the nextnode before
+ //myTail to point to the new Node
+ numNodes++;
+ }
+ }
+
+ //*PopBack*********************************************************************
+ // Notes: Removes an element from the top of the list
+ //*****************************************************************************
+ type * PopBack()
+ {
+ if(myTail != NULL)
+ {
+ type * temp = myTail->thedata;
+ Node * oldtail = myTail; //keep a hold of the data to prevent memory leak
+ myTail = myTail->lastnode;
+ numNodes--;
+ delete oldtail;
+ return temp;
+ }
+ else
+ return NULL;
+ };
+
+ //*PopFront*******************************************************************
+ // Notes: Removes an element from the bottom of the list
+ //****************************************************************************
+ type * PopFront()
+ {
+
+ if(myHead != NULL)
+ {
+ type * temp = myHead->thedata;
+ Node * oldHead = myHead; //keep a hold of the data to prevent memory leak
+ myHead = myHead->nextnode;
+ numNodes--;
+ delete oldHead;
+ return temp;
+ }
+ else
+ return NULL;
+
+ };
+
+ //*PeekFront******************************************************************
+ // Notes: Accesses the top of the list without removing the node
+ //****************************************************************************
+ type * PeekFront()
+ {
+ if(myHead)
+ return myHead->thedata;
+ else
+ return NULL;
+ };
+
+ //*PeekBack*******************************************************************
+ // Notes: Accesses the bottom of the list without removing the node
+ //****************************************************************************
+ type * PeekBack()
+ {
+ if(myTail)
+ return myTail->thedata;
+ else
+ return NULL;
+ };
+
+ //*operator []****************************************************************
+ // Notes: Accesses the any node of the list without removing it
+ // Position is from top to
+ //****************************************************************************
+ type * operator[] (int index)
+ {
+ if(numNodes < 0 || index >= numNodes) //prevent overrun
+ return NULL;
+ else
+ {
+ Node * temp = myHead;
+ int counter = 0;
+
+ while (counter < index)
+ {
+ temp = temp->nextnode; //point temp at the next node in the list
+ counter++;
+ }
+ return temp->thedata;
+ }
+ };
+
+private:
+ Node * myTail;
+ Node * myHead;
+ int numNodes;
+};
+
+#endif // !defined(AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_)
diff --git a/game/code/input/inputmanager.cpp b/game/code/input/inputmanager.cpp
new file mode 100644
index 0000000..8bd8550
--- /dev/null
+++ b/game/code/input/inputmanager.cpp
@@ -0,0 +1,731 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: inputmanager.cpp
+//
+// Description: Implementation for the InputManager class.
+//
+// History: + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h> // ::strcmp()
+//FTech
+#include <raddebugwatch.hpp>
+#include <raddebug.hpp>
+#include <radcontroller.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+#include <input/button.h>
+
+#include <main/game.h>
+#include <main/platform.h>
+
+#include <data/gamedatamanager.h>
+
+#ifndef WORLD_BUILDER
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#define GMA_PERSISTENT 0
+#define GMA_DEFAULT 0
+#endif
+
+#ifdef RAD_PS2
+#include <main/ps2platform.h>
+#include <libmtap.h>
+#endif
+
+#if defined RAD_XBOX
+
+ InputManager::eButtonMap RESET_BUTTONS[] =
+ {
+ InputManager::Start,
+ InputManager::Select
+ };
+
+ const int NUM_RESET_BUTTONS = 2;
+
+#else //GC handled in gcmanager and we don't do this on the PC
+
+InputManager::eButtonMap RESET_BUTTONS[] =
+{
+ InputManager::Start
+};
+
+const int NUM_RESET_BUTTONS = 0;
+
+#endif
+
+const unsigned int RESET_TIMEOUT = 2000;
+
+//******************************************************************************
+// Global Data, Local Data, Local Classes
+//******************************************************************************
+
+//
+// Static pointer to instance of singleton.
+//
+InputManager* InputManager::spInstance = NULL;
+
+
+//******************************************************************************
+// Public Member Functions
+//******************************************************************************
+
+// Creates the InputManager.
+// This is a singleton so only one instance is allowed.
+InputManager* InputManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "InputManager" );
+
+ rAssert( spInstance == NULL );
+
+ spInstance = new InputManager;
+MEMTRACK_POP_GROUP("InputManager");
+
+ return spInstance;
+}
+
+// Access point for the InputManager singleton.
+InputManager* InputManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+// Destroy the InputManager.singleton
+void InputManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+
+void InputManager::Init()
+{
+
+MEMTRACK_PUSH_GROUP( "InputManager" );
+ ::radControllerInitialize( this, GMA_DEFAULT );
+
+ mxIControllerSystem2 = radControllerSystemGet();
+
+#ifdef RAD_PS2
+ // On PS2, check for initial button pushes.
+ m_isProScanButtonsPressed = PS2Platform::GetInstance()->CheckForStartupButtons( );
+ rDebugPrintf( "Do Progressive scan: %s\n", m_isProScanButtonsPressed ? "yup" : "nope" );
+#endif
+
+ unsigned int i = 0;
+
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ // preallocate run time controller structure.
+ mControllerArray[ i ].Create(i);
+ }
+#ifndef RAD_WIN32
+ mxIControllerSystem2->RegisterConnectionChangeCallback( this );
+#endif
+ rDebugString( "Just created User controller system\n" );
+
+ EnumerateControllers( );
+MEMTRACK_POP_GROUP("InputManager");
+
+}
+
+
+void InputManager::Update( unsigned int timeinms )
+{
+ // update the button timestamp (so we can tell when buttons were pressed)
+ Button::Tick(timeinms);
+
+ ::radControllerSystemService();
+
+ // if controllers have been disconnected, change the state
+ if(mConnectStateChanged)
+ {
+ mConnectStateChanged = false;
+ EnumerateControllers( );
+ }
+
+ unsigned int i = 0;
+
+ // update physical controllers
+ bool resetting = false;
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ if(mControllerArray[i].IsConnected())
+ {
+ mControllerArray[i].Update(timeinms);
+
+ if ( mResetEnabled &&
+ NUM_RESET_BUTTONS > 0 &&
+ !resetting &&
+ mControllerArray[i].GetInputValueRT( RESET_BUTTONS[ 0 ] ) > 0.5f &&
+ mControllerArray[i].GetInputValueRT( RESET_BUTTONS[ 1 ] ) > 0.5f )
+ {
+ resetting = true;
+ }
+ }
+ }
+
+#ifndef RAD_XBOX
+ if ( mResetEnabled )
+ {
+ if ( !resetting )
+ {
+ mResetTimeout = 0;
+ mIsResetting = false;
+ }
+ else if ( resetting && !mIsResetting )
+ {
+ mResetTimeout = RESET_TIMEOUT;
+ mIsResetting = true;
+ }
+ }
+#endif
+
+
+ // broadcast new game state if it has changed
+ if(mChangeGameState)
+ {
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ // TC: Why does controller have to be connected for its game state to be updated?
+ // Having the 'if' conditional here causes a problem when the controller is
+ // disconnnected during a state when it's non-active.
+ //
+// if(mControllerArray[i].IsConnected())
+ {
+ mControllerArray[i].SetGameState(mGameState);
+ }
+ }
+ mChangeGameState = false;
+ }
+
+ if ( mResetEnabled && mIsResetting && mResetTimeout >= 0 )
+ {
+ if ( mResetTimeout <= timeinms )
+ {
+ GetGame()->GetPlatform()->LaunchDashboard();
+ mResetTimeout = 0;
+ mIsResetting = false;
+ }
+ else
+ {
+ mResetTimeout -= timeinms;
+ }
+ }
+}
+
+
+void InputManager::OnControllerConnectionStatusChange( IRadController * pIController2 )
+{
+ mConnectStateChanged = true;
+}
+
+bool InputManager::IsControllerInPort( int portnum ) const
+{
+ rAssert(portnum < static_cast< int >( Input::MaxControllers ) );
+ return mControllerArray[portnum].IsConnected();
+}
+
+void InputManager::ToggleRumble( bool on )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ if ( mControllerArray[ i ].IsConnected() )
+ {
+ mControllerArray[ i ].SetRumble( on );
+ }
+ }
+}
+
+
+//=============================================================================
+// InputManager::SetRumbleEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isEnabled )
+//
+// Return: nothing
+//
+//=============================================================================
+void InputManager::SetRumbleEnabled( bool isEnabled )
+{
+ mIsRumbleEnabled = isEnabled;
+}
+
+void InputManager::SetRumbleForDevice( int controllerId, bool bRumbleOn )
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ mControllerArray[controllerId].SetRumble( bRumbleOn );
+ }
+}
+
+
+bool InputManager::IsRumbleOnForDevice( int controllerId ) const
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ return mControllerArray[controllerId].IsRumbleOn( );
+ }
+ return false;
+}
+
+void InputManager::TriggerRumblePulse( int controllerId )
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ mControllerArray[ controllerId ].PulseRumble();
+ }
+}
+
+// Returns the value of the input point 'inputIndex' owned by the controller at
+// controllerIndex.
+float InputManager::GetValue( unsigned int controllerIndex, unsigned int inputIndex ) const
+{
+ rAssert( controllerIndex < Input::MaxControllers);
+ if ( controllerIndex < Input::MaxControllers)
+ {
+ return mControllerArray[ controllerIndex ].GetInputValue( inputIndex );
+ }
+ else
+ {
+ return 0.0f;
+ }
+}
+
+// Returns a const pointer to the controller at controller index.
+UserController* InputManager::GetController( unsigned int controllerIndex )
+{
+ rAssert( controllerIndex < Input::MaxControllers );
+ if ( controllerIndex < Input::MaxControllers )
+ {
+ return &mControllerArray[ controllerIndex ];
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+// Associate this device with a game object.
+int InputManager::RegisterMappable( unsigned int index, Mappable *pMappable )
+{
+ int handle = Input::INVALID_CONTROLLERID;
+ if ( index < Input::MaxControllers)
+ {
+ handle = mControllerArray[ index ].RegisterMappable( pMappable );
+ }
+ return handle;
+}
+
+
+// Remove associations between this device and a game object.
+void InputManager::UnregisterMappable( unsigned int index, int handle )
+{
+ if ( handle > Input::INVALID_CONTROLLERID )
+ {
+ if ( index < Input::MaxControllers)
+ {
+ mControllerArray[ index ].UnregisterMappable( handle );
+ }
+ }
+}
+
+void InputManager::UnregisterMappable( unsigned int index, Mappable* mappable)
+{
+ mControllerArray[ index ].UnregisterMappable( mappable );
+}
+
+void InputManager::UnregisterMappable( Mappable *pMappable )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ mControllerArray[ i ].UnregisterMappable( pMappable );
+ }
+}
+
+
+void InputManager::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ mIsRumbleEnabled = ( dataBuffer[ 0 ] != 0 );
+}
+
+void InputManager::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ dataBuffer[ 0 ] = mIsRumbleEnabled ? ~0 : 0;
+}
+
+void InputManager::ResetData()
+{
+ //Rumble does not RESET. This is a TRC thing.
+ //mIsRumbleEnabled = true;
+}
+
+//******************************************************************************
+// Private Member Functions
+//******************************************************************************
+
+InputManager::InputManager()
+:
+mGameState(Input::ACTIVE_ALL),
+mChangeGameState(false),
+mConnectStateChanged(false),
+mIsRumbleEnabled(true),
+mIsResetting(false),
+mResetEnabled( false ),
+m_isProScanButtonsPressed( false )
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ m_registeredControllerID[ i ] = -1;
+ }
+
+ GetGameDataManager()->RegisterGameData( this, 1, "Input Manager" );
+#ifdef RAD_WIN32
+ m_pFEMouse = new FEMouse;
+#endif
+#ifdef RAD_PS2
+ mLastMultitapStatus[0] = mLastMultitapStatus[1] = 0;
+ mCurMultitapStatus[0] = mCurMultitapStatus[1] = 0;
+
+#endif
+}
+
+
+InputManager::~InputManager()
+{
+ ReleaseAllControllers();
+
+#ifndef RAD_WIN32
+ mxIControllerSystem2->UnRegisterConnectionChangeCallback( this );
+#endif
+ ::radControllerTerminate();
+#ifdef RAD_WIN32
+ delete m_pFEMouse;
+ m_pFEMouse = NULL;
+#endif
+}
+
+void InputManager::EnumerateControllers( void )
+{
+ // on the console the controller device and all the mappables are
+ // preallocated. So we don't have to create new associations.
+ //
+#ifndef RAD_WIN32
+ ref< IRadController > xIC2;
+#else
+ ref< IRadController > radController[ NUM_CONTROLLERTYPES ];
+#endif
+
+ ReleaseAllControllers( );
+
+ bool somethingPluggedIn0 = false;
+ bool somethingPluggedIn1 = false;
+
+ unsigned int slot = 0;
+ unsigned int port = 0;
+ for ( port = 0; port < Input::MaxPorts; ++port )
+ {
+ for ( slot = 0; slot < Input::MaxSlots; ++slot )
+ {
+ unsigned int i = port * Input::MaxSlots + slot;
+
+ char szLocation[ 256 ];
+
+#if defined(RAD_XBOX) || defined( RAD_PS2 )
+ sprintf( szLocation, "Port%d\\Slot%d", port, slot );
+#elif defined(RAD_WIN32)
+ char szJoystickLoc[ 256 ];
+ char szMouseLoc[ 256 ];
+ char szWheelLoc[ 256 ];
+
+ sprintf( szLocation, "Keyboard%d", 0);
+ sprintf( szJoystickLoc, "Joystick%d", i);
+ sprintf( szMouseLoc, "Mouse%d", i);
+ sprintf( szWheelLoc, "SteeringWheel%d", i);
+#else //This is GC
+ sprintf( szLocation, "Channel%d", i );
+#endif
+
+#ifndef RAD_WIN32
+ xIC2 = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+#else
+ radController[KEYBOARD] = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+ radController[GAMEPAD] = mxIControllerSystem2->GetControllerAtLocation( szJoystickLoc );
+ radController[MOUSE] = mxIControllerSystem2->GetControllerAtLocation( szMouseLoc );
+ radController[STEERINGWHEEL] = mxIControllerSystem2->GetControllerAtLocation( szWheelLoc );
+
+ //Check to see if the steering wheel is on controller 0.
+
+#endif
+
+ //
+ // Grab the controller device associated with the foundation controller.
+ //
+ UserController* controller = &mControllerArray[ i ];
+
+
+#ifdef RAD_WIN32
+ // One keyboard has to be present. (might want to change later)
+ if ( (radController[KEYBOARD] == NULL || !radController[KEYBOARD]->IsConnected( )) && (i == 0) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+ controller->Initialize( radController );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ radController[i] = NULL;
+ }
+ }
+#else
+ if ( xIC2 == NULL || !xIC2->IsConnected( ) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+#ifdef RAD_PS2
+ if(strcmp( "PsxDualShock2", xIC2->GetType() ) == 0 )
+ {
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ controller->SetRumble( IsRumbleEnabled() );
+ }
+ xIC2 = NULL;
+
+ if ( port == 0 )
+ {
+ somethingPluggedIn0 = true;
+ }
+ else
+ {
+ somethingPluggedIn1 = true;
+ }
+#else
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ controller->SetRumble( IsRumbleEnabled() );
+
+ xIC2 = NULL;
+#endif
+ }
+#endif
+ }
+ }
+
+#ifdef RAD_PS2
+
+ //TEST THE MULTITAP STATUS
+ if ( somethingPluggedIn0 || somethingPluggedIn1 )
+ {
+ if ( somethingPluggedIn0 )
+ {
+ mLastMultitapStatus[0] = sceMtapGetConnection(0);
+ }
+
+ if ( somethingPluggedIn1 )
+ {
+ mLastMultitapStatus[1] = sceMtapGetConnection(1);
+ }
+ }
+#endif
+
+
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ char szLocation[ 256 ];
+ sprintf( szLocation, "USB%d", i );
+
+ xIC2 = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+ //
+ // Grab the controller device associated with the foundation controller.
+ //
+ UserController* controller = &mControllerArray[ Input::USB0 + i ];
+
+ if ( xIC2 == NULL || !xIC2->IsConnected( ) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+ if ( xIC2->IsConnected( ) && strcmp( xIC2->GetClassification(), "Wheel" ) == 0 )
+ {
+ // Initialize the controller. This will notify all observers of the
+ // controller being added.
+
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ }
+ else
+ {
+ controller->NotifyDisconnect( );
+ }
+ xIC2 = NULL;
+ }
+ }
+#endif
+
+//#ifdef RAD_DEBUG
+ char connectionMap[128] = "Controller status changed (connection : ";
+
+ for( port = 0; port < Input::MaxPorts; port++)
+ {
+ for( slot = 0; slot < Input::MaxSlots; slot++)
+ {
+ int index = port * Input::MaxSlots + slot;
+ if(mControllerArray[ index ].IsConnected())
+ {
+ strcat(connectionMap, "x");
+ }
+ else
+ {
+ strcat(connectionMap, "o");
+ }
+ }
+ strcat(connectionMap," ");
+ }
+#ifdef RAD_PS2
+ for(unsigned int i = (Input::MaxSlots * Input::MaxPorts); i < Input::MaxControllers; ++i )
+ {
+ if ( mControllerArray[i].IsConnected() )
+ {
+ strcat(connectionMap,"U");
+ }
+ else
+ {
+ strcat(connectionMap, "o");
+ }
+ }
+#endif
+ strcat(connectionMap,")\n");
+
+ rReleaseString(connectionMap);
+//#endif
+}
+
+void InputManager::ReleaseAllControllers( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ mControllerArray[ i ].ReleaseRadController( );
+ }
+}
+
+
+void InputManager::SetGameState( Input::ActiveState state )
+{
+ //
+ // The only way to get out of the animated cam input state is to set it
+ // to the DEACTIVE_ANIM_CAM state, which actually sends you to active all.
+ // Perhaps the input system should really be a stack?
+ //
+ if( mGameState == Input::ACTIVE_ANIM_CAM )
+ {
+ if( state == Input::DEACTIVE_ANIM_CAM )
+ {
+ state = Input::ACTIVE_ALL;
+ }
+ else
+ {
+ state = Input::ACTIVE_ANIM_CAM;
+ }
+ }
+ else
+ {
+ if( state == Input::DEACTIVE_ANIM_CAM )
+ {
+ return;
+ }
+ }
+ mChangeGameState = true;
+ mGameState = state;
+}
+
+Input::ActiveState InputManager::GetGameState() const
+{
+ return static_cast<Input::ActiveState>(mGameState);
+}
+
+
+void
+InputManager::RegisterControllerID( int playerID, int controllerID )
+{
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ m_registeredControllerID[ playerID ] = controllerID;
+
+ rTunePrintf( "*** Registered Controller ID [%d] for Player %d ***\n",
+ controllerID, playerID + 1 );
+}
+
+void
+InputManager::UnregisterControllerID( int playerID )
+{
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ m_registeredControllerID[ playerID ] = -1;
+}
+
+void
+InputManager::UnregisterAllControllerID()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ UnregisterControllerID( i );
+ }
+}
+
+#ifdef RAD_WIN32
+
+void InputManager::StartRumbleEffects()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ SetRumbleForDevice(i, true);
+ }
+}
+
+void InputManager::StopRumbleEffects()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ SetRumbleForDevice(i, false);
+ }
+}
+
+#endif
+
diff --git a/game/code/input/inputmanager.h b/game/code/input/inputmanager.h
new file mode 100644
index 0000000..d21303e
--- /dev/null
+++ b/game/code/input/inputmanager.h
@@ -0,0 +1,414 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: inputmanager.h
+//
+// Description: InputManager class declaration.
+//
+// History: + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef InputManager_HPP
+#define InputManager_HPP
+
+//========================================
+// System Includes
+//========================================
+#include <radcontroller.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <data/gamedata.h>
+#include <input/controller.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#include <input/FEMouse.h>
+#else
+#include <input/usercontroller.h>
+#endif
+#include <constants/maxplayers.h>
+
+//========================================
+// Forward References
+//========================================
+class UserController;
+class Mappable;
+
+//==============================================================================
+//
+// Synopsis: Used to trigger events and route them to the listeners.
+//
+//==============================================================================
+class InputManager : public IRadControllerConnectionChangeCallback,
+ public GameDataHandler
+{
+public:
+#ifdef RAD_XBOX
+ enum eButtonMap
+ {
+ DPadUp,
+ DPadDown,
+ DPadLeft,
+ DPadRight,
+ Start,
+ Back,
+ Select = Back,
+ LeftThumb,
+ L3 = LeftThumb,
+ RightThumb,
+ R3 = RightThumb,
+ A,
+ X = A,
+ B,
+ Circle = B,
+// X, //This one is conflicting with PS2
+ Square, //This is X
+ Y,
+ Triangle = Y,
+ AnalogA,
+ AnalogB,
+ AnalogX,
+ AnalogY,
+ Black,
+ White,
+ AnalogBlack,
+ AnalogWhite,
+ LeftTrigger,
+ AnalogL1 = LeftTrigger,
+ RightTrigger,
+ AnalogR1 = RightTrigger,
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY
+ };
+#elif defined(RAD_WIN32) // Clumsy because of win32<->console differences
+ enum eButtonMap
+ {
+ MoveUp, // These are the real buttons names
+ MoveDown,
+ MoveLeft,
+ MoveRight,
+ Attack,
+ Jump,
+ Sprint,
+ DoAction,
+ Accelerate,
+ Reverse,
+ SteerLeft,
+ SteerRight,
+ GetOutCar,
+ HandBrake,
+ Horn,
+ ResetCar,
+ CameraLeft,
+ CameraRight,
+ CameraMoveIn,
+ CameraMoveOut,
+ CameraZoom,
+ CameraLookUp,
+ CameraCarLeft,
+ CameraCarRight,
+ CameraCarLookUp,
+ CameraCarLookBack,
+ CameraToggle,
+ feBack,
+ feMoveUp,
+ feMoveDown,
+ feMoveLeft,
+ feMoveRight,
+ feSelect,
+ feFunction1,
+ feFunction2,
+ feMouseLeft,
+ feMouseRight,
+ feMouseUp,
+ feMouseDown,
+
+ P1_KBD_Start,
+ P1_KBD_Gas,
+ P1_KBD_Brake,
+ P1_KBD_EBrake,
+ P1_KBD_Nitro,
+ P1_KBD_Left,
+ P1_KBD_Right,
+ Select = feBack, // These are mappings to PS2 buttons, needed sometimes.
+ Start = feSelect,
+ DPadUp = MoveUp,
+ DPadRight = MoveRight,
+ DPadDown = MoveDown,
+ DPadLeft = MoveLeft,
+ R1 = HandBrake,
+ Triangle = DoAction,
+ Circle = Sprint,
+ X = Jump,
+ Square = Attack,
+ L3 = Horn,
+ AnalogL1 = CameraZoom,
+ AnalogR1 = CameraLookUp,
+ LeftStickX = 200, // Must hack to get camera stuff to compile
+ LeftStickY = 201, // Must hack to get camera stuff to compile
+ KeyboardEsc = 202 // Cute hack to get escape only back buttons
+ };
+#elif defined(RAD_PS2)
+ enum eButtonMap
+ {
+ Select,
+ Start,
+ DPadUp,
+ DPadRight,
+ DPadDown,
+ DPadLeft,
+ L2,
+ R2,
+ L1,
+ R1,
+ Triangle,
+ Circle,
+ X,
+ Square,
+ L3,
+ R3,
+ RightStickX,
+ RightStickY,
+ LeftStickX,
+ LeftStickY,
+ AnalogDPadRight,
+ AnalogDPadLeft,
+ AnalogDPadUp,
+ AnalogDPadDown,
+ AnalogTriangle,
+ AnalogCircle,
+ AnalogX,
+ AnalogSquare,
+ AnalogL1,
+ AnalogR1,
+ AnalogL2,
+ AnalogR2,
+ };
+#else //RAD_GAMECUBE
+ enum eButtonMap
+ {
+ DPadLeft,
+ DPadRight,
+ DPadDown,
+ DPadUp,
+ TriggerZ,
+ L3 = TriggerZ,
+ TriggerR,
+ AnalogR1 = TriggerR,
+ TriggerL,
+ AnalogL1 = TriggerL,
+ A,
+ X = A,
+ B,
+ Square = B,
+// X, //This one is conflicting with PS2
+ Circle, //This is X
+ Y,
+ Triangle = Y,
+ Menu,
+ Start = Menu,
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY,
+ AnalogTriggerL,
+ LeftTrigger = AnalogTriggerL,
+ AnalogTriggerR,
+ RightTrigger = AnalogTriggerR,
+ AnalogA,
+ AnalogueX = AnalogA,
+ AnalogB,
+ AnalogSquare = AnalogB
+ };
+#endif
+
+ // Static Methods for accessing this singleton.
+ static InputManager* CreateInstance( void );
+ static InputManager* GetInstance( void );
+ static void DestroyInstance( void );
+
+ // set everything up
+ void Init();
+
+ // per frame update
+ void Update ( unsigned int timeinms );
+
+ // various info
+ bool IsControllerInPort( int portnum ) const;
+ static unsigned int GetMaxControllers( void ) { return Input::MaxControllers; }
+ const char* GetName() const { return "Game Controller System"; }
+
+ void ToggleRumble( bool on );
+ void SetRumbleForDevice( int controllerId, bool bRumbleOn );
+ bool IsRumbleOnForDevice( int controllerId ) const;
+ void TriggerRumblePulse( int controllerId );
+
+ void SetRumbleEnabled( bool isEnabled );
+ bool IsRumbleEnabled() const;
+
+#ifdef RAD_WIN32
+ void StartRumbleEffects();
+ void StopRumbleEffects();
+#endif
+
+ // Returns the value of the input point 'inputIndex' owned by the controller at
+ // controllerIndex.
+ float GetValue( unsigned int controllerIndex, unsigned int inputIndex ) const;
+
+ // Get a physical controller
+ UserController* GetController( unsigned int controllerIndex );
+
+ // Associate this logical controller with the physical controller in slot "index"
+ int RegisterMappable( unsigned int index, Mappable *pMappable );
+
+ // Remove associations between a physical and logical controller
+ void UnregisterMappable( unsigned int index, int handle );
+ void UnregisterMappable( unsigned int index, Mappable *pMappable );
+ void UnregisterMappable( Mappable *pMappable );
+
+ // set the current game state for the input system (one of the enums in Input::Active state)
+ void SetGameState( Input::ActiveState state );
+ Input::ActiveState GetGameState() const;
+
+ // registration of controller ID to player ID
+ //
+ void RegisterControllerID( int playerID, int controllerID );
+ void UnregisterControllerID( int playerID );
+ void UnregisterAllControllerID();
+ int GetControllerIDforPlayer( int playerID ) const;
+ int GetControllerPlayerIDforController( int controllerIndex ) const;
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ void EnableReset( bool reset ) { mResetEnabled = reset; };
+
+ bool IsProScanButtonsPressed() const { return m_isProScanButtonsPressed; }
+
+#ifdef RAD_WIN32
+ FEMouse* GetFEMouse() const { return m_pFEMouse; }
+#endif
+
+#ifdef RAD_PS2
+ int GetLastMultitapStatus(int port) const {return mLastMultitapStatus[port];}
+ int GetCurMultitapStatus(int port) const {return mCurMultitapStatus[port];}
+#endif
+
+private:
+ InputManager();
+ ~InputManager();
+
+ // IRadControllerConnectionChangeCallback interface
+ // called when someone plugs in or pulls out a controller
+ void OnControllerConnectionStatusChange( IRadController* pIController );
+
+ void ReleaseAllControllers( void );
+ void EnumerateControllers( void );
+
+ static InputManager* spInstance;
+
+ ref< IRadControllerSystem > mxIControllerSystem2;
+
+ UserController mControllerArray[ Input::MaxControllers ];
+
+ unsigned mGameState;
+ bool mChangeGameState : 1;
+ bool mConnectStateChanged : 1;
+ bool mIsRumbleEnabled : 1;
+ bool mIsResetting : 1;
+ bool mResetEnabled : 1;
+
+ int m_registeredControllerID[ MAX_PLAYERS ];
+
+ unsigned int mResetTimeout;
+
+ bool m_isProScanButtonsPressed : 1;
+
+#ifdef RAD_WIN32
+ FEMouse* m_pFEMouse;
+#endif
+#ifdef RAD_PS2
+ int mLastMultitapStatus[2];
+ int mCurMultitapStatus[2];
+#endif
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline InputManager* GetInputManager() { return( InputManager::GetInstance() ); }
+
+inline int
+InputManager::GetControllerIDforPlayer( int playerID ) const
+{
+ int controllerID;
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ if ( playerID >= 0 && playerID < MAX_PLAYERS )
+ {
+ controllerID = m_registeredControllerID[ playerID ];
+ }
+ else
+ {
+ // Return error code
+ controllerID = -1;
+ }
+ #ifndef RAD_RELEASE
+ if( m_registeredControllerID[ playerID ] == -1 )
+ {
+ // too much spew, we are polling this constantly for controller unplugged
+ // rTunePrintf( "*** WARNING: No controller ID registered for player %d!]\n",
+ // playerID + 1 );
+ }
+ #endif
+
+ return controllerID;
+}
+
+//=============================================================================
+// InputManager::GetControllerPlayerIDforController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerIndex )
+//
+// Return: int
+//
+//=============================================================================
+inline int InputManager::GetControllerPlayerIDforController( int controllerIndex ) const
+{
+ int i;
+ for ( i = 0; i < MAX_PLAYERS; ++i )
+ {
+ if ( m_registeredControllerID[ i ] == controllerIndex )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//=============================================================================
+// InputManager::IsRumbleEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: none
+//
+// Return: bool
+//
+//=============================================================================
+inline bool InputManager::IsRumbleEnabled() const
+{
+ return mIsRumbleEnabled;
+}
+
+
+#endif
diff --git a/game/code/input/mappable.cpp b/game/code/input/mappable.cpp
new file mode 100644
index 0000000..d0fc73f
--- /dev/null
+++ b/game/code/input/mappable.cpp
@@ -0,0 +1,251 @@
+#include <input/mappable.h>
+
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+
+#include <input/inputmanager.h>
+
+#include <raddebug.hpp>
+
+Mappable::Mappable( unsigned active)
+ :
+mButtonMask( 0 ),
+mActiveMapper( 0 ),
+mActiveState(active),
+mActive(true),
+mNeedResync(false)
+{
+}
+
+Mappable::~Mappable( void )
+{
+}
+
+// Route the Button changes through the Active Mapper.
+//
+void Mappable::DispatchOnButton( int controllerId, int id, const Button* pButton )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ // perform a lookup on this physical id and set state of the appropriate logical button
+ int destButtonID = GetActiveMapper( )->GetLogicalIndex(id);
+
+ if ( destButtonID != Input::INVALID_CONTROLLERID && IsActive())
+ {
+ // Grab the state of the button before we update the state.
+ bool bWasButtonDown = IsButtonDown( destButtonID );
+ bool duplicate = mButton[ destButtonID ].TimeSinceChange() == 0;
+
+ if(!duplicate || (duplicate && ( pButton->GetValue( ) >= mButton[ destButtonID].GetValue( ) )))
+ {
+ UpdateButtonState( controllerId, destButtonID, pButton );
+
+ OnButton( controllerId, destButtonID, pButton );
+
+ if ( 0.0f == pButton->GetValue( ) )
+ {
+ if ( bWasButtonDown )
+ {
+ OnButtonUp( controllerId, destButtonID, pButton );
+ }
+ }
+ else
+ {
+ if ( !bWasButtonDown )
+ {
+ OnButtonDown( controllerId, destButtonID, pButton );
+ }
+ }
+ }
+ }
+}
+
+// load in a complete button state
+// we only update the state, no edge-trigerred stuff (OnButton*)
+void Mappable::InitButtons( int controllerId, const Button* pButtons )
+{
+ for(unsigned int i = 0; i < Input::MaxPhysicalButtons; i++)
+ {
+ int destButtonID = GetActiveMapper( )->GetLogicalIndex(i);
+
+ if ( destButtonID != Input::INVALID_CONTROLLERID )
+ {
+ UpdateButtonState( controllerId, destButtonID, &pButtons[i] );
+ }
+ }
+
+}
+
+void Mappable::OnControllerDisconnect( int id )
+{
+ this->Reset();
+}
+
+void Mappable::OnControllerConnect( int id )
+{
+}
+
+// Update the internal variables that track the button state.
+// Always tracks the button state, regardless of game state.
+// OnButton will only be called in an active state.
+// Values updated BEFORE OnButton is called.
+//
+void Mappable::UpdateButtonState( int controllerId, int buttonId, const Button* pButton )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ unsigned int newButtonMask = 0;
+ unsigned int bit = 1 << buttonId;
+
+ mButton[ buttonId ] = *pButton;
+ mButton[ buttonId ].ForceChange();
+
+ if ( 0.0f != mButton[ buttonId ].GetValue( ) )
+ {
+ newButtonMask = bit | mButtonMask;
+ }
+ else
+ {
+ bit = ~bit;
+ newButtonMask = bit & mButtonMask;
+ }
+ mButtonMask = newButtonMask;
+}
+
+// Returns the value of the logical output referenced by 'index'.
+//
+float Mappable::GetValue( unsigned int index ) const
+{
+ rAssert( index < Input::MaxLogicalButtons );
+ if ( index < Input::MaxLogicalButtons )
+ {
+ return mButton[ index ].GetValue( );
+ }
+
+ return 0.0f;
+}
+
+
+// Returns a const pointer to the Button at index.
+//
+Button* Mappable::GetButton( unsigned int index )
+{
+ return &mButton [ index ];
+}
+
+
+// To set up the different controller configurations.
+//
+void Mappable::SetControlMap( unsigned map )
+{
+ mActiveMapper = map;
+}
+
+// Returns the current control map enumeration value.
+//
+unsigned Mappable::GetControlMap( void ) const
+{
+ return mActiveMapper;
+}
+
+// Returns a Mapper by index.
+//
+Mapper* Mappable::GetMapper( unsigned int index )
+{
+ return &mMapper[ index ];
+}
+
+// Returns the Active mapper.
+//
+Mapper* Mappable::GetActiveMapper( void )
+{
+ return &mMapper[ mActiveMapper ];
+}
+
+void Mappable::SetGameState(unsigned state)
+{
+ if(state & mActiveState)
+ {
+ mNeedResync = !mActive;
+ mActive = true;
+ }
+ else
+ {
+ mNeedResync = false;
+ mActive = false;
+ Reset();
+ }
+}
+
+// Is this mappable object active in this game state.
+//
+bool Mappable::IsActive( void ) const
+{
+ return mActive;
+}
+
+// Is the button down?
+//
+bool Mappable::IsButtonDown( unsigned int id )
+{
+ unsigned int bit = 1 << id;
+ return ( mButtonMask & bit ) != 0;
+}
+
+// Is the button up?
+//
+bool Mappable::IsButtonUp( unsigned int id )
+{
+ return !IsButtonDown( id );
+}
+
+// set up controller mappings. return false if cannot find name.
+//
+bool Mappable::Map( const char* pszName, int destination, unsigned int mapperIndex, unsigned int controllerId )
+{
+ Mapper* pMapper = this->GetMapper( mapperIndex );
+ //rValid( pMapper );
+
+ InputManager* pInputManager = InputManager::GetInstance( );
+ rAssert( pInputManager );
+
+ const UserController* pUserController = pInputManager->GetController( controllerId );
+ //rValid( pUserController );
+
+ int source = pUserController->GetIdByName( pszName );
+ if ( source >=0 )
+ {
+ pMapper->SetAssociation( source, destination );
+ return true;
+ }
+ return false;
+}
+
+// Clear all associations.
+//
+void Mappable::ClearMap( unsigned int mapperIndex )
+{
+ Mapper* pMapper = GetMapper( mapperIndex );
+ //rValid( pMapper );
+
+ pMapper->ClearAssociations( );
+}
+
+void Mappable::Reset( void )
+{
+ for( unsigned int i = 0; i < Input::MaxLogicalButtons; i++)
+ {
+ mButton[i].SetValue(0.0f);
+ }
+
+ mButtonMask = 0;
+} \ No newline at end of file
diff --git a/game/code/input/mappable.h b/game/code/input/mappable.h
new file mode 100644
index 0000000..23d7c17
--- /dev/null
+++ b/game/code/input/mappable.h
@@ -0,0 +1,138 @@
+#ifndef MAPPABLE_HPP_
+#define MAPPABLE_HPP_
+
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/mapper.h>
+
+#include <p3d/refcounted.hpp>
+
+struct ControlMap;
+class UserController;
+
+class Mappable
+:
+public tRefCounted
+{
+public:
+ Mappable( unsigned active = Input::ACTIVE_ALL & ~Input::ACTIVE_ANIM_CAM );
+
+ virtual ~Mappable( void );
+
+ //-------------------------------------------
+ //Stuff that derived classes should override
+ //-------------------------------------------
+
+ // This method is called when ever a button is active (i.e. is non zero, or just went zero).
+ virtual void OnButton( int controllerId, int id, const Button* pButton ) = 0;
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ virtual void OnButtonUp( int controllerId, int buttonId, const Button* pButton ) = 0;
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ virtual void OnButtonDown( int controllerId, int buttonId, const Button* pButton ) = 0;
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ virtual void LoadControllerMappings( unsigned int controllerId ) = 0;
+
+ // should be overriden by dewrived calasses to do something if the controller
+ // attached to this mappable unplugs.
+ // there is a default implementation which does nothing.
+ virtual void OnControllerDisconnect( int id );
+
+ // should be overriden by dewrived calasses to do something if the controller
+ // attached to this mappable plugs in.
+ // there is a default implementation which does nothing.
+ virtual void OnControllerConnect( int id );
+
+ // ---------------------
+ // Regular functions
+ // ---------------------
+
+ // Route the Button changes through the Mappers.
+ void DispatchOnButton( int controllerId, int id, const Button* pButton );
+
+ // load a complete physical button state
+ // This will set the internal state, but not generate any events (OnButton*)
+ void InitButtons( int controllerId, const Button* pButtons );
+
+ // Is the button down?
+ bool IsButtonDown( unsigned int id );
+
+ // Is the button up?
+ bool IsButtonUp( unsigned int id );
+
+ // Update the internal variables that track the button state.
+ // Always tracks the button state, regardless of game state.
+ // OnButton will only be called in an active state.
+ // Values updated BEFORE OnButton is called.
+ void UpdateButtonState( int controllerId, int id, const Button* pButton );
+
+ // set the current game state
+ void SetGameState(unsigned state);
+
+ // Returns true if the mappable is active in the current game state.
+ bool IsActive( void ) const;
+
+ // Returns the value of the logical output referenced by 'index'.
+ // Hint: make an enum with your own Logical Names.
+ float GetValue( unsigned int index ) const;
+
+ // Returns a pointer to the Button at index.
+ Button* GetButton( unsigned int index );
+
+ // To set up the different controller configurations.
+ void SetControlMap( unsigned map );
+
+ // Returns the current control map enumeration value.
+ //
+ unsigned GetControlMap( void ) const;
+
+ // Reset internal state (all vutton values go to 0)
+ void Reset( void );
+
+ bool GetResync(void) { return mNeedResync;}
+ void SetResync(bool b) { mNeedResync = b;}
+
+ // Returns a Mapper by index.
+ //
+ Mapper* GetMapper( unsigned int index );
+
+protected:
+ // set up controller mappings. return false if cannot find name.
+ //
+ bool Map( const char* pszName, int destination, unsigned int mapperIndex, unsigned int controllerId );
+
+ // Remove all associations.
+ //
+ void ClearMap( unsigned int mapperIndex );
+
+private:
+
+ // Returns the Active mapper.
+ //
+ Mapper* GetActiveMapper( void );
+ // A 32 Bit button mask.
+ //
+ unsigned int mButtonMask;
+
+ // State for all the (logical) buttons.
+ Button mButton[ Input::MaxLogicalButtons ];
+
+ // The button map.
+ Mapper mMapper[ Input::MaxMappings];
+
+ // The index of the currently active mapper.
+ unsigned mActiveMapper;
+
+ unsigned mActiveState;
+ bool mActive;
+ bool mNeedResync;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/mapper.cpp b/game/code/input/mapper.cpp
new file mode 100644
index 0000000..107749f
--- /dev/null
+++ b/game/code/input/mapper.cpp
@@ -0,0 +1,39 @@
+#include <input/mapper.h>
+#include <input/mappable.h>
+
+Mapper::Mapper( void )
+{
+ ClearAssociations( );
+}
+
+int Mapper::SetAssociation( int sourceButtonID, int destButtonID )
+{
+ buttonMap[sourceButtonID] = destButtonID;
+ return 0;
+}
+
+int Mapper::GetLogicalIndex( int sourceButtonID ) const
+{
+ return buttonMap[sourceButtonID];
+}
+
+int Mapper::GetPhysicalIndex( int destButtonID ) const
+{
+ for ( unsigned i = 0; i < Input::MaxLogicalButtons; i++ )
+ {
+ if ( buttonMap[i] == destButtonID)
+ {
+ return i;
+ }
+ }
+ return Input::INVALID_CONTROLLERID;
+}
+
+
+void Mapper::ClearAssociations( void )
+{
+ for( unsigned int i = 0; i < Input::MaxLogicalButtons; i++)
+ {
+ buttonMap[i] = Input::INVALID_CONTROLLERID;
+ }
+}
diff --git a/game/code/input/mapper.h b/game/code/input/mapper.h
new file mode 100644
index 0000000..4f93f5a
--- /dev/null
+++ b/game/code/input/mapper.h
@@ -0,0 +1,40 @@
+#ifndef MAPPER_HPP
+#define MAPPER_HPP
+
+#include <input/controller.h>
+#include <input/button.h>
+
+class Mappable;
+
+// Holds mappings from physical inputs to logical inputs
+//
+// All inputs from controller system are passed through this table by the base Mappable
+// class before being passed on to derived classes (for a particular gameplay state)
+class Mapper
+{
+public:
+ Mapper( void );
+
+ // Associate sourceButtonID with destButtonID.
+ int SetAssociation( int physicalIndex, int logicalIndex);
+
+ // Remove all mappings. Set num associations to zero.
+ void ClearAssociations( void );
+
+ // Will return the physical button id ("X") associated with the logical
+ // input ("Gas"). Useful for controller config.
+ // Warning, could be miltiple mappings, it will only return the first one
+ int GetPhysicalIndex( int logicalIndex) const;
+
+ // Will return the logical button id ("Gas") associated with the logical
+ // input ("X"). Useful for controller config.
+ int GetLogicalIndex( int physicalIndex ) const;
+
+private:
+ // Association structure and members
+ int buttonMap[Input::MaxPhysicalButtons];
+};
+
+
+#endif
+
diff --git a/game/code/input/rumbleeffect.cpp b/game/code/input/rumbleeffect.cpp
new file mode 100644
index 0000000..8ecfcf4
--- /dev/null
+++ b/game/code/input/rumbleeffect.cpp
@@ -0,0 +1,464 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumbleeffect.cpp
+//
+// Description: Implement RumbleEffect
+//
+// History: 12/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/rumbleeffect.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+#ifdef DEBUGWATCH
+int gRECount = -1;
+#endif
+
+extern EffectValue VALUES[];
+
+extern EffectValue DYNA_VALUES[];
+
+float GAIN_FUDGE = 25.0f;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RumbleEffect::RumbleEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RumbleEffect::RumbleEffect() :
+ mWheelEffect( NULL )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ mMotors[ i ] = NULL;
+ mMotorUpdated[ i ] = false;
+ }
+
+#ifdef DEBUGWATCH
+ ++gRECount;
+ mEffectNum = gRECount;
+ char nameSpace[256];
+ sprintf( nameSpace, "Controller\\RumbleEffect%d", mEffectNum );
+
+ char name[256];
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ sprintf( name, "%s Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &mCurrentEffects[i].mRumbleTimeLeft, name, nameSpace, NULL, NULL, 0, 100000 );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ sprintf( name, "%s Dyna Effect", DYNA_VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &mCurrentDynaEffects[i].mRumbleTimeLeft, name, nameSpace, NULL, NULL, 0, 100000 );
+ radDbgWatchAddFloat( &mCurrentDynaEffects[i].mMaxGain, name, nameSpace, NULL, NULL, 0.0f, 1.0f );
+ }
+#endif
+
+ InitEffects();
+}
+
+//=============================================================================
+// RumbleEffect::~RumbleEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RumbleEffect::~RumbleEffect()
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ if( mMotors[ i ] )
+ {
+ mMotors[ i ]->Release();
+ mMotors[ i ] = NULL;
+ }
+ }
+
+#ifdef DEBUGWATCH
+ gRECount--;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &mCurrentEffects[i].mRumbleTimeLeft );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &mCurrentDynaEffects[i].mRumbleTimeLeft );
+ radDbgWatchDelete( &mCurrentDynaEffects[i].mMaxGain );
+ }
+#endif
+
+ ShutDownEffects();
+}
+
+//=============================================================================
+// RumbleEffect::SetMotor
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int whichMotor, IRadControllerOutputPoint* motor )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetMotor( unsigned int whichMotor, IRadControllerOutputPoint* motor )
+{
+ rAssert( whichMotor < Input::MaxOutputMotor );
+
+ if ( mMotors[ whichMotor ] )
+ {
+ mMotors[ whichMotor ]->Release();
+ }
+
+ mMotors[ whichMotor ] = motor;
+ mMotors[ whichMotor ]->AddRef();
+}
+
+//=============================================================================
+// RumbleEffect::SetEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Effect effect, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetEffect( Effect effect, unsigned int milliseconds )
+{
+ if ( mCurrentEffects[ effect ].mRumbleTimeLeft < milliseconds )
+ {
+ mCurrentEffects[ effect ].mRumbleTimeLeft = milliseconds;
+ }
+}
+
+//=============================================================================
+// RumbleEffect::SetDynaEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaEffect effect, unsigned int milliseconds, float gain )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain )
+{
+ if ( mCurrentDynaEffects[ effect ].mRumbleTimeLeft < milliseconds )
+ {
+ mCurrentDynaEffects[ effect ].mRumbleTimeLeft = milliseconds;
+
+ if ( gain > 1.0f )
+ {
+ gain = 1.0f;
+ }
+
+ mCurrentDynaEffects[ effect ].mMaxGain = gain * GAIN_FUDGE;
+
+#ifdef RAD_GAMECUBE
+ if ( gain * GAIN_FUDGE < 0.3f )
+ {
+ mCurrentDynaEffects[ effect ].mMaxGain = 0;
+ }
+#endif
+ }
+}
+
+//=============================================================================
+// RumbleEffect::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::Update( unsigned int milliseconds )
+{
+ //The heaviest effect with time left is the one that runs.
+ Effect currentEffect = NUM_EFFECTS;
+
+ unsigned int i;
+
+ //Update all the effects.
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ if ( mCurrentEffects[ i ].mRumbleTimeLeft <= milliseconds )
+ {
+ if ( mCurrentEffects[ i ].mRumbleTimeLeft > 0 )
+ {
+ UpdateEffect( static_cast<Effect>(i), mCurrentEffects[ i ].mRumbleTimeLeft );
+ }
+
+ mCurrentEffects[ i ].mRumbleTimeLeft = 0;
+ }
+ else
+ {
+ mCurrentEffects[ i ].mRumbleTimeLeft -= milliseconds;
+
+ UpdateEffect( static_cast<Effect>(i), milliseconds );
+ }
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ if ( mCurrentDynaEffects[ i ].mRumbleTimeLeft <= milliseconds )
+ {
+ if ( mCurrentDynaEffects[ i ].mRumbleTimeLeft > 0 )
+ {
+ UpdateDynaEffect( static_cast<DynaEffect>(i), mCurrentDynaEffects[ i ].mRumbleTimeLeft, mCurrentDynaEffects[ i ].mMaxGain );
+ }
+
+ mCurrentDynaEffects[ i ].mRumbleTimeLeft = 0;
+ }
+ else
+ {
+ mCurrentDynaEffects[ i ].mRumbleTimeLeft -= milliseconds;
+
+ UpdateDynaEffect( static_cast<DynaEffect>(i), milliseconds, mCurrentDynaEffects[ i ].mMaxGain );
+ }
+ }
+
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ //You only update a motor when you want to give it a positive value.
+ if ( !mMotorUpdated[ i ] )
+ {
+ if ( mMotors[ i ]&& mMotors[ i ]->GetGain() > 0.0f )
+ {
+#ifdef RAD_GAMECUBE
+ mMotors[ i ]->SetGain( -1.0f ); //This stops the motor HARD
+#else
+ mMotors[ i ]->SetGain( 0.0f );
+#endif
+ }
+ }
+
+ //Reset them all.
+ mMotorUpdated[ i ] = false;
+ }
+}
+
+//=============================================================================
+// RumbleEffect::ShutDownEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::ShutDownEffects()
+{
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ mCurrentEffects[ i ].mRumbleTimeLeft = 0;
+ }
+
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ if ( mMotors[ i ] && mMotors[ i ]->GetGain() > 0.0f )
+ {
+#ifdef RAD_GAMECUBE
+ mMotors[ i ]->SetGain( -1.0f ); //This stops the motor HARD
+#else
+ mMotors[ i ]->SetGain( 0.0f );
+#endif
+ }
+ }
+
+ OnShutDownEffects();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RumbleEffect::UpdateEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Effect effect, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::UpdateEffect( Effect effect, unsigned int milliseconds )
+{
+ if ( mCurrentEffects[ effect ].mRumbleTimeLeft % VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ VALUES[effect].motor ] )
+ {
+ float currMotorGain = mMotors[ VALUES[effect].motor ]->GetGain();
+ float desiredGain = VALUES[effect].gain;
+
+ if ( currMotorGain < desiredGain )
+ {
+ mMotors[ VALUES[effect].motor ]->SetGain( VALUES[effect].gain );
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+#ifdef RAD_GAMECUBE
+ // Michael Riegger - Gamecube is different from other platforms in rumble
+ // control in that motor control is basically turn on / turn off commands
+ // and that rumble just 'goes' in between those commands. No need to
+ // toggle on and off manually
+ // so set the updated flag to true always if its where we want. Otherwise
+ // gain will be reset to 0 or -1 automatically
+ else if ( currMotorGain == desiredGain )
+ {
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+#endif
+ }
+ }
+ /* if ( mCurrentEffects[ effect ].mRumbleTimeLeft % VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ VALUES[effect].motor ] )
+ {
+
+
+ if ( currMotorGain == desiredGain )
+ {
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+ else if ( currMotorGain < desiredGain )
+ {
+ mMotors[ VALUES[effect].motor ]->SetGain( VALUES[effect].gain );
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+ }
+ }*/
+}
+
+//=============================================================================
+// RumbleEffect::UpdateDynaEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaEffect effect, unsigned int milliseconds, float gain )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::UpdateDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain ) //This is a pcnt
+{
+ if ( mCurrentDynaEffects[ effect ].mRumbleTimeLeft % DYNA_VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ DYNA_VALUES[effect].motor ] &&
+ mMotors[ DYNA_VALUES[effect].motor ]->GetGain() < DYNA_VALUES[effect].gain * gain )
+ {
+ mMotors[ DYNA_VALUES[effect].motor ]->SetGain( DYNA_VALUES[effect].gain * gain );
+ mMotorUpdated[ DYNA_VALUES[effect].motor ] = true;
+ }
+ }
+}
+
+
+//=============================================================================
+// RumbleEffect::InitEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::InitEffects()
+{
+#ifdef DEBUGWATCH
+ if ( gRECount == 0 )
+ {
+ char name[256];
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ sprintf( name, "Controller\\%s Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &VALUES[i].pulseTime, "PulseTime", name, NULL, NULL, 1, 1000 );
+ radDbgWatchAddFloat( &VALUES[i].gain, "Gain", name, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddChar( &VALUES[i].motor, "Motor", name, NULL, NULL, 0, 1 );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ sprintf( name, "Controller\\%s Dyna Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &DYNA_VALUES[i].pulseTime, "PulseTime", name, NULL, NULL, 1, 1000 );
+ radDbgWatchAddFloat( &DYNA_VALUES[i].gain, "Gain", name, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddChar( &DYNA_VALUES[i].motor, "Motor", name, NULL, NULL, 0, 1 );
+ }
+ }
+#endif
+}
+
+//=============================================================================
+// RumbleEffect::OnShutDownEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::OnShutDownEffects()
+{
+#ifdef DEBUGWATCH
+ if ( gRECount == -1 )
+ {
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &VALUES[i].pulseTime );
+ radDbgWatchDelete( &VALUES[i].gain );
+ radDbgWatchDelete( &VALUES[i].motor );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &DYNA_VALUES[i].pulseTime );
+ radDbgWatchDelete( &DYNA_VALUES[i].gain );
+ radDbgWatchDelete( &DYNA_VALUES[i].motor );
+ }
+ }
+#endif
+} \ No newline at end of file
diff --git a/game/code/input/rumbleeffect.h b/game/code/input/rumbleeffect.h
new file mode 100644
index 0000000..bd5746d
--- /dev/null
+++ b/game/code/input/rumbleeffect.h
@@ -0,0 +1,116 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumbleeffect.h
+//
+// Description: Blahblahblah
+//
+// History: 12/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RUMBLEEFFECT_H
+#define RUMBLEEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#include <input/controller.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+struct EffectValue
+{
+ unsigned int pulseTime;
+ float gain;
+ char motor;
+ const char* name;
+};
+
+class RumbleEffect
+{
+public:
+ enum Effect
+ {
+ LIGHT,
+ MEDIUM,
+ HARD1,
+ HARD2,
+ GROUND1,
+ GROUND2,
+ GROUND3,
+ GROUND4,
+ PULSE,
+ NUM_EFFECTS
+ };
+
+ enum DynaEffect
+ {
+ COLLISION1,
+ COLLISION2,
+ NUM_DYNA_EFFECTS
+ };
+
+ RumbleEffect();
+ virtual ~RumbleEffect();
+
+ void SetWheelEffect( IRadControllerOutputPoint* wheelEffect );
+ void SetMotor( unsigned int whichMotor, IRadControllerOutputPoint* motor );
+ void SetEffect( Effect effect, unsigned int milliseconds );
+ void SetDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain );
+
+ void Update( unsigned int milliseconds );
+ void ShutDownEffects();
+
+private:
+ IRadControllerOutputPoint* mWheelEffect;
+ IRadControllerOutputPoint* mMotors[ Input::MaxOutputMotor ];
+ bool mMotorUpdated[ Input::MaxOutputMotor ];
+
+ struct EffectInfo
+ {
+ EffectInfo() : mRumbleTimeLeft( 0 ) {};
+ unsigned int mRumbleTimeLeft;
+ };
+
+ EffectInfo mCurrentEffects[ NUM_EFFECTS ];
+
+ struct DynaEffectInfo : public EffectInfo
+ {
+ DynaEffectInfo() : mMaxGain( 1.0f ) {};
+ float mMaxGain;
+ };
+
+ DynaEffectInfo mCurrentDynaEffects[ NUM_DYNA_EFFECTS ];
+
+#ifdef DEBUGWATCH
+ int mEffectNum;
+#endif
+
+ void InitEffects();
+ void UpdateEffect( Effect effect, unsigned int milliseconds );
+ void UpdateDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain );
+ void OnShutDownEffects();
+
+ //Prevent wasteful constructor creation.
+ RumbleEffect( const RumbleEffect& rumbleeffect );
+ RumbleEffect& operator=( const RumbleEffect& rumbleeffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //RUMBLEEFFECT_H
diff --git a/game/code/input/rumblegc.cpp b/game/code/input/rumblegc.cpp
new file mode 100644
index 0000000..9de065b
--- /dev/null
+++ b/game/code/input/rumblegc.cpp
@@ -0,0 +1,49 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 0, "Light" },
+ { 1, 0.8f, 0, "Med" },
+ { 1, 1.0f, 0, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 0, "Ground 1" },
+ { 120, 0.4f, 0, "Ground 2" },
+ { 150, 0.35f, 0, "Ground 3" },
+ { 120, 0.35f, 0, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 0, "Collision2" }
+};
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/rumbleps2.cpp b/game/code/input/rumbleps2.cpp
new file mode 100644
index 0000000..5b15999
--- /dev/null
+++ b/game/code/input/rumbleps2.cpp
@@ -0,0 +1,44 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 1, "Light" },
+ { 1, 0.8f, 1, "Med" },
+ { 1, 1.0f, 1, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 1, "Ground 1" },
+ { 120, 0.4f, 1, "Ground 2" },
+ { 150, 0.35f, 1, "Ground 3" },
+ { 120, 0.35f, 1, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+} \ No newline at end of file
diff --git a/game/code/input/rumblewin32.cpp b/game/code/input/rumblewin32.cpp
new file mode 100644
index 0000000..963b607
--- /dev/null
+++ b/game/code/input/rumblewin32.cpp
@@ -0,0 +1,68 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumblewin32.cpp
+//
+// Description: Implementation of rumble for win. This is probably just a
+// dummy file.. we need to define some constants for rumble to
+// compile, but who knows if we'll need them.
+//
+// History: 03/04/2003 + Created -- ziemek
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 1, "Light" },
+ { 1, 0.8f, 1, "Med" },
+ { 1, 1.0f, 1, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 1, "Ground 1" },
+ { 120, 0.4f, 1, "Ground 2" },
+ { 150, 0.35f, 1, "Ground 3" },
+ { 120, 0.35f, 1, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/input/rumblexbox.cpp b/game/code/input/rumblexbox.cpp
new file mode 100644
index 0000000..276351b
--- /dev/null
+++ b/game/code/input/rumblexbox.cpp
@@ -0,0 +1,51 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 0, "Light" },
+ { 1, 0.8f, 0, "Med" },
+ { 1, 1.0f, 0, "Hard1" },
+ { 1, 1.0f, 1, "Hard2" },
+ { 100, 0.9f, 1, "Ground 1" },
+ { 120, 1.0f, 1, "Ground 2" },
+ { 150, 0.95f, 1, "Ground 3" },
+ { 120, 0.95f, 1, "Ground 4" },
+ { 1, 1.0f, 1, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 1, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/input/steeringspring.cpp b/game/code/input/steeringspring.cpp
new file mode 100644
index 0000000..d78d4f0
--- /dev/null
+++ b/game/code/input/steeringspring.cpp
@@ -0,0 +1,187 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SteeringSpring.cpp
+//
+// Description: Implement SteeringSpring
+//
+// History: 10/21/2002 + Created -- Cary Brisebois
+// Modified -- Neil Haran
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/SteeringSpring.h>
+#include <input/wheeldefines.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SteeringSpring::SteeringSpring
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SteeringSpring::SteeringSpring()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_conditon.lOffset = 0;
+ m_conditon.lDeadBand = 0;
+ m_conditon.lPositiveCoefficient = 127;
+ m_conditon.lNegativeCoefficient = 127;
+ m_conditon.dwPositiveSaturation = 127;
+ m_conditon.dwNegativeSaturation = 127;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONDITION);
+ mForceEffect.lpvTypeSpecificParams = &m_conditon;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_SPRING;
+ mForceEffect.duration = LG_DURATION_INFINITE;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.condition[0].offset = 0;
+ mForceEffect.p.condition[0].deadband = 0;
+ mForceEffect.p.condition[0].saturationNeg = 127;
+ mForceEffect.p.condition[0].saturationPos = 127;
+ mForceEffect.p.condition[0].coefficientNeg = 127;
+ mForceEffect.p.condition[0].coefficientPos = 127;
+#endif
+}
+
+//==============================================================================
+// SteeringSpring::~SteeringSpring
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SteeringSpring::~SteeringSpring()
+{
+}
+
+//=============================================================================
+// SteeringSpring::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::OnInit()
+{
+}
+
+//=============================================================================
+// SteeringSpring::SetCenterPoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s8 point, u8 deadband )
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::SetCenterPoint( s8 point, u8 deadband )
+{
+#ifdef RAD_WIN32
+ m_conditon.lOffset = point;
+ m_conditon.lDeadBand = deadband;
+#else
+ mForceEffect.p.condition[0].offset = point;
+ mForceEffect.p.condition[0].deadband = deadband;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// SteeringSpring::SetSpringStrength
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 strength )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void SteeringSpring::SetSpringStrength( u16 strength )
+#else
+void SteeringSpring::SetSpringStrength( u8 strength )
+#endif
+{
+#ifdef RAD_WIN32
+ m_conditon.dwPositiveSaturation = strength;
+ m_conditon.dwNegativeSaturation = strength;
+#else
+ mForceEffect.p.condition[0].saturationNeg = strength;
+ mForceEffect.p.condition[0].saturationPos = strength;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// SteeringSpring::SetSpringCoefficient
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 coeff )
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::SetSpringCoefficient( s16 coeff )
+{
+#ifdef RAD_WIN32
+ m_conditon.lPositiveCoefficient = coeff;
+ m_conditon.lNegativeCoefficient = coeff;
+#else
+ mForceEffect.p.condition[0].coefficientNeg = coeff;
+ mForceEffect.p.condition[0].coefficientPos = coeff;
+#endif
+
+ mEffectDirty = true;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/input/steeringspring.h b/game/code/input/steeringspring.h
new file mode 100644
index 0000000..d28530f
--- /dev/null
+++ b/game/code/input/steeringspring.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: steeringspring.h
+//
+// Description: Blahblahblah
+//
+// History: 10/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef STEERINGSPRING_H
+#define STEERINGSPRING_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SteeringSpring : public ForceEffect
+{
+public:
+ SteeringSpring();
+ virtual ~SteeringSpring();
+
+ void OnInit();
+ void SetCenterPoint( s8 degrees, u8 deadband ); //Where 0 is straight up.
+#ifdef RAD_WIN32
+ void SetSpringStrength( u16 strength );
+#else
+ void SetSpringStrength( u8 strength );
+#endif
+ void SetSpringCoefficient( s16 coeff );
+
+private:
+
+#ifdef RAD_WIN32
+ DICONDITION m_conditon;
+#endif
+ //Prevent wasteful constructor creation.
+ SteeringSpring( const SteeringSpring& steeringspring );
+ SteeringSpring& operator=( const SteeringSpring& steeringspring );
+};
+
+
+#endif //STEERINGSPRING_H
+
+
diff --git a/game/code/input/usercontroller.cpp b/game/code/input/usercontroller.cpp
new file mode 100644
index 0000000..e740374
--- /dev/null
+++ b/game/code/input/usercontroller.cpp
@@ -0,0 +1,726 @@
+#include <input/usercontroller.h>
+#include <input/mappable.h>
+#include <input/mapper.h>
+#include <input/button.h>
+
+#include <radcontroller.hpp>
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE)
+#include <input/wheeldefines.h>
+#endif
+
+
+
+#ifndef WORLD_BUILDER
+#include <main/commandlineoptions.h>
+#endif
+
+#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
+
+// We use ::strcmp()
+//
+#include <string.h>
+
+#include <raddebugwatch.hpp>
+#ifdef DEBUGWATCH
+#include <input/inputmanager.h>
+
+
+struct ButtonData
+{
+ ButtonData() : controllerID( -1 ), button( -1 ), range( 1.0f ) {};
+ int controllerID;
+ int button;
+ float range;
+};
+
+static ButtonData gData[Input::MaxPhysicalButtons];
+
+void ButtonCallback( void* userData )
+{
+ ButtonData* data = static_cast<ButtonData*>( userData );
+
+ if ( data->button != Input::INVALID_CONTROLLERID )
+ {
+ UserController* controller = GetInputManager()->GetController( data->controllerID );
+
+ Button* button = controller->GetInputButton( data->button );
+ button->SetValue( 1.0f );
+ button->ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ Mappable* mappable = controller->GetMappable( j );
+ if ( mappable )
+ {
+ mappable->DispatchOnButton( data->controllerID, data->button, button );
+ }
+ }
+
+ button->SetValue( 0.0f );
+ button->ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ Mappable* mappable = controller->GetMappable( j );
+ if ( mappable )
+ {
+ mappable->DispatchOnButton( data->controllerID, data->button, button );
+ }
+ }
+ }
+}
+
+#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
+
+//******************************************************************************
+//
+// Constructors, Destructors and Operators
+//
+//******************************************************************************
+UserController::UserController( )
+:
+m_iPlayerIndex( -1 ),
+m_xIController2( NULL ),
+m_controllerId( -1 ),
+m_bConnected( false ),
+mbInputPointsRegistered( false ),
+mGameState( Input::ACTIVE_ALL ),
+mbIsRumbleOn( false )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ this->mMappable[ i ] = 0;
+ }
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE)
+ mHeavyWheelRumble.SetRumbleType( LG_TYPE_SQUARE );
+#endif
+}
+
+void UserController::NotifyConnect( void )
+{
+ if ( !IsConnected( ) )
+ {
+ m_bConnected = true;
+
+ for ( unsigned int i = 0; i < Input::MaxMappables; i++ )
+ {
+ if (mMappable[ i ])
+ mMappable[ i ]->OnControllerConnect( GetControllerId( ) );
+ }
+ }
+}
+void UserController::NotifyDisconnect( void )
+{
+ if ( IsConnected( ) )
+ {
+ m_bConnected = false;
+
+ for ( unsigned int i = 0; i < Input::MaxMappables; i++ )
+ {
+ if (mMappable[ i ])
+ mMappable[ i ]->OnControllerDisconnect( GetControllerId( ) );
+ }
+ }
+}
+
+void UserController::Create( int id )
+{
+ m_controllerId = id;
+}
+
+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::OnControllerInputPointChange( unsigned int buttonId, float value )
+{
+ // We need to query the input point directly to get the value
+ // remapped to the requested range.
+
+ float fLastValue = mButtonArray[ buttonId ].GetValue( );
+ float fDeadZone = mButtonDeadZones[buttonId];
+
+ value = m_xIController2->GetInputPointByIndex( buttonId )->GetCurrentValue( );
+
+ if ( rmt::Epsilon( value, 0.0f, fDeadZone ) )
+ {
+ value = 0.0f;
+ }
+ else
+ {
+ 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;
+ }
+ }
+
+ mButtonArray[ buttonId ].SetValue( value );
+}
+
+void UserController::Initialize( IRadController* pIController2 )
+{
+ // if we are being called while initialized, something is wrong
+ rAssert(!mbInputPointsRegistered && !m_xIController2);
+
+ m_xIController2 = pIController2;
+
+ if( !mbInputPointsRegistered && m_xIController2 != NULL )
+ {
+ // register input point callbacks
+ unsigned int i;
+
+ for( i = 0; i < Input::MaxPhysicalButtons; i++ )
+ {
+ mButtonNames[i] = 0;
+ }
+
+ // get number of input points on controller
+ mNumButtons = m_xIController2->GetNumberOfInputPoints();
+ rAssert(mNumButtons < Input::MaxPhysicalButtons);
+
+ // register all input points
+ for( i = 0; i < mNumButtons; i++ )
+ {
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( i );
+ if( pInputPoint != NULL )
+ {
+ const char* inputPointType = pInputPoint->GetType();
+
+ if( ::strcmp( inputPointType, "Button" ) == 0 )
+ {
+ pInputPoint->SetTolerance( 1.0f );
+ mButtonDeadZones[ i ] = 0.0f;
+ }
+ else if( strcmp( inputPointType, "XAxis" ) == 0
+ || strcmp( inputPointType, "YAxis" ) == 0)
+ {
+ pInputPoint->SetTolerance( 0.01f );
+ pInputPoint->SetRange( -1.0f, 1.0f );
+
+ if ( strcmp( pInputPoint->GetName(), "Wheel" ) == 0 )
+ {
+ mButtonDeadZones[ i ] = 0.025f;
+ }
+ else
+ {
+ mButtonDeadZones[ i ] = 0.25f;
+ }
+ }
+ else // Analog?
+ {
+ pInputPoint->SetTolerance( 0.01f );
+ pInputPoint->SetRange( 0.0f, 1.0f );
+ mButtonDeadZones[ i ] = 0.0f;
+ }
+ mButtonNames[ i ] = radMakeKey(pInputPoint->GetName( ) );
+ pInputPoint->RegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( this ), i );
+
+ // [ps] Initialize the input points
+ OnControllerInputPointChange( i , pInputPoint->GetCurrentValue( ) );
+
+#ifdef DEBUGWATCH
+ char name[64];
+ sprintf( name, "%s Button", pInputPoint->GetName() );
+ char nameSpace[64];
+ sprintf( nameSpace, "Controller\\Input%d", m_controllerId );
+ gData[m_controllerId].controllerID = m_controllerId;
+ gData[m_controllerId].button = static_cast<InputManager::eButtonMap>(i);
+ float min, max;
+ pInputPoint->GetRange( &min, &max );
+ gData[m_controllerId].range = max;
+ radDbgWatchAddFunction( name, &ButtonCallback, &gData[m_controllerId], nameSpace );
+#endif
+ }
+ }
+
+ mbInputPointsRegistered = true;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ //Get the steering spring.
+ IRadControllerOutputPoint* p_OutputPoint = m_xIController2->GetOutputPointByName( "CenterSpring" );
+ if ( p_OutputPoint )
+ {
+ mSteeringSpring.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "BaseDamper" );
+ if ( p_OutputPoint )
+ {
+ mSteeringDamper.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Constant" );
+ if ( p_OutputPoint )
+ {
+ mConstantEffect.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Rumble" );
+ if ( p_OutputPoint )
+ {
+ mWheelRumble.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "HeavyRumble" );
+ if ( p_OutputPoint )
+ {
+ mHeavyWheelRumble.Init( p_OutputPoint );
+ }
+#endif
+
+#ifdef RAD_GAMECUBE
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Motor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+#elif defined( RAD_PS2 )
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "SmallMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "LargeMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 1, p_OutputPoint );
+ }
+#else
+ IRadControllerOutputPoint* p_OutputPoint = m_xIController2->GetOutputPointByName( "LeftMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "RightMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 1, p_OutputPoint );
+ }
+#endif
+ }
+
+#ifdef CONTROLLER_DEBUG
+#ifdef DEBUGWATCH
+ char nameSpace[64];
+ sprintf( nameSpace, "Controller\\Input%d", m_controllerId );
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ radDbgWatchAddBoolean( &rb.mRandomButtonEnable, "Enable Random Buttons", nameSpace );
+ radDbgWatchAddUnsignedInt( &rb.mRandomButtonTiming, "Button Timing", nameSpace, NULL, NULL, 0, MAX_BUTTON_TIME );
+ radDbgWatchAddBoolean( &rb.mRadomizeButtonTiming, "Randomize Timing", nameSpace );
+ radDbgWatchAddUnsignedInt( &rb.mMaxButtons, "Max Simul. Buttons", nameSpace, NULL, NULL, 1, MAX_BUTTON_PRESSES );
+#endif
+#endif
+}
+
+void UserController::ReleaseRadController( void )
+{
+ // deregister input point callbacks
+ if( mbInputPointsRegistered && m_xIController2 != NULL )
+ {
+ // get number of input points on controller.
+ //
+ const unsigned int NUM_INPUT_POINTS = m_xIController2->GetNumberOfInputPoints();
+ unsigned int i;
+
+ // unregister all input points.
+ //
+ for( i = 0; i < NUM_INPUT_POINTS; i++ )
+ {
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( i );
+ if( pInputPoint != NULL )
+ {
+ pInputPoint->UnRegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( this ) );
+#ifdef DEBUGWATCH
+ char name[64];
+ sprintf( name, "%s Button", pInputPoint->GetName() );
+ radDbgWatchDelete( &name );
+#endif
+ }
+ mButtonArray[ i ].SetValue(0.0f);
+ }
+
+ mbInputPointsRegistered = false;
+ }
+
+ mRumbleEffect.ShutDownEffects();
+
+ this->m_xIController2 = NULL;
+
+#ifdef CONTROLLER_DEBUG
+#ifdef DEBUGWATCH
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ radDbgWatchDelete( &rb.mRandomButtonEnable );
+ radDbgWatchDelete( &rb.mRandomButtonTiming );
+ radDbgWatchDelete( &rb.mRadomizeButtonTiming );
+ radDbgWatchDelete( &rb.mMaxButtons );
+#endif
+#endif
+}
+
+void UserController::SetRumble( bool bRumbleOn, bool pulse )
+{
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ if ( bRumbleOn && !mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+ mSteeringSpring.Start();
+ mSteeringDamper.Start();
+ mConstantEffect.Start();
+ mWheelRumble.Start();
+ mHeavyWheelRumble.Start();
+ }
+ else if ( !bRumbleOn && mbIsRumbleOn )
+ {
+ mSteeringSpring.Stop();
+ mSteeringDamper.Stop();
+ mConstantEffect.Stop();
+ mWheelRumble.Stop();
+ mHeavyWheelRumble.Stop();
+ }
+#endif
+
+ if ( pulse )
+ {
+ PulseRumble();
+ }
+
+ if ( !bRumbleOn && mbIsRumbleOn )
+ {
+ 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 ) )
+ {
+#ifdef RAD_XBOX
+ gain *= 2.0f;
+#endif
+ 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<int>( 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
+
+ // 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 mappiung will screw up in some cases)
+ if((mButtonArray[ i ].TimeSinceChange() == 0) || (mButtonArray[ i ].IsDown()))
+ {
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, i, &mButtonArray[ i ] );
+ }
+ }
+ }
+ }
+
+#ifdef RAD_GAMECUBE
+ if ( mbIsRumbleOn )
+ {
+#endif
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ mSteeringSpring.Update();
+ mSteeringDamper.Update();
+ mConstantEffect.Update();
+ mWheelRumble.Update();
+ mHeavyWheelRumble.Update();
+#endif
+
+ //I leave this out so the FE can rumble me anyway.
+ mRumbleEffect.Update( timeins );
+
+#ifdef RAD_GAMECUBE
+ }
+#endif
+}
+
+// Returns the value stored by input point at index.
+float UserController::GetInputValue( unsigned int index ) const
+{
+ return mButtonArray[ index ].GetValue( );
+}
+
+// Returns the real-time value of the input point at index.
+float UserController::GetInputValueRT( unsigned int index ) const
+{
+ rAssert( m_xIController2 != NULL );
+
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( index );
+ if( pInputPoint != NULL )
+ {
+ return pInputPoint->GetCurrentValue();
+ }
+ else
+ {
+ 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;
+ }
+ }
+}
+
+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;
+}
diff --git a/game/code/input/usercontroller.h b/game/code/input/usercontroller.h
new file mode 100644
index 0000000..922164a
--- /dev/null
+++ b/game/code/input/usercontroller.h
@@ -0,0 +1,139 @@
+#ifndef USERCONTROLLER_HPP
+#define USERCONTROLLER_HPP
+
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/rumbleeffect.h>
+#include <radcontroller.hpp>
+#include <radkey.hpp>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+#include <input/steeringspring.h>
+#include <input/baseDamper.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#endif
+
+class Mappable;
+class Mapper;
+
+
+class UserController :
+ public IRadControllerInputPointCallback
+{
+public:
+ UserController( void );
+ virtual ~UserController( void );
+
+ // initial set up of controller (called once at input ystem init)
+ virtual void Create( int id );
+
+ // set up actual controller mapping (called once at startup and prior to any time input
+ // connection state changes)
+ virtual void Initialize( IRadController* pIController2 );
+
+ // release ftech controllers (called prior to input connection state change)
+ virtual void ReleaseRadController( void );
+
+ // get the index of this controller
+ unsigned int GetControllerId( void ) const { return m_controllerId; }
+
+ // per-frame update
+ void Update( unsigned timeins );
+
+ // set the current game state (to activate / deactivate appropriate logical controllerts)
+ void SetGameState(unsigned);
+
+ // Rumble (not yet implimented)
+ void SetRumble( bool bRumbleOn, bool pulse = false );
+ bool IsRumbleOn( void ) const;
+ void PulseRumble();
+ void ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms );
+ void ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain );
+
+
+ // set the player index associated with this controller
+ void SetPlayerIndex( int i ) { m_iPlayerIndex = i; }
+ int GetPlayerIndex( void ) const { return m_iPlayerIndex; }
+
+ // connection status
+ bool IsConnected( void ) const { return m_bConnected; }
+ void NotifyConnect( void );
+ void NotifyDisconnect( void );
+
+ // Returns the value stored by input point at index.
+ float GetInputValue( unsigned int index ) const;
+ Button* GetInputButton( unsigned int index );
+
+ // Returns the current 'real-time' value of the input point
+ float GetInputValueRT( unsigned int index ) const;
+
+ // Attaches an logical controller this physical controller
+ int RegisterMappable( Mappable* pMappable );
+
+ // Detach a logical controller
+ void UnregisterMappable( int handle );
+ void UnregisterMappable( Mappable* pMappable );
+
+ // Set up the physical-logical button mappings.
+ void LoadControllerMappings( void );
+
+ // return the button id from the name.
+ int GetIdByName( const char* pszName ) const;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ SteeringSpring* GetSpring() { return mSteeringSpring.IsInit() ? &mSteeringSpring : NULL; };
+ BaseDamper* GetDamper() { return mSteeringDamper.IsInit() ? &mSteeringDamper : NULL; };
+ ConstantEffect* GetConstantEffect() { return mConstantEffect.IsInit() ? &mConstantEffect : NULL; };
+ WheelRumble* GetWheelRumble() { return mWheelRumble.IsInit() ? &mWheelRumble : NULL; };
+ WheelRumble* GetHeavyWheelRumble() { return mHeavyWheelRumble.IsInit() ? &mHeavyWheelRumble : NULL; };
+#endif
+
+ Mappable* GetMappable( unsigned int which ) { return mMappable[ which ]; };
+
+ bool IsWheel()
+ {
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ return mSteeringSpring.IsInit();
+#else
+ return false;
+#endif
+ }
+protected:
+ // Implements IRadControllerInputPointCallback
+ void OnControllerInputPointChange( unsigned int buttonId, float value );
+
+ // the player index we are controlling.
+ //
+ int m_iPlayerIndex;
+
+ ref< IRadController > m_xIController2;
+
+ int m_controllerId;
+
+ bool m_bConnected;
+
+ Mappable* mMappable[ Input::MaxMappables ];
+ bool mbInputPointsRegistered;
+
+ unsigned mNumButtons;
+ Button mButtonArray[ Input::MaxPhysicalButtons ];
+ radKey mButtonNames[ Input::MaxPhysicalButtons ];
+ float mButtonDeadZones[ Input::MaxPhysicalButtons ];
+
+ unsigned mGameState;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ SteeringSpring mSteeringSpring;
+ BaseDamper mSteeringDamper;
+ ConstantEffect mConstantEffect;
+ WheelRumble mWheelRumble;
+ WheelRumble mHeavyWheelRumble;
+#endif
+ bool mbIsRumbleOn;
+
+ RumbleEffect mRumbleEffect;
+};
+
+#endif
+
diff --git a/game/code/input/usercontrollerWin32.cpp b/game/code/input/usercontrollerWin32.cpp
new file mode 100644
index 0000000..47231d5
--- /dev/null
+++ b/game/code/input/usercontrollerWin32.cpp
@@ -0,0 +1,1443 @@
+//===========================================================================
+// 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 <radcontroller.hpp>
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <input/usercontrollerWin32.h>
+#include <input/mappable.h>
+#include <input/mapper.h>
+#include <input/button.h>
+#include <input/Mouse.h>
+#include <input/Keyboard.h>
+#include <input/Gamepad.h>
+#include <input/SteeringWheel.h>
+#include <input/inputmanager.h>
+
+#include <input/steeringspring.h>
+#include <input/baseDamper.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#include <presentation/tutorialmanager.h>
+
+#include <console/fbstricmp.h>
+#include <data/config/gameconfigmanager.h>
+
+#ifndef WORLD_BUILDER
+#include <main/commandlineoptions.h>
+#endif
+
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+
+// 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<int>( 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 ]++;
+ }
+ }
+}
diff --git a/game/code/input/usercontrollerWin32.h b/game/code/input/usercontrollerWin32.h
new file mode 100644
index 0000000..7d85957
--- /dev/null
+++ b/game/code/input/usercontrollerWin32.h
@@ -0,0 +1,294 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: UserController
+//
+// Description: An input/output controller for a given player in the game. It
+// bundles a number of actual controllers together to allow the
+// player to use different devices (keyboard, mouse, gamepad...).
+// This is the input interface between the game and the player,
+// sending inputs to registered input processing classes, letting
+// the player drive, move around, control the front end...
+//
+// This specific class is an implementation of the UserController
+// class strictly for win32.
+//
+// History: Branched from the console UserController class.
+//
+//=============================================================================
+
+#ifndef USERCONTROLLER_HPP
+#define USERCONTROLLER_HPP
+
+//========================================
+// Platform check
+//========================================
+
+#ifndef RAD_WIN32
+#error 'This implementation of the user controller is specific to Win32.'
+#endif
+
+//========================================
+// System Includes
+//========================================
+
+#include <radcontroller.hpp>
+#include <radkey.hpp>
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <data/config/gameconfig.h>
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/mapper.h>
+#include <input/rumbleeffect.h>
+#include <input/RealController.h>
+
+//=============================================================================
+// Forward Class Declarations
+//=============================================================================
+
+class Mappable;
+class Mapper;
+class SteeringSpring;
+class BaseDamper;
+class ConstantEffect;
+class WheelRumble;
+
+//=============================================================================
+// Enumerations
+//=============================================================================
+
+enum eVirtualMapSlot
+{
+ SLOT_PRIMARY = 0,
+ SLOT_SECONDARY
+};
+
+#define MAX_AXIS_THRESH 1.0f
+#define MIN_AXIS_THRESH -1.0f
+
+#define MAPPING_DEADZONE 0.5f
+
+//=============================================================================
+// Interface: ButtonMappedCallback
+//=============================================================================
+//
+// Description: Callback interface for the UserController::RemapButton method.
+// Implement the method to receive notification for when a button
+// has been mapped.
+//
+//=============================================================================
+
+struct ButtonMappedCallback
+{
+ virtual void OnButtonMapped( const char* InputName, eControllerType cont, int num_dirs, eDirectionType direction ) = 0;
+};
+
+//=============================================================================
+// Struct: ButtonMapData
+//=============================================================================
+//
+// Description: Internal struct for user controller.
+//
+//=============================================================================
+
+struct ButtonMapData
+{
+ ButtonMapData() : MapNext(false) {};
+
+ bool MapNext;
+ int map;
+ int virtualButton;
+ ButtonMappedCallback* callback;
+};
+
+//=============================================================================
+// Class: UserController
+//=============================================================================
+//
+// Description: The controller interface between the user and the game. It
+// encapsulates a number of radControllers for a given player.
+//
+//=============================================================================
+
+class UserController :
+ public IRadControllerInputPointCallback,
+ public GameConfigHandler
+{
+public:
+ UserController( void );
+ virtual ~UserController( void );
+
+ // initial set up of controller (called once at input ystem init)
+ virtual void Create( int id );
+
+ // set up actual controller mapping (called once at startup and prior to any time input
+ // connection state changes)
+ virtual void Initialize( RADCONTROLLER* pIController );
+
+ // release ftech controllers (called prior to input connection state change)
+ virtual void ReleaseRadController( void );
+
+ RealController* GetRealController( eControllerType type ) { return m_pController[type]; }
+ // get the index of this controller
+ unsigned int GetControllerId( void ) const { return m_controllerId; }
+
+ // per-frame update
+ void Update( unsigned timeins );
+
+ void StartForceEffects();
+ void StopForceEffects();
+
+ // set the current game state (to activate / deactivate appropriate logical controllerts)
+ void SetGameState(unsigned);
+
+ // Rumble (not yet implimented)
+ void SetRumble( bool bRumbleOn, bool pulse = false );
+ bool IsRumbleOn( void ) const;
+ void PulseRumble();
+ void ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms );
+ void ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain );
+
+ // connection status
+ bool IsConnected( void ) const;
+ void NotifyConnect( void );
+ void NotifyDisconnect( void );
+
+ // Returns the value stored by input point at index.
+ float GetInputValue( unsigned int index ) const;
+ Button* GetInputButton( unsigned int index );
+
+ // Returns the current 'real-time' value of the input point
+ float GetInputValueRT( unsigned int index ) const;
+
+ // Attaches an logical controller this physical controller
+ int RegisterMappable( Mappable* pMappable );
+
+ // Detach a logical controller
+ void UnregisterMappable( int handle );
+ void UnregisterMappable( Mappable* pMappable );
+
+ // Set up the physical-logical button mappings.
+ void LoadControllerMappings( void );
+
+ // return the button id from the name.
+ int GetIdByName( const char* pszName ) const;
+
+ SteeringSpring* GetSpring();
+ BaseDamper* GetDamper();
+ ConstantEffect* GetConstantEffect();
+ WheelRumble* GetWheelRumble();
+ WheelRumble* GetHeavyWheelRumble();
+
+ Mappable* GetMappable( unsigned int which ) { return mMappable[ which ]; };
+
+ bool IsWheel() const { return m_bIsWheel; }
+
+ // Implementation of the GameConfigHandler interface
+ virtual const char* GetConfigName() const;
+ virtual int GetNumProperties() const;
+ virtual void LoadDefaults();
+ virtual void LoadConfig( ConfigString& config );
+ virtual void SaveConfig( ConfigString& config );
+
+ // Public mapping functions for the user controller.
+ const char* GetMap( int map, int virtualKey, int& numDirs, eControllerType& cont, eDirectionType& dir ) const;
+ void RemapButton( int map, int VirtualButton, ButtonMappedCallback* callback );
+
+ void SetMouseLook( bool bMouseLook ) { m_bMouseLook = bMouseLook; }
+ void SetInvertMouseX( bool bInvertMouseX ) { m_bInvertMouseX = bInvertMouseX; }
+ void SetInvertMouseY( bool bInvertMouseY ) { m_bInvertMouseY = bInvertMouseY; }
+ void SetForceFeedback( bool bForceFeedback ) { m_bForceFeedback = bForceFeedback; }
+ void SetTutorialDisabled( bool bDisabled ) { m_bTutorialDisabled = bDisabled; }
+ void SetMouseSensitivityX( float fValue ) { m_fMouseSensitivityX = fValue; }
+ void SetMouseSensitivityY( float fValue ) { m_fMouseSensitivityY = fValue; }
+ void SetWheelSensitivityX( float fValue ) { m_fWheelSensitivityX = fValue; }
+ void SetWheelSensitivityY( float fValue ) { m_fWheelSensitivityY = fValue; }
+
+ bool IsMouseLookOn() const { return m_bMouseLook; }
+ bool IsMouseXInverted() const { return m_bInvertMouseX; }
+ bool IsMouseYInverted() const { return m_bInvertMouseY; }
+ bool IsForceFeedbackOn() const { return m_bForceFeedback; }
+ bool IsTutorialDisabled() const { return m_bTutorialDisabled; }
+
+ float GetMouseSensitivityX() const { return m_fMouseSensitivityX; }
+ float GetMouseSensitivityY() const { return m_fMouseSensitivityY; }
+ float GetWheelSensitivityX() const { return m_fWheelSensitivityX; }
+ float GetWheelSensitivityY() const { return m_fWheelSensitivityY; }
+
+protected:
+
+ // These register our callbacks with the RadControllers.
+ void RegisterInputPoints();
+ void RegisterInputPoint( eControllerType type, int inputpoint );
+
+ // Callback for when keys/inputs are pressed by the user.
+ void OnControllerInputPointChange( unsigned int buttonId, float value );
+
+ // These process input point values as received by the input point callback.
+ int DeriveDirectionValues( eControllerType type, int dxKey, float value, float* dir_values );
+ void SetButtonValue( unsigned int virtualButton, float value, bool sticky );
+
+ // Needed to get mouse axes to function properly
+ void ResetMouseAxes();
+
+ // Private mapping functions for the user controller.
+ void SetMap( int map, int virtualKey, eControllerType cont, int dxKey, eDirectionType dir = DIR_UP );
+ bool GetMap( int map, int virtualKey, eControllerType& cont, int& dxKey, eDirectionType& dir ) const;
+ void Remap( eControllerType cont, int dxKey, float* dvalues, int directions );
+ void ClearMappings();
+ void LoadFEMappings();
+
+private:
+
+ int m_controllerId;
+ bool mIsConnected;
+
+ unsigned mGameState;
+ bool mbInputPointsRegistered;
+
+ RealController* m_pController[ NUM_CONTROLLERTYPES ];
+
+ SteeringSpring* m_pSteeringSpring;
+ BaseDamper* m_pSteeringDamper;
+ ConstantEffect* m_pConstantEffect;
+ WheelRumble* m_pWheelRumble;
+ WheelRumble* m_pHeavyWheelRumble;
+
+ Mappable* mMappable[ Input::MaxMappables ];
+
+ // Virtual mapping data.
+ Mapper mVirtualMap[ Input::MaxVirtualMappings ];
+ ButtonMapData mMapData;
+
+ // Button data.
+ int mNumButtons;
+ Button mButtonArray[ Input::MaxPhysicalButtons ];
+ radKey mButtonNames[ Input::MaxPhysicalButtons ];
+ float mButtonDeadZones[ Input::MaxPhysicalButtons ];
+ bool mButtonSticky[ Input::MaxPhysicalButtons ];
+
+ bool mKeyboardBack;
+
+ bool mbIsRumbleOn;
+ bool m_bIsWheel;
+ RumbleEffect mRumbleEffect;
+
+ bool m_bMouseLook,
+ m_bInvertMouseX,
+ m_bInvertMouseY,
+ m_bForceFeedback;
+ bool m_bTutorialDisabled;
+
+ float m_fMouseSensitivityX,
+ m_fMouseSensitivityY,
+ m_fWheelSensitivityX, // The wheel
+ m_fWheelSensitivityY; // The pedals
+
+ int mResetMouseCounter[3]; // Hack needed for proper mouse inputs.
+};
+
+#endif
diff --git a/game/code/input/virtualinputs.cpp b/game/code/input/virtualinputs.cpp
new file mode 100644
index 0000000..d4de3f3
--- /dev/null
+++ b/game/code/input/virtualinputs.cpp
@@ -0,0 +1,178 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: VirtualInputs
+//
+// Description: Implementation of the virtual input class.
+//
+// History: 03/28/2003 + Created -- Ziemek
+//
+//===========================================================================
+
+#include <input/virtualinputs.hpp>
+#include <input/inputmanager.h>
+
+static const char* szVirtualInputs[] =
+{
+ "MoveUp",
+ "MoveDown",
+ "MoveLeft",
+ "MoveRight",
+ "Attack",
+ "Jump",
+ "Sprint",
+ "DoAction",
+
+ "Accelerate",
+ "Reverse",
+ "SteerLeft",
+ "SteerRight",
+ "GetOutCar",
+ "HandBrake",
+ "Horn",
+ "ResetCar",
+
+ "CameraLeft",
+ "CameraRight",
+ "CameraMoveIn",
+ "CameraMoveOut",
+ "CameraZoom",
+ "CameraLookUp",
+ "CameraCarLeft",
+ "CameraCarRight",
+ "CameraCarLookUp",
+ "CameraCarLookBack",
+ "CameraToggle",
+
+ "feBack", // Do not allow the user to configure any fe buttons!
+ "feMoveUp", // These are standard, unconfigurable
+ "feMoveDown",
+ "feMoveLeft",
+ "feMoveRight",
+ "feSelect",
+ "feFunction1",
+ "feFunction2",
+ "feMouseLeft",
+ "feMouseRight",
+ "feMouseUp",
+ "feMouseDown",
+
+ "P1_KBD_Start",
+ "P1_KBD_Gas",
+ "P1_KBD_Brake",
+ "P1_KBD_EBrake",
+ "P1_KBD_Nitro",
+ "P1_KBD_Left",
+ "P1_KBD_Right"
+};
+
+//==============================================================================
+// VirtualInputs::GetName
+//==============================================================================
+//
+// Description: Returns the name of the given input
+//
+// Parameters: virtualinput - input to look up
+//
+// Return: name of input
+//
+//==============================================================================
+
+const char* VirtualInputs::GetName( int VirtualInput )
+{
+ rAssert( VirtualInput >= 0 && VirtualInput < GetNumber() );
+
+ return szVirtualInputs[ VirtualInput ];
+}
+
+//==============================================================================
+// VirtualInputs::GetType
+//==============================================================================
+//
+// Description: Returns the type of a virtual input
+//
+// Parameters: virtualinput - input to look up
+//
+// Return: type of input
+//
+//==============================================================================
+
+eMapType VirtualInputs::GetType( int VirtualInput )
+{
+ rAssert( VirtualInput >= 0 && VirtualInput < GetNumber() );
+
+ switch( VirtualInput )
+ {
+ case InputManager::MoveUp:
+ case InputManager::MoveDown:
+ case InputManager::MoveLeft:
+ case InputManager::MoveRight:
+ case InputManager::Attack:
+ case InputManager::Jump:
+ case InputManager::Sprint:
+ case InputManager::DoAction:
+ case InputManager::CameraLeft:
+ case InputManager::CameraRight:
+ case InputManager::CameraMoveIn:
+ case InputManager::CameraMoveOut:
+ case InputManager::CameraZoom:
+ case InputManager::CameraLookUp:
+ return MAP_CHARACTER;
+ case InputManager::Accelerate:
+ case InputManager::Reverse:
+ case InputManager::SteerLeft:
+ case InputManager::SteerRight:
+ case InputManager::GetOutCar:
+ case InputManager::HandBrake:
+ case InputManager::Horn:
+ case InputManager::ResetCar:
+ case InputManager::CameraCarLeft:
+ case InputManager::CameraCarRight:
+ case InputManager::CameraCarLookUp:
+ case InputManager::CameraCarLookBack:
+ case InputManager::CameraToggle:
+ return MAP_VEHICLE;
+ case InputManager::feBack:
+ case InputManager::feMoveUp:
+ case InputManager::feMoveDown:
+ case InputManager::feMoveLeft:
+ case InputManager::feMoveRight:
+ case InputManager::feSelect:
+ case InputManager::feFunction1:
+ case InputManager::feFunction2:
+ case InputManager::feMouseLeft:
+ case InputManager::feMouseRight:
+ case InputManager::feMouseUp:
+ case InputManager::feMouseDown:
+ return MAP_FRONTEND;
+ default:
+ {
+ if ( VirtualInput >= InputManager::P1_KBD_Start && VirtualInput <= InputManager::P1_KBD_Right )
+ {
+ return MAP_FRONTEND;
+ }
+ else
+ {
+ return MAP_CHARACTER;
+ }
+ }
+ }
+}
+//==============================================================================
+// VirtualInputs::GetNumber
+//==============================================================================
+//
+// Description: Returns the number of virtual inputs
+//
+// Parameters: n/a
+//
+// Return: number of virtual inputs
+//
+//==============================================================================
+
+int VirtualInputs::GetNumber()
+{
+ static int NumVirtualInputs = sizeof( szVirtualInputs ) / sizeof( szVirtualInputs[0] );
+
+ return NumVirtualInputs;
+}
diff --git a/game/code/input/virtualinputs.hpp b/game/code/input/virtualinputs.hpp
new file mode 100644
index 0000000..214bb07
--- /dev/null
+++ b/game/code/input/virtualinputs.hpp
@@ -0,0 +1,31 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: VirtualInput
+//
+// Description: Small helper class for dealing with virtual inputs.
+//
+// History: Created 19/08/03.
+//
+//=============================================================================
+
+#ifndef VIRTUALINPUTS_HPP
+#define VIRTUALINPUTS_HPP
+
+enum eMapType
+{
+ MAP_CHARACTER = 0,
+ MAP_VEHICLE,
+ MAP_FRONTEND,
+ NUM_MAPTYPES
+};
+
+class VirtualInputs
+{
+public:
+ static const char* GetName( int VirtualInput );
+ static eMapType GetType( int VirtualInput );
+ static int GetNumber();
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/wheeldefines.h b/game/code/input/wheeldefines.h
new file mode 100644
index 0000000..32533cf
--- /dev/null
+++ b/game/code/input/wheeldefines.h
@@ -0,0 +1,53 @@
+#ifndef WHEEL_DEFINES
+#define WHEEL_DEFINES
+
+#ifdef RAD_PS2
+typedef struct lgDevForceEffect LGForceEffect;
+#endif
+
+#ifdef RAD_PS2
+//Why the hell are these different?
+#define LG_TYPE_DAMPER LGTYPE_DAMPER
+#define LG_TYPE_SPRING LGTYPE_SPRING
+#define LG_TYPE_CONSTANT LGTYPE_CONSTANT
+#define LG_TYPE_TRIANGLE LGTYPE_TRIANGLE
+#define LG_TYPE_SQUARE LGTYPE_SQUARE
+#define LG_DURATION_INFINITE LGDURATION_INFINITE
+#define type Type
+#define duration Duration
+#define startDelay StartDelay
+#define magnitude Magnitude
+#define direction Direction
+#define offset Offset
+#define deadband Deadband
+#define saturationNeg SaturationNeg
+#define saturationPos SaturationPos
+#define coefficientNeg CoefficientNeg
+#define coefficientPos CoefficientPos
+#define period Period
+#define phase Phase
+#define attackTime AttackTime
+#define fadeTime FadeTime
+#define attackLevel AttackLevel
+#define fadeLevel FadeLevel
+#endif
+
+#ifdef RAD_WIN32
+enum eForceTypes
+{
+ CONSTANT_FORCE,
+ RAMP_FORCE,
+ SQUARE,
+ SINE,
+ TRIANGLE,
+ SAWTOOTH_UP,
+ SAWTOOTH_DOWN,
+ SPRING,
+ DAMPER,
+ INERTIA,
+ FRICTION,
+ CUSTOM_FORCE
+};
+#endif
+
+#endif \ No newline at end of file
diff --git a/game/code/input/wheelrumble.cpp b/game/code/input/wheelrumble.cpp
new file mode 100644
index 0000000..c9f4df3
--- /dev/null
+++ b/game/code/input/wheelrumble.cpp
@@ -0,0 +1,214 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wheelrumble.cpp
+//
+// Description: Implement WheelRumble
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <input/wheelrumble.h>
+#include <input/wheeldefines.h>
+#include <input/inputmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WheelRumble::WheelRumble
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WheelRumble::WheelRumble()
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwMagnitude = 0;
+ m_diPeriodic.lOffset = 0;
+ m_diPeriodic.dwPhase = 0;
+ m_diPeriodic.dwPeriod = 80;
+
+ m_diEnvelope.dwSize = sizeof(DIENVELOPE);
+ m_diEnvelope.dwAttackLevel = 0;
+ m_diEnvelope.dwAttackTime = (DWORD)(0.5 * DI_SECONDS);
+ m_diEnvelope.dwFadeLevel = 0;
+ m_diEnvelope.dwFadeTime = (DWORD)(1 * DI_SECONDS);
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwSamplePeriod = 0;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.dwTriggerRepeatInterval = 0;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = &m_diEnvelope;
+ mForceEffect.cbTypeSpecificParams = sizeof(DIPERIODIC);
+ mForceEffect.lpvTypeSpecificParams = &m_diPeriodic;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_TRIANGLE;
+ mForceEffect.duration = 500;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.periodic.magnitude = 0;
+ mForceEffect.p.periodic.direction = 90;
+ mForceEffect.p.periodic.period = 80;
+ mForceEffect.p.periodic.phase = 0;
+ mForceEffect.p.periodic.offset = 0;
+ mForceEffect.p.periodic.envelope.attackTime = 0;
+ mForceEffect.p.periodic.envelope.fadeTime = 0;
+ mForceEffect.p.periodic.envelope.attackLevel = 0;
+ mForceEffect.p.periodic.envelope.fadeLevel = 0;
+#endif
+}
+
+//=============================================================================
+// WheelRumble::~WheelRumble
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WheelRumble::~WheelRumble()
+{
+}
+
+//=============================================================================
+// WheelRumble::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::OnInit()
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwMagnitude = 0;
+#else
+ mForceEffect.p.periodic.magnitude = 0;
+#endif
+}
+
+//=============================================================================
+// WheelRumble::SetMagDir
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 mag, u16 dir )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void WheelRumble::SetMagDir( u16 mag, u16 dir )
+#else
+void WheelRumble::SetMagDir( u8 mag, u16 dir )
+#endif
+{
+#ifdef RAD_WIN32
+ LONG rglDirection[2] = { dir, 0 };
+ m_diPeriodic.dwMagnitude = mag;
+ mForceEffect.rglDirection = rglDirection;
+#else
+ mForceEffect.p.periodic.magnitude = mag;
+ mForceEffect.p.periodic.direction = dir;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// WheelRumble::SetPPO
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u16 per, u16 phas, s16 offset )
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::SetPPO( u16 per, u16 phas, s16 offset )
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwPeriod = per;
+ m_diPeriodic.dwPhase = phas;
+ m_diPeriodic.lOffset = offset;
+#else
+ mForceEffect.p.periodic.period = per;
+ mForceEffect.p.periodic.phase = phas;
+ mForceEffect.p.periodic.offset = offset;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// WheelRumble::SetRumbleType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 type )
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::SetRumbleType( u8 type )
+{
+#ifdef RAD_WIN32
+
+#else
+ mForceEffect.type = type;
+#endif
+
+};
+
+#ifdef RAD_WIN32
+void WheelRumble::Update(unsigned timeins)
+{
+ m_currentTime += timeins;
+ if( m_currentTime > m_effectTime )
+ {
+ SetMagDir(0,0);
+ m_currentTime = 0;
+ }
+ ForceEffect::Update();
+}
+#endif
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/wheelrumble.h b/game/code/input/wheelrumble.h
new file mode 100644
index 0000000..2971833
--- /dev/null
+++ b/game/code/input/wheelrumble.h
@@ -0,0 +1,67 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wheelrumble.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WHEELRUMBLE_H
+#define WHEELRUMBLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WheelRumble : public ForceEffect
+{
+public:
+ WheelRumble();
+ virtual ~WheelRumble();
+
+ void OnInit();
+
+#ifdef RAD_WIN32
+ void SetMagDir( u16 mag, u16 dir );
+#else
+ void SetMagDir( u8 mag, u16 dir );
+#endif
+ void SetPPO( u16 per, u16 phas, s16 offset );
+
+ void SetRumbleType( u8 type );
+#ifdef RAD_WIN32
+ void Update(unsigned timeins = 0);
+#endif
+
+private:
+
+#ifdef RAD_WIN32
+ DIPERIODIC m_diPeriodic;
+ DIENVELOPE m_diEnvelope;
+#endif
+ //Prevent wasteful constructor creation.
+ WheelRumble( const WheelRumble& wheelrumble );
+ WheelRumble& operator=( const WheelRumble& wheelrumble );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //WHEELRUMBLE_H