//----------------------------------------------------------------------------- // File: MultiMapper.cpp // // Desc: This is a simple sample to demonstrate how to code using the DInput // action mapper feature. // //----------------------------------------------------------------------------- #define STRICT #define DIRECTINPUT_VERSION 0x0800 #include #include #include #include #include #include #include #include "MultiDI.h" #include "DXUtil.h" #include "resource.h" //----------------------------------------------------------------------------- // Defines, and constants //----------------------------------------------------------------------------- #define SAMPLE_KEY TEXT("Software\\Microsoft\\DirectX SDK\\MultiMapper") // This GUID must be unique for every game, and the same for // every instance of this app. // {67131584-2938-4857-8A2E-D99DC2C82068} // The GUID allows DirectInput to remember input settings GUID g_guidApp = { 0x67131584, 0x2938, 0x4857, { 0x8a, 0x2e, 0xd9, 0x9d, 0xc2, 0xc8, 0x20, 0x68 } }; // DirectInput action mapper reports events only when buttons/axis change // so we need to remember the present state of relevant axis/buttons for // each DirectInput device. The CMultiplayerInputDeviceManager will store a // pointer for each device that points to this struct struct InputDeviceState { FLOAT fAxisMoveUD; BOOL bButtonForwardThrust; BOOL bButtonReverseThrust; FLOAT fAxisRotateLR; BOOL bButtonRotateLeft; BOOL bButtonRotateRight; BOOL bButtonFireWeapons; BOOL bButtonEnableShield; }; // Struct to store the current input state struct UserInput { FLOAT fAxisMoveUD; FLOAT fAxisRotateLR; BOOL bButtonFireWeapons; BOOL bButtonEnableShield; BOOL bDoConfigureInput; BOOL bDoQuitGame; }; // Input semantics used by this app enum INPUT_SEMANTICS { // Gameplay semantics INPUT_ROTATE_AXIS_LR=1, INPUT_MOVE_AXIS_UD, INPUT_FIREWEAPONS, INPUT_ENABLESHIELD, INPUT_TURNLEFT, INPUT_TURNRIGHT, INPUT_FORWARDTHRUST, INPUT_REVERSETHRUST, INPUT_DISPLAYGAMEMENU, INPUT_QUITGAME, }; // Actions used by this app DIACTION g_rgGameAction[] = { // If DirectInput has already been given an action map for this GUID, it // will have created a user map for this application // (C:\Program Files\DirectX\DirectInput\User Maps\*.ini). If a user map // exists, DirectInput will use the action map defined in the stored user // map instead of the map defined in your program. This allows the user to // customize controls without losing changes when the game restarts. If you // wish to make changes to the default action map without changing the // GUID, you will need to delete the stored user map from your hard drive // for the system to detect your changes and recreate a stored user map. // Device input (joystick, etc.) that is pre-defined by DInput, according // to genre type. The genre for this app is space simulators. { INPUT_ROTATE_AXIS_LR, DIAXIS_SPACESIM_LATERAL, 0, TEXT("Turn left/right"), }, { INPUT_MOVE_AXIS_UD, DIAXIS_SPACESIM_MOVE, 0, TEXT("Move"), }, { INPUT_FIREWEAPONS, DIBUTTON_SPACESIM_FIRE, 0, TEXT("Shoot"), }, { INPUT_ENABLESHIELD, DIBUTTON_SPACESIM_GEAR, 0, TEXT("Shield"), }, { INPUT_DISPLAYGAMEMENU, DIBUTTON_SPACESIM_DISPLAY, 0, TEXT("Configure"), }, // Keyboard input mappings { INPUT_TURNLEFT, DIKEYBOARD_LEFT, 0, TEXT("Turn left"), }, { INPUT_TURNRIGHT, DIKEYBOARD_RIGHT, 0, TEXT("Turn right"), }, { INPUT_FORWARDTHRUST, DIKEYBOARD_UP, 0, TEXT("Move forward"), }, { INPUT_REVERSETHRUST, DIKEYBOARD_DOWN, 0, TEXT("Move backward"), }, { INPUT_FIREWEAPONS, DIKEYBOARD_F, 0, TEXT("Fire weapons"), }, { INPUT_ENABLESHIELD, DIKEYBOARD_S, 0, TEXT("Enable shields"), }, { INPUT_DISPLAYGAMEMENU, DIKEYBOARD_D, DIA_APPFIXED, TEXT("Configure"), }, { INPUT_QUITGAME, DIKEYBOARD_ESCAPE, DIA_APPFIXED, TEXT("Quit game"), }, // Mouse input mappings { INPUT_ROTATE_AXIS_LR, DIMOUSE_XAXIS, 0, _T("Turn left/right"), }, { INPUT_MOVE_AXIS_UD, DIMOUSE_YAXIS, 0, _T("Move"), }, { INPUT_FIREWEAPONS, DIMOUSE_BUTTON0, 0, _T("Shoot"), }, { INPUT_ENABLESHIELD, DIMOUSE_BUTTON1, 0, _T("Enable shields"), }, }; #define NUMBER_OF_GAMEACTIONS (sizeof(g_rgGameAction)/sizeof(DIACTION)) //----------------------------------------------------------------------------- // Function prototypes //----------------------------------------------------------------------------- INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); //----------------------------------------------------------------------------- // Name: class CMyApplication // Desc: Application class. //----------------------------------------------------------------------------- class CMyApplication { TCHAR* m_strWindowTitle; // Title for the app's window HWND m_hWnd; // The main app window FLOAT m_fTime; // Current time in seconds FLOAT m_fElapsedTime; // Time elapsed since last frame CMultiplayerInputDeviceManager* m_pInputDeviceManager; // DirectInput multiplayer device manager DIACTIONFORMAT m_diafGame; // Action format for game play UserInput m_UserInput[4]; // Struct for storing user input DWORD m_dwNumPlayers; // Number of players in the game FLOAT m_fWorldRotX; // World rotation state X-axis FLOAT m_fWorldRotY; // World rotation state Y-axis protected: HRESULT OneTimeSceneInit(); HRESULT Render(); HRESULT FrameMove(); HRESULT FinalCleanup(); HRESULT EnableUI(); HRESULT RenderText(); HRESULT InitInput(); HRESULT ChangeNumPlayers( DWORD dwNumPlayers, BOOL bResetOwnership, BOOL bResetMappings ); VOID ConfigInput(); void UpdateInput(); void CleanupDirectInput(); void CleanupDeviceStateStructs(); public: HRESULT Create( HINSTANCE hInstance ); INT Run(); INT_PTR MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); CMyApplication(); HRESULT InputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi ); static HRESULT CALLBACK StaticInputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi, LPVOID pParam ); }; //----------------------------------------------------------------------------- // Global access to the app (needed for the global WndProc()) //----------------------------------------------------------------------------- CMyApplication* g_pApp = NULL; HINSTANCE g_hInst = NULL; //----------------------------------------------------------------------------- // Name: WinMain() // Desc: Application entry point //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow ) { CMyApplication app; g_hInst = hInstance; if( FAILED( app.Create( hInstance ) ) ) return 0; return app.Run(); } //----------------------------------------------------------------------------- // Name: CMyApplication() // Desc: Constructor //----------------------------------------------------------------------------- CMyApplication::CMyApplication() { g_pApp = this; m_hWnd = NULL; m_strWindowTitle = TEXT( "DirectInput MultiMapper Sample" ); m_pInputDeviceManager = NULL; ZeroMemory( m_UserInput, sizeof(UserInput)*4 ); m_fWorldRotX = 0.0f; m_fWorldRotY = 0.0f; m_dwNumPlayers = 1; } //----------------------------------------------------------------------------- // Name: Create() // Desc: Creates the window //----------------------------------------------------------------------------- HRESULT CMyApplication::Create( HINSTANCE hInstance ) { // Display the main dialog box. CreateDialog( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, StaticMsgProc ); if( NULL == m_hWnd ) return E_FAIL; // Initialize the application timer DXUtil_Timer( TIMER_START ); return S_OK; } //----------------------------------------------------------------------------- // Name: Run() // Desc: Handles the message loop and calls FrameMove() and Render() when // idle. //----------------------------------------------------------------------------- INT CMyApplication::Run() { MSG msg; // Message loop to run the app while( TRUE ) { if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { // Skip WM_KEYDOWN so they aren't handled by the dialog if( msg.message == WM_KEYDOWN ) continue; if( !IsDialogMessage( m_hWnd, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } if( msg.message == WM_QUIT ) { DestroyWindow( m_hWnd ); break; } } else { // Update the time variables m_fTime = DXUtil_Timer( TIMER_GETAPPTIME ); m_fElapsedTime = DXUtil_Timer( TIMER_GETELAPSEDTIME ); // This app uses idle time processing for the game loop if( FAILED( FrameMove() ) ) SendMessage( m_hWnd, WM_DESTROY, 0, 0 ); if( FAILED( Render() ) ) SendMessage( m_hWnd, WM_DESTROY, 0, 0 ); Sleep( 20 ); } } FinalCleanup(); return (INT)msg.wParam; } //----------------------------------------------------------------------------- // Name: OneTimeSceneInit() // Desc: Called during initial app startup, this function performs all the // permanent initialization. //----------------------------------------------------------------------------- HRESULT CMyApplication::OneTimeSceneInit() { HRESULT hr; // Load the icon HICON hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE( IDI_MAIN ) ); // Set the icon for this dialog. SendMessage( m_hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon ); // Set big icon SendMessage( m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon ); // Set small icon // Initialize DirectInput if( FAILED( hr = InitInput() ) ) return DXTRACE_ERR( TEXT("InitInput"), hr ); HWND hNumPlayers = GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO ); SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("1") ); SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("2") ); SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("3") ); SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("4") ); SendMessage( hNumPlayers, CB_SETCURSEL, m_dwNumPlayers-1, 0 ); EnableUI(); return S_OK; } //----------------------------------------------------------------------------- // Name: InitInput() // Desc: Initialize DirectInput objects //----------------------------------------------------------------------------- HRESULT CMyApplication::InitInput() { HRESULT hr; // Setup action format for the actual gameplay ZeroMemory( &m_diafGame, sizeof(DIACTIONFORMAT) ); m_diafGame.dwSize = sizeof(DIACTIONFORMAT); m_diafGame.dwActionSize = sizeof(DIACTION); m_diafGame.dwDataSize = NUMBER_OF_GAMEACTIONS * sizeof(DWORD); m_diafGame.guidActionMap = g_guidApp; m_diafGame.dwGenre = DIVIRTUAL_SPACESIM; m_diafGame.dwNumActions = NUMBER_OF_GAMEACTIONS; m_diafGame.rgoAction = g_rgGameAction; m_diafGame.lAxisMin = -100; m_diafGame.lAxisMax = 100; m_diafGame.dwBufferSize = 16; _tcscpy( m_diafGame.tszActionMap, _T("MultiMapper Sample") ); // Create a new input device manager m_pInputDeviceManager = new CMultiplayerInputDeviceManager( SAMPLE_KEY ); if( FAILED( hr = ChangeNumPlayers( 1, FALSE, FALSE ) ) ) return DXTRACE_ERR( TEXT("ChangeNumPlayers"), hr ); return S_OK; } //----------------------------------------------------------------------------- // Name: StaticInputAddDeviceCB() // Desc: Static callback helper to call into CMyApplication class //----------------------------------------------------------------------------- HRESULT CALLBACK CMyApplication::StaticInputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi, LPVOID pParam ) { CMyApplication* pApp = (CMyApplication*) pParam; return pApp->InputAddDeviceCB( pPlayerInfo, pDeviceInfo, pdidi ); } //----------------------------------------------------------------------------- // Name: InputAddDeviceCB() // Desc: Called from CMultiplayerInputDeviceManager whenever a device is added. // Set the dead zone, and creates a new InputDeviceState for each device //----------------------------------------------------------------------------- HRESULT CMyApplication::InputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi ) { if( (GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_KEYBOARD) && (GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_MOUSE) ) { // Setup the deadzone DIPROPDWORD dipdw; dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = 500; pDeviceInfo->pdidDevice->SetProperty( DIPROP_DEADZONE, &dipdw.diph ); } // Create a new InputDeviceState for each device so the // app can record its state InputDeviceState* pNewInputDeviceState = new InputDeviceState; ZeroMemory( pNewInputDeviceState, sizeof(InputDeviceState) ); pDeviceInfo->pParam = (LPVOID) pNewInputDeviceState; HWND hPlayerDevice; switch( pPlayerInfo->dwPlayerIndex ) { case 0: hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P1 ); break; case 1: hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P2 ); break; case 2: hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P3 ); break; case 3: hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P4 ); break; } SendMessage( hPlayerDevice, LB_ADDSTRING, 0, (LPARAM) pdidi->tszProductName ); return S_OK; } //----------------------------------------------------------------------------- // Name: ChangeNumPlayers() // Desc: Signals a change in the number of players. It is also called to reset // ownership and mapping data. //----------------------------------------------------------------------------- HRESULT CMyApplication::ChangeNumPlayers( DWORD dwNumPlayers, BOOL bResetOwnership, BOOL bResetMappings ) { HRESULT hr; m_dwNumPlayers = dwNumPlayers; // Just pass in stock names. Real games may want to ask the user for a name, etc. TCHAR* strUserNames[4] = { TEXT("Player 1"), TEXT("Player 2"), TEXT("Player 3"), TEXT("Player 4") }; BOOL bSuccess = FALSE; while( !bSuccess ) { // Update UI EnableUI(); SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P1), LB_RESETCONTENT, 0, 0 ); SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P2), LB_RESETCONTENT, 0, 0 ); SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P3), LB_RESETCONTENT, 0, 0 ); SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P4), LB_RESETCONTENT, 0, 0 ); hr = m_pInputDeviceManager->Create( m_hWnd, strUserNames, m_dwNumPlayers, &m_diafGame, StaticInputAddDeviceCB, this, bResetOwnership, bResetMappings ); if( FAILED(hr) ) { switch( hr ) { case E_DIUTILERR_DEVICESTAKEN: { // It's possible that a single user could "own" too many devices for the other // players to get into the game. If so, we reinit the manager class to provide // each user with a device that has a default configuration. bResetOwnership = TRUE; MessageBox( m_hWnd, TEXT("You have entered more users than there are suitable ") \ TEXT("devices, or some users are claiming too many devices.\n") \ TEXT("Press OK to give each user a default device"), TEXT("Devices Are Taken"), MB_OK | MB_ICONEXCLAMATION ); break; } case E_DIUTILERR_TOOMANYUSERS: { // Another common error is if more users are attempting to play than there are devices // attached to the machine. In this case, the number of players is automatically // lowered to make playing the game possible. DWORD dwNumDevices = m_pInputDeviceManager->GetNumDevices(); m_dwNumPlayers = dwNumDevices; SendMessage( GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO ), CB_SETCURSEL, m_dwNumPlayers-1, 0 ); TCHAR sz[256]; wsprintf( sz, TEXT("There are not enough devices attached to the ") \ TEXT("system for the number of users you entered.\nThe ") \ TEXT("number of users has been automatically changed to ") \ TEXT("%i (the number of devices available on the system)."), m_dwNumPlayers ); MessageBox( m_hWnd, sz, _T("Too Many Users"), MB_OK | MB_ICONEXCLAMATION ); break; } default: return DXTRACE_ERR( TEXT("m_pInputDeviceManager->Create"), hr ); break; } m_pInputDeviceManager->Cleanup(); } else { bSuccess = TRUE; } } return S_OK; } //----------------------------------------------------------------------------- // Name: FrameMove() // Desc: Called once per frame, the call is the entry point for animating // the scene. //----------------------------------------------------------------------------- HRESULT CMyApplication::FrameMove() { // Update user input state UpdateInput(); for( DWORD iPlayer=0; iPlayerGetDevices( &pDeviceInfos, &dwNumDevices ); // Loop through all devices and check game input for( DWORD i=0; ifAxisMoveUD = 0.0f; pInputDeviceState->fAxisRotateLR = 0.0f; // Scale the relative axis data to make it more equal to // absolute joystick data fScale = 5.0f; } hr = pdidDevice->Acquire(); hr = pdidDevice->Poll(); hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0 ); if( FAILED(hr) ) continue; // Get the sematics codes for the game menu for( DWORD j=0; jfAxisRotateLR = fAxisState; break; case INPUT_MOVE_AXIS_UD: pInputDeviceState->fAxisMoveUD = -fAxisState; break; // Handle buttons separately so the button state data // doesn't overwrite the axis state data, and handle // each button separately so they don't overwrite each other case INPUT_TURNLEFT: pInputDeviceState->bButtonRotateLeft = bButtonState; break; case INPUT_TURNRIGHT: pInputDeviceState->bButtonRotateRight = bButtonState; break; case INPUT_FORWARDTHRUST: pInputDeviceState->bButtonForwardThrust = bButtonState; break; case INPUT_REVERSETHRUST: pInputDeviceState->bButtonReverseThrust = bButtonState; break; case INPUT_FIREWEAPONS: pInputDeviceState->bButtonFireWeapons = bButtonState; break; case INPUT_ENABLESHIELD: pInputDeviceState->bButtonEnableShield = bButtonState; break; // Handle one-shot buttons case INPUT_DISPLAYGAMEMENU: if( bButtonState ) m_UserInput[0].bDoConfigureInput = TRUE; break; case INPUT_QUITGAME: if( bButtonState ) m_UserInput[0].bDoQuitGame = TRUE; break; } } } for( DWORD iPlayer=0; iPlayerdwPlayerIndex != iPlayer ) continue; InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam; // Use the axis data that is furthest from zero if( fabs(pInputDeviceState->fAxisRotateLR) > fabs(m_UserInput[iPlayer].fAxisRotateLR) ) m_UserInput[iPlayer].fAxisRotateLR = pInputDeviceState->fAxisRotateLR; if( fabs(pInputDeviceState->fAxisMoveUD) > fabs(m_UserInput[iPlayer].fAxisMoveUD) ) m_UserInput[iPlayer].fAxisMoveUD = pInputDeviceState->fAxisMoveUD; // Process the button data if( pInputDeviceState->bButtonRotateLeft ) m_UserInput[iPlayer].fAxisRotateLR = -1.0f; else if( pInputDeviceState->bButtonRotateRight ) m_UserInput[iPlayer].fAxisRotateLR = 1.0f; if( pInputDeviceState->bButtonForwardThrust ) m_UserInput[iPlayer].fAxisMoveUD = 1.0f; else if( pInputDeviceState->bButtonReverseThrust ) m_UserInput[iPlayer].fAxisMoveUD = -1.0f; if( pInputDeviceState->bButtonFireWeapons ) m_UserInput[iPlayer].bButtonFireWeapons = TRUE; if( pInputDeviceState->bButtonEnableShield ) m_UserInput[iPlayer].bButtonEnableShield = TRUE; } } } //----------------------------------------------------------------------------- // Name: Render() // Desc: Called once per frame, the call is the entry point for rendering the // world. //----------------------------------------------------------------------------- HRESULT CMyApplication::Render() { TCHAR szMsg[MAX_PATH]; TCHAR szMsgCurrent[MAX_PATH]; // Update Player 1 _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[0].fAxisMoveUD ); GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P1), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P1), szMsg ); _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[0].fAxisRotateLR ); GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P1), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P1), szMsg ); if( !m_UserInput[0].bButtonEnableShield && !m_UserInput[0].bButtonFireWeapons ) _stprintf( szMsg, TEXT("None") ); else _stprintf( szMsg, TEXT("%s%s"), m_UserInput[0].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), m_UserInput[0].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") ); GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P1), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P1), szMsg ); // Update Player 2 _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[1].fAxisMoveUD ); GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), szMsg ); _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[1].fAxisRotateLR ); GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), szMsg ); if( !m_UserInput[1].bButtonEnableShield && !m_UserInput[1].bButtonFireWeapons ) _stprintf( szMsg, TEXT("None") ); else _stprintf( szMsg, TEXT("%s%s"), m_UserInput[1].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), m_UserInput[1].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") ); GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), szMsg ); // Update Player 3 _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[2].fAxisMoveUD ); GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), szMsg ); _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[2].fAxisRotateLR ); GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), szMsg ); if( !m_UserInput[2].bButtonEnableShield && !m_UserInput[2].bButtonFireWeapons ) _stprintf( szMsg, TEXT("None") ); else _stprintf( szMsg, TEXT("%s%s"), m_UserInput[2].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), m_UserInput[2].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") ); GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), szMsg ); // Update Player 4 _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[3].fAxisMoveUD ); GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), szMsg ); _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[3].fAxisRotateLR ); GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), szMsg ); if( !m_UserInput[3].bButtonEnableShield && !m_UserInput[3].bButtonFireWeapons ) _stprintf( szMsg, TEXT("None") ); else _stprintf( szMsg, TEXT("%s%s"), m_UserInput[3].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), m_UserInput[3].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") ); GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), szMsg ); // Update world state _stprintf( szMsg, TEXT("%0.3f, %0.3f"), m_fWorldRotX, m_fWorldRotY ); GetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsgCurrent, MAX_PATH ); if( lstrcmp( szMsgCurrent, szMsg ) != 0 ) SetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsg ); return S_OK; } //----------------------------------------------------------------------------- // Name: StaticMsgProc() // Desc: Static msg handler which passes messages to the application class. //----------------------------------------------------------------------------- INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return g_pApp->MsgProc( hWnd, uMsg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: MsgProc() // Desc: Callback for all Windows messages //----------------------------------------------------------------------------- INT_PTR CMyApplication::MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { HRESULT hr; switch( msg ) { case WM_INITDIALOG: m_hWnd = hWnd; // Initialize the app's custom scene stuff if( FAILED( hr = OneTimeSceneInit() ) ) { DXTRACE_ERR( TEXT("OneTimeSceneInit"), hr ); MessageBox( hWnd, TEXT("Error initializing DirectInput. Sample will now exit."), TEXT("DirectInput Sample"), MB_OK | MB_ICONERROR ); PostQuitMessage( IDCANCEL ); return TRUE; } break; case WM_COMMAND: { switch( LOWORD(wParam) ) { case IDC_NUM_PLAYERS_COMBO: { if( HIWORD(wParam) == CBN_SELCHANGE ) { TCHAR strItem[MAX_PATH]; HWND hNumPlayers = GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO ); int nCurSel = (int) SendMessage( hNumPlayers, CB_GETCURSEL, 0, 0 ); SendMessage( hNumPlayers, CB_GETLBTEXT, nCurSel, (LPARAM) strItem ); DWORD dwNumPlayers = _ttoi( strItem ); if( FAILED( hr = ChangeNumPlayers( dwNumPlayers, FALSE, FALSE ) ) ) { PostQuitMessage( IDCANCEL ); } } break; } case IDC_RESET_OWNERSHIP: ChangeNumPlayers( m_dwNumPlayers, TRUE, FALSE ); break; case IDC_RESET_MAPPINGS: ChangeNumPlayers( m_dwNumPlayers, FALSE, TRUE ); break; case IDCANCEL: PostQuitMessage( IDCANCEL ); break; case IDM_CONFIGINPUT: m_UserInput[0].bDoConfigureInput = TRUE; break; } break; } } return FALSE; } //----------------------------------------------------------------------------- // Name: FinalCleanup() // Desc: Called before the app exits, this function gives the app the chance // to cleanup after itself. //----------------------------------------------------------------------------- HRESULT CMyApplication::FinalCleanup() { // Cleanup DirectInput CleanupDirectInput(); return S_OK; } //----------------------------------------------------------------------------- // Name: CleanupDirectInput() // Desc: Cleanup DirectInput //----------------------------------------------------------------------------- VOID CMyApplication::CleanupDirectInput() { if( NULL == m_pInputDeviceManager ) return; CleanupDeviceStateStructs(); // Cleanup DirectX input objects SAFE_DELETE( m_pInputDeviceManager ); } //----------------------------------------------------------------------------- // Name: CleanupDeviceStateStructs() // Desc: //----------------------------------------------------------------------------- VOID CMyApplication::CleanupDeviceStateStructs() { // Get access to the list of semantically-mapped input devices // to delete all InputDeviceState structs before calling ConfigureDevices() CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfos; DWORD dwNumDevices; m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices ); for( DWORD i=0; iConfigureDevices( m_hWnd, NULL, NULL, DICD_EDIT, NULL ); if( FAILED(hr) ) { if( hr == E_DIUTILERR_PLAYERWITHOUTDEVICE ) { // There's a player that hasn't been assigned a device. Some games may // want to handle this by reducing the number of players, or auto-assigning // a device, or warning the user, etc. MessageBox( m_hWnd, TEXT("There is at least one player that wasn't assigned ") \ TEXT("a device\n") \ TEXT("Press OK to auto-assign a device to these users"), TEXT("Player Without Device"), MB_OK | MB_ICONEXCLAMATION ); } // Auto-reassign every player a device. ChangeNumPlayers( m_dwNumPlayers, FALSE, FALSE ); } EnableWindow( m_hWnd, TRUE ); SetForegroundWindow( m_hWnd ); } //----------------------------------------------------------------------------- // Name: EnableUI() // Desc: //----------------------------------------------------------------------------- HRESULT CMyApplication::EnableUI() { BOOL bEnablePlayer4 = ( m_dwNumPlayers > 3 ); BOOL bEnablePlayer3 = ( m_dwNumPlayers > 2 ); BOOL bEnablePlayer2 = ( m_dwNumPlayers > 1 ); // Enable/disable the player 2 ui EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER2_GROUP), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), bEnablePlayer2 ); EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P2), bEnablePlayer2 ); if( !bEnablePlayer2 ) { SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), TEXT("n/a") ); } // Enable/disable the player 3 ui EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER3_GROUP), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), bEnablePlayer3 ); EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P3), bEnablePlayer3 ); if( !bEnablePlayer4 ) { SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), TEXT("n/a") ); } // Enable/disable the player 4 ui EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER4_GROUP), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), bEnablePlayer4 ); EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P4), bEnablePlayer4 ); if( !bEnablePlayer4 ) { SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), TEXT("n/a") ); SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), TEXT("n/a") ); } return S_OK; }