home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / ffdonuts / input.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  28.9 KB  |  912 lines

  1. /*==========================================================================
  2.  *
  3.  * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  * File:       input.c
  6.  * Content:    DirectInput functionality for FFDonuts sample
  7.  *
  8.  * Functions:
  9.  * inputInitDirectInput()
  10.  * inputCleanupDirectInput()
  11.  * inputEnumDeviceProc(LPDIDEVICEINSTANCE pdidi, LPVOID pv);
  12.  * inputAcquireDevices(void);
  13.  * inputCreateEffects(void);
  14.  * inputProcessDeviceInput(void);
  15.  * inputPrepareDevice(void);
  16.  * inputPlayEffect(DWORD dwEffectFlag);
  17.  *
  18.  *
  19.  ***************************************************************************/
  20.  
  21. #include "input.h"
  22. #include "resource.h"
  23.  
  24. // file global variables
  25. static BOOL                 fIsFFDevice  = FALSE;   // does our device support
  26.                                                     // ForceFeedback
  27. static DWORD                dwGain       = FF_ADULT;// gain selection from user
  28. static LPDIRECTINPUT        gpdi         = NULL;    // base DirectInput object
  29. static LPDIRECTINPUTDEVICE2 gpdiJoystick = NULL;    // DirectInputDevice2 objects
  30.                                                     // support ForceFeedback
  31. static LPDIRECTINPUTEFFECT  gpdieBounce  = NULL;    // effect used when "bouncing"
  32.                                                     // off of the screen edges
  33. static LPDIRECTINPUTEFFECT  gpdieExplode = NULL;    // effect used when the ship
  34.                                                     // explodes
  35. static LPDIRECTINPUTEFFECT  gpdieFire    = NULL;    // effect used when firing
  36.  
  37. //===========================================================================
  38. // inputInitDirectInput
  39. //
  40. // Creates and initializes DirectInput objects
  41. //
  42. // Parameters:
  43. //
  44. // Returns:
  45. //
  46. //===========================================================================
  47. BOOL inputInitDirectInput(HINSTANCE hInst, HWND hWnd)
  48. {
  49.     HRESULT             hRes;
  50.     LPDIRECTINPUTDEVICE pdiTempDevice       = NULL;
  51.     DIDEVCAPS           didc;
  52.     GUID                guidDevice;
  53.     TCHAR               tszBuf[256];
  54.  
  55.     // create the base DirectInput object
  56.     hRes = DirectInputCreate(hInst, DIRECTINPUT_VERSION, &gpdi, NULL);
  57.     if(FAILED(hRes))
  58.     {
  59.         wsprintf(tszBuf, TEXT("DirectInputCreate() failed - %08Xh\n\n")
  60.                   TEXT("DirectX 5 or later required."), hRes);
  61.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  62.         return FALSE;
  63.     }
  64.  
  65.     // enumerate for joystick devices
  66.     hRes = gpdi->lpVtbl->EnumDevices(gpdi, DIDEVTYPE_JOYSTICK,
  67.                                     (LPDIENUMDEVICESCALLBACK)inputEnumDeviceProc,
  68.                                     &guidDevice,
  69.                                     DIEDFL_ATTACHEDONLY);
  70.     if(FAILED(hRes))
  71.     {
  72.         wsprintf(tszBuf, TEXT("EnumDevices() failed - %08Xh"), hRes);
  73.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  74.         return FALSE;
  75.     }
  76.  
  77.     // create a temporary "Device 1" object
  78.     hRes = gpdi->lpVtbl->CreateDevice(gpdi, &guidDevice, &pdiTempDevice, NULL);
  79.     if(FAILED(hRes))
  80.     {
  81.         wsprintf(tszBuf, TEXT("CreateDevice() failed - %08Xh\n\n")
  82.                   TEXT("This version of ""Space Donuts"" requires a JOYSTICK device."), hRes);
  83.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  84.         return FALSE;
  85.     }
  86.  
  87.     // get a "Device 2" object
  88.     //
  89.     // this is needed for access to the ForceFeedback functionality
  90.     hRes = pdiTempDevice->lpVtbl->QueryInterface(pdiTempDevice,
  91.                                                 &IID_IDirectInputDevice2,
  92.                                                 &gpdiJoystick);
  93.     if(FAILED(hRes))
  94.     {
  95.         wsprintf(tszBuf, TEXT("QueryInterface(IID_IDirectInputDevice2) failed - %08Xh"), hRes);
  96.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  97.         return FALSE;
  98.     }
  99.  
  100.     // we no longer need the temporary device, go ahead and release it.
  101.     if(pdiTempDevice)
  102.     {
  103.         pdiTempDevice->lpVtbl->Release(pdiTempDevice);
  104.         pdiTempDevice = NULL;
  105.     }
  106.  
  107.     // set the device's data format
  108.     //
  109.     // This tells the device object to act like a specific device --
  110.     // in our case, like a joystick
  111.     hRes = gpdiJoystick->lpVtbl->SetDataFormat(gpdiJoystick, &c_dfDIJoystick);
  112.     if(FAILED(hRes))
  113.     {
  114.         wsprintf(tszBuf, TEXT("SetDataFormat(Joystick) failed - %08Xh"), hRes);
  115.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  116.         return FALSE;
  117.     }
  118.  
  119.     // set the device's cooperative level
  120.     //
  121.     // ForceFeedback requires Exclusive access to the device.
  122.     hRes = gpdiJoystick->lpVtbl->SetCooperativeLevel(gpdiJoystick, hWnd,
  123.                                                     DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  124.     if(FAILED(hRes))
  125.     {
  126.         wsprintf(tszBuf, TEXT("SetCooperativeLevel(Exclusive | Foreground) failed - %08Xh"), hRes);
  127.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  128.         return FALSE;
  129.     }
  130.  
  131.     // set joystick parameters (deadzone, etc)
  132.     if(!inputPrepareDevice())
  133.     {
  134.         MessageBox(hWnd, TEXT("Device preparation failed"),
  135.                   TEXT("Space Donuts - Force Feedback"), MB_OK);
  136.         return FALSE;
  137.     }
  138.  
  139.     // get the device capabilities
  140.     //
  141.     // We're going to check to see if the device we created supports
  142.     // ForceFeedback.  If so, we will create effects, if not, we'll
  143.     // support standard joystick functionality
  144.     fIsFFDevice = FALSE;
  145.     didc.dwSize = sizeof(DIDEVCAPS);
  146.     hRes = gpdiJoystick->lpVtbl->GetCapabilities(gpdiJoystick, &didc);
  147.     if(FAILED(hRes))
  148.     {
  149.         wsprintf(tszBuf, TEXT("GetCapabilities() failed - %08Xh"), hRes);
  150.         MessageBox(hWnd, tszBuf, TEXT("Space Donuts - Force Feedback"), MB_OK);
  151.         return FALSE;
  152.     }
  153.     if(didc.dwFlags & DIDC_FORCEFEEDBACK)
  154.     {
  155.         OutputDebugString("ForceFeedback device found.\n");
  156.  
  157.         // get the gain level from the user
  158.         DialogBox(hInst, MAKEINTRESOURCE(IDD_FORCE), hWnd, inputForceLevelDlgProc);
  159.  
  160.         // we're supporting ForceFeedback
  161.         fIsFFDevice = TRUE;
  162.         if(!inputCreateEffect(EF_BOUNCE | EF_FIRE | EF_EXPLODE))
  163.         {
  164.             OutputDebugString("inputCreateEffects() failed - ForceFeedback disabled\n");
  165.         }
  166.     } //** end if(ForceFeedback device)
  167.  
  168.     // if we get here, we succeeded
  169.     return TRUE;
  170.  
  171. } //*** end inputInitDirectInput()
  172.  
  173.  
  174. //===========================================================================
  175. // inputCleanupDirectInput
  176. //
  177. // Cleans up DirectInput objects
  178. //
  179. // Parameters: none
  180. //
  181. // Returns: nothing
  182. //
  183. //===========================================================================
  184. void inputCleanupDirectInput(void)
  185. {
  186.     OutputDebugString("Cleaning up after DirectInput\n");
  187.  
  188.     // Release() effect objects
  189.     if(gpdieBounce)
  190.     {
  191.         gpdieBounce->lpVtbl->Release(gpdieBounce);
  192.         gpdieBounce = NULL;
  193.     }
  194.     if(gpdieExplode)
  195.     {
  196.         gpdieExplode->lpVtbl->Release(gpdieExplode);
  197.         gpdieExplode = NULL;
  198.     }
  199.     if(gpdieFire)
  200.     {
  201.         gpdieFire->lpVtbl->Release(gpdieFire);
  202.         gpdieFire = NULL;
  203.     }
  204.  
  205.     // Unacquire() and Release() device objects
  206.     //
  207.     // It is perfectly safe to call Unacquire() on a device that is not
  208.     // currently acquired.  In fact, it is good practice to call
  209.     // Unacquire() just prior to Release().
  210.     if(gpdiJoystick)
  211.     {
  212.         gpdiJoystick->lpVtbl->Unacquire(gpdiJoystick);
  213.         gpdiJoystick->lpVtbl->Release(gpdiJoystick);
  214.         gpdiJoystick = NULL;
  215.     }
  216.  
  217.     // Release() base object
  218.     if(gpdi)
  219.     {
  220.         gpdi->lpVtbl->Release(gpdi);
  221.         gpdi = NULL;
  222.     }
  223.  
  224. } //*** end inputCleanupDirectInput()
  225.  
  226.  
  227. //===========================================================================
  228. // inputEnumDeviceProc
  229. //
  230. // Enumerates DirectInput devices of type specified in call to
  231. //  IDirectInput::EnumDevices()
  232. //
  233. // Parameters:
  234. //
  235. // Returns:
  236. //
  237. //===========================================================================
  238. BOOL CALLBACK inputEnumDeviceProc(LPDIDEVICEINSTANCE pdidi, LPVOID pv)
  239. {
  240.     GUID *pguidDevice = NULL;
  241.  
  242.     // validate pv
  243.     // BUGBUG
  244.  
  245.     // report back the instance guid of the device we enumerated
  246.     if(pv)
  247.     {
  248.  
  249.         pguidDevice = (GUID *)pv;
  250.  
  251.         *pguidDevice = pdidi->guidInstance;
  252.  
  253.     }
  254.  
  255.     // BUGBUG for now, stop after the first device has been found
  256.     return DIENUM_STOP;
  257.  
  258. } //*** end inputEnumDeviceProc()
  259.  
  260.  
  261. //===========================================================================
  262. // inputEnumEffectTypeProc
  263. //
  264. // Enumerates ForceFeedback effect types (ie "Constant Force").
  265. //
  266. // Parameters:
  267. //
  268. // Returns:
  269. //
  270. //===========================================================================
  271. BOOL CALLBACK inputEnumEffectTypeProc(LPCDIEFFECTINFO pei, LPVOID pv)
  272. {
  273.     GUID *pguidEffect = NULL;
  274.  
  275.     // validate pv
  276.     // BUGBUG
  277.  
  278.     // report back the guid of the effect we enumerated
  279.     if(pv)
  280.     {
  281.  
  282.         pguidEffect = (GUID *)pv;
  283.  
  284.         *pguidEffect = pei->guid;
  285.  
  286.     }
  287.  
  288.     // BUGBUG - look at this some more....
  289.     return DIENUM_STOP;
  290.  
  291. } //*** end inputEnumEffectTypeProc()
  292.  
  293.  
  294. //===========================================================================
  295. // inputAcquireDevices
  296. //
  297. // Acquires the input device(s).
  298. //
  299. // Parameters:
  300. //
  301. // Returns:
  302. //
  303. //===========================================================================
  304. BOOL inputAcquireDevices(void)
  305. {
  306.     if(!gpdiJoystick)
  307.     {
  308.         return FALSE;
  309.     }
  310.  
  311.     // reacquire the device
  312.     if(SUCCEEDED(gpdiJoystick->lpVtbl->Acquire(gpdiJoystick)))
  313.     {
  314.  
  315.  
  316.         // DirectInput automatically resets the device whenever
  317.         // ownership changes, so we can assume we've got a device
  318.         // unsullied by its previous owner.
  319.         inputCreateEffect(EF_BOUNCE | EF_FIRE | EF_EXPLODE);
  320.  
  321.         return TRUE;
  322.     }
  323.     // if we get here, we did >not< acquire the device
  324.     return FALSE;
  325.  
  326. } //*** end inputAcquireDevices()
  327.  
  328.  
  329. //===========================================================================
  330. // inputCreateEffect
  331. //
  332. // Creates the DirectInputEffect object(s) used by the application
  333. //
  334. // Parameters:
  335. //
  336. // Returns:
  337. //
  338. //===========================================================================
  339. BOOL inputCreateEffect(DWORD dwEffectFlags)
  340. {
  341.     HRESULT         hRes;
  342.     GUID            guidEffect;
  343.     DIEFFECT        diEffect;
  344.     DIENVELOPE      diEnvelope;
  345.     DWORD           rgdwAxes[2];
  346.     LONG            rglDirections[2];
  347.     DICONSTANTFORCE dicf;
  348.     DIPERIODIC      dipf;
  349.     TCHAR           tszBuf[256];
  350.  
  351.     // make sure that we have a non-NULL device object
  352.     if(!gpdiJoystick)
  353.     {
  354.         return FALSE;
  355.     }
  356.  
  357.     // initialize DIEFFECT and DIENVELOPE structures
  358.     ZeroMemory(&diEffect, sizeof(DIEFFECT));
  359.     ZeroMemory(&diEnvelope, sizeof(DIENVELOPE));
  360.  
  361.     // these fields are the same for all effects we will be creating
  362.     diEffect.dwSize                     = sizeof(DIEFFECT);
  363.     diEffect.dwSamplePeriod             = 0; // use default sample period
  364.     diEffect.dwTriggerButton            = DIEB_NOTRIGGER;
  365.     diEffect.dwTriggerRepeatInterval    = 0;
  366.     diEffect.rgdwAxes                   = rgdwAxes;
  367.     diEffect.rglDirection               = rglDirections;
  368.     diEffect.dwGain                     = dwGain; // gain selected by user
  369.  
  370.     // enumerate for a constant force effect
  371.     //
  372.     // both the "bounce" and "fire" effects will be based on the first
  373.     // constant force effect enumerated
  374.     if((dwEffectFlags & EF_BOUNCE) || (dwEffectFlags & EF_FIRE))
  375.     {
  376.         hRes = gpdiJoystick->lpVtbl->EnumEffects(gpdiJoystick,
  377.                                                 (LPDIENUMEFFECTSCALLBACK)inputEnumEffectTypeProc,
  378.                                                 &guidEffect, DIEFT_CONSTANTFORCE);
  379.         if(FAILED(hRes))
  380.         {
  381.             OutputDebugString("EnumEffects(Constant Force) failed\n");
  382.             return FALSE;
  383.         }
  384.     }
  385.  
  386.     // create "bounce" effect
  387.     if(dwEffectFlags & EF_BOUNCE)
  388.     {
  389.         // if we have already created this effect...
  390.         //
  391.         // Call Release() before recreating it
  392.         if(gpdieBounce)
  393.         {
  394.             gpdieBounce->lpVtbl->Release(gpdieBounce);
  395.             gpdieBounce = NULL;
  396.         }
  397.  
  398.         // prepare the DICONSTANTFORCE structure
  399.         //
  400.         // this is the type-specific data for this force
  401.         dicf.lMagnitude                     = 10000;
  402.  
  403.         // what axes and directions to use?
  404.         // (directions do not matter at this point, set them to 0)
  405.         rgdwAxes[0]                         = DIJOFS_X;
  406.         rgdwAxes[1]                         = DIJOFS_Y;
  407.         rglDirections[0]                    = 0;
  408.         rglDirections[1]                    = 0;
  409.  
  410.         // prepare the DIEFFECT structure
  411.         //
  412.         // fill in the force-specific values
  413.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
  414.         diEffect.dwDuration                 = 200000;
  415.         diEffect.cAxes                      = 2;
  416.         diEffect.lpEnvelope                 = NULL;
  417.         diEffect.cbTypeSpecificParams       = sizeof(DICONSTANTFORCE);
  418.         diEffect.lpvTypeSpecificParams      = &dicf;
  419.  
  420.         // call CreateEffect()
  421.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  422.                                                     &diEffect, &gpdieBounce,
  423.                                                     NULL);
  424.         if(FAILED(hRes))
  425.         {
  426.             wsprintf(tszBuf, "CreateEffect(Bounce) failed - %08Xh\n", hRes);
  427.             OutputDebugString(tszBuf);
  428.             return FALSE;
  429.         }
  430.  
  431.     } //** end if(bounce effect)
  432.  
  433.     // create "fire" effect
  434.     if(dwEffectFlags & EF_FIRE)
  435.     {
  436.         // if we have already created this effect...
  437.         //
  438.         // Call Release() before recreating it
  439.         if(gpdieFire)
  440.         {
  441.             gpdieFire->lpVtbl->Release(gpdieFire);
  442.             gpdieFire = NULL;
  443.         }
  444.  
  445.         // prepare the DICONSTANTFORCE structure
  446.         //
  447.         // this is the type-specific data for this force
  448.         dicf.lMagnitude                     = 10000;
  449.  
  450.         // what axes and directions to use?
  451.         rgdwAxes[0]                         = DIJOFS_Y;
  452.         rglDirections[0]                    = 1;
  453.  
  454.         // prepare the DIEFFECT structure
  455.         //
  456.         // fill in the force-specific values
  457.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
  458.         diEffect.dwDuration                 = 20000;
  459.         diEffect.cAxes                      = 1;
  460.         diEffect.lpEnvelope                 = NULL;
  461.         diEffect.cbTypeSpecificParams       = sizeof(DICONSTANTFORCE);
  462.         diEffect.lpvTypeSpecificParams      = &dicf;
  463.  
  464.         // call CreateEffect()
  465.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  466.                                                     &diEffect, &gpdieFire,
  467.                                                     NULL);
  468.         if(FAILED(hRes))
  469.         {
  470.             wsprintf(tszBuf, "CreateEffect(Fire) failed - %08Xh\n", hRes);
  471.             OutputDebugString(tszBuf);
  472.             return FALSE;
  473.         }
  474.  
  475.     } //** end if(fire effect)
  476.  
  477.     // enumerate for a periodic effect
  478.     //
  479.     // the "explode" effect will be based on the first
  480.     // periodic effect enumerated
  481.     if((dwEffectFlags & EF_EXPLODE))
  482.     {
  483.         hRes = gpdiJoystick->lpVtbl->EnumEffects(gpdiJoystick,
  484.                                                 (LPDIENUMEFFECTSCALLBACK)inputEnumEffectTypeProc,
  485.                                                 &guidEffect, DIEFT_PERIODIC);
  486.         if(FAILED(hRes))
  487.         {
  488.             OutputDebugString("EnumEffects(Periodic Force) failed\n");
  489.             return FALSE;
  490.         }
  491.     }
  492.     // create "explode" effect
  493.     if(dwEffectFlags & EF_FIRE)
  494.     {
  495.         // if we have already created this effect...
  496.         //
  497.         // Call Release() before recreating it
  498.         if(gpdieExplode)
  499.         {
  500.             gpdieExplode->lpVtbl->Release(gpdieExplode);
  501.             gpdieExplode = NULL;
  502.         }
  503.  
  504.         // prepare the DIENVELOPE structure
  505.         //
  506.         // We want to shape the explode effect so that it starts
  507.         // at it's peak and then fades out
  508.         diEnvelope.dwSize                   = sizeof(DIENVELOPE);
  509.         diEnvelope.dwAttackLevel            = 0;
  510.         diEnvelope.dwAttackTime             = 0;
  511.         diEnvelope.dwFadeLevel              = 0;
  512.         diEnvelope.dwFadeTime               = 1000000;
  513.  
  514.         // prepare the DIPERIODIC structure
  515.         //
  516.         // this is the type-specific data for this force
  517.         dipf.dwMagnitude                    = 10000;
  518.         dipf.lOffset                        = 0;
  519.         dipf.dwPhase                        = 0;
  520.         dipf.dwPeriod                       = 100000;
  521.  
  522.         // what axes and directions to use?
  523.         rgdwAxes[0]                         = DIJOFS_X;
  524.         rglDirections[0]                    = 0;
  525.  
  526.         // prepare the DIEFFECT structure
  527.         //
  528.         // fill in the force-specific values
  529.         diEffect.dwFlags                    = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
  530.         diEffect.dwDuration                 = 1000000;
  531.         diEffect.cAxes                      = 1;
  532.         diEffect.lpEnvelope                 = &diEnvelope;
  533.         diEffect.cbTypeSpecificParams       = sizeof(DIPERIODIC);
  534.         diEffect.lpvTypeSpecificParams      = &dipf;
  535.  
  536.         // call CreateEffect()
  537.         hRes = gpdiJoystick->lpVtbl->CreateEffect(gpdiJoystick, &guidEffect,
  538.                                                     &diEffect, &gpdieExplode,
  539.                                                     NULL);
  540.         if(FAILED(hRes))
  541.         {
  542.             wsprintf(tszBuf, "CreateEffect(Explode) failed - %08Xh\n", hRes);
  543.             OutputDebugString(tszBuf);
  544.             return FALSE;
  545.         }
  546.  
  547.     } //** end if(explode effect)
  548.  
  549.     return TRUE;
  550.  
  551. } //*** end inputCreateEffects()
  552.  
  553.  
  554. //===========================================================================
  555. // inputProcessDeviceInput
  556. //
  557. // Processes data from the input device.  Uses GetDeviceState().
  558. //
  559. // Parameters:
  560. //
  561. // Returns:
  562. //
  563. //===========================================================================
  564. DWORD inputProcessDeviceInput(void)
  565. {
  566.     HRESULT     hRes;
  567.     DIJOYSTATE  dijs;
  568.     DWORD       dwInput = 0;
  569.  
  570.     // poll the joystick to read the current state
  571.     hRes = gpdiJoystick->lpVtbl->Poll(gpdiJoystick);
  572.  
  573.     // read the device state
  574.     hRes = gpdiJoystick->lpVtbl->GetDeviceState(gpdiJoystick, sizeof(DIJOYSTATE),
  575.                                                 &dijs);
  576.     if(FAILED(hRes))
  577.     {
  578.         if((hRes == DIERR_INPUTLOST))
  579.         {
  580.             inputAcquireDevices();
  581.         }
  582.  
  583.         // we did not read anything, return no motion
  584.         return 0;
  585.     }
  586.  
  587.     // process device state
  588.     //
  589.     // to preserve as much of the existing input handling code from the
  590.     // original space donuts sample, we will be converting the joystick data to
  591.     // "keyboard" input
  592.  
  593.     //* x-axis (left)
  594.     if(dijs.lX < 0)
  595.     {
  596.         dwInput |= KEY_LEFT;
  597.     }
  598.     //* x-axis (right)
  599.     if(dijs.lX > 0)
  600.     {
  601.         dwInput |= KEY_RIGHT;
  602.     }
  603.     //* y-axis (forward)
  604.     if(dijs.lY < 0)
  605.     {
  606.         dwInput |= KEY_UP;
  607.     }
  608.     //* y-axis (backward)
  609.     if(dijs.lY > 0)
  610.     {
  611.         dwInput |= KEY_DOWN;
  612.     }
  613.     //* button 0 (fire)
  614.     if(dijs.rgbButtons[0] & 0x80)
  615.     {
  616.         dwInput |= KEY_FIRE;
  617.     }
  618.     //* button 1 (shield)
  619.     if(dijs.rgbButtons[1] & 0x80)
  620.     {
  621.         dwInput |= KEY_SHIELD;
  622.     }
  623.     //* button 2 (stop) - requires a joystick with more than 2 buttons
  624.     if(dijs.rgbButtons[2] & 0x80)
  625.     {
  626.         dwInput |= KEY_STOP;
  627.     }
  628.  
  629.     // return the new device state
  630.     return dwInput;
  631.  
  632. } //*** end inputProcessDeviceInput()
  633.  
  634.  
  635. //===========================================================================
  636. // inputPrepareDevice
  637. //
  638. // Performs device preparation by setting the device's parameters (ie
  639. // deadzone).
  640. //
  641. // Parameters:
  642. //
  643. // Returns:
  644. //
  645. //===========================================================================
  646. BOOL inputPrepareDevice(void)
  647. {
  648.     HRESULT       hRes;
  649.     DIPROPRANGE   dipr;
  650.     DIPROPDWORD   dipdw;
  651.  
  652.  
  653.     // quick check to make sure that the object pointer is non-NULL
  654.     if(!gpdiJoystick)
  655.     {
  656.         return FALSE;
  657.     }
  658.  
  659.     // call Unacquire() on the device
  660.     //
  661.     // SetParameter() will fail if a device is currently acquired, we are
  662.     // doing this here in case we get careless and forget to call this
  663.     // function either before we call Acquire() or after we call Unacquire().
  664.     gpdiJoystick->lpVtbl->Unacquire(gpdiJoystick);
  665.  
  666.     // set the axis ranges for the device
  667.     //
  668.     // We will use the same range for the X and Y axes.  We are setting them
  669.     // fairly low since we are not concerned with anything other than
  670.     // "left", "right", "forward", "backward" and "centered"
  671.     //* prepare DIPROPRANGE structure
  672.     dipr.diph.dwSize        = sizeof(DIPROPRANGE);
  673.     dipr.diph.dwHeaderSize  = sizeof(dipr.diph);
  674.     dipr.diph.dwHow         = DIPH_BYOFFSET;
  675.     dipr.lMin               = RANGE_MIN;  // negative to the left/top
  676.     dipr.lMax               = RANGE_MAX;  // positive to the right/bottom
  677.     //* x-axis
  678.     dipr.diph.dwObj         = DIJOFS_X;
  679.     //* set the x-axis range property
  680.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_RANGE, &dipr.diph);
  681.     if(FAILED(hRes))
  682.     {
  683.         OutputDebugString("SetProperty(RANGE, X-Axis) failed.\n");
  684.         return FALSE;
  685.     }
  686.     //* y-axis
  687.     dipr.diph.dwObj         = DIJOFS_Y;
  688.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_RANGE, &dipr.diph);
  689.     if(FAILED(hRes))
  690.     {
  691.         OutputDebugString("SetProperty(RANGE, Y-Axis) failed.\n");
  692.         return FALSE;
  693.     }
  694.  
  695.     // set the deadzone for the device
  696.     //
  697.     // We will use the same deadzone percentage for the X and Y axes.
  698.     // This call uses a symbolic constant for the deadzone percentage so that
  699.     // it is easy to change if we decide we don't like it.
  700.     //* prepare DIPROPDWORD structure
  701.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  702.     dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
  703.     dipdw.diph.dwHow        = DIPH_BYOFFSET;
  704.     dipdw.dwData            = DEADZONE;
  705.     //* set the x-axis range property
  706.     dipdw.diph.dwObj         = DIJOFS_X;
  707.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_DEADZONE, &dipdw.diph);
  708.     if(FAILED(hRes))
  709.     {
  710.         OutputDebugString("SetProperty(DEADZONE, X-Axis) failed.\n");
  711.         return FALSE;
  712.     }
  713.     //* y-axis
  714.     dipdw.diph.dwObj         = DIJOFS_Y;
  715.     hRes = gpdiJoystick->lpVtbl->SetProperty(gpdiJoystick, DIPROP_DEADZONE, &dipdw.diph);
  716.     if(FAILED(hRes))
  717.     {
  718.         OutputDebugString("SetProperty(DEADZONE, Y-Axis) failed.\n");
  719.         return FALSE;
  720.     }
  721.  
  722.     // set the ForceFeedback gain
  723.     //
  724.     // If the device supports feedback, use the user selected gain level
  725.     // to scale the strength of the forces applied to the stick.  We do this
  726.     // so that if a small child is playing the game, the stick does not jerk
  727.     // hard enough to hurt them, yet an adult can have a stronger force
  728.     // experience
  729.     if(fIsFFDevice)
  730.     {
  731.         // BUGBUG get setting from user (done somewhere else)
  732.         dwGain = FF_ADULT;
  733.     }
  734.  
  735.     // Acquire the device(s)
  736.     //
  737.     // This is being done as a convenience since we unacquired earlier in
  738.     // this function.  This does not guarantee that the device will be
  739.     // acquired at the time we return from the function (in other words, we
  740.     // are not going to spin here until we get a succeessful acquisition).
  741.     inputAcquireDevices();
  742.  
  743.     // we've actually done somthing here
  744.     return TRUE;
  745.  
  746. } //** end inputPrepareDevice()
  747.  
  748.  
  749. //===========================================================================
  750. // inputPlayEffect
  751. //
  752. // Plays specified effect object.
  753. //
  754. // Parameters:
  755. //
  756. // Returns:
  757. //
  758. //===========================================================================
  759. BOOL inputPlayEffect(DWORD dwEffectFlags, LONG lDirection)
  760. {
  761.     HRESULT         hRes;
  762.     DIEFFECT        diEffect;
  763.     LONG            rglDirections[2] = { 0, 0 };
  764.  
  765.     // initialize DIEFFECT structure
  766.     ZeroMemory(&diEffect, sizeof(DIEFFECT));
  767.     diEffect.dwSize = sizeof(DIEFFECT);
  768.  
  769.     // play "bounce" effect?
  770.     if(dwEffectFlags & EF_BOUNCE)
  771.     {
  772.         if(gpdieBounce)
  773.         {
  774.             // set the direction
  775.             //
  776.             // since this is a polar coordinate effect, we will pass the angle
  777.             // in as the direction relative to the x-axis, and will leave 0
  778.             // for the y-axis direction
  779.             //
  780.             // Direction is passed in in degrees, we convert to 100ths
  781.             // of a degree to make it easier for the caller.
  782.             rglDirections[0]        = lDirection * 100;
  783.             diEffect.dwFlags        = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
  784.             diEffect.cAxes          = 2;
  785.             diEffect.rglDirection   = rglDirections;
  786.             hRes = gpdieBounce->lpVtbl->SetParameters(gpdieBounce,
  787.                                                         &diEffect,
  788.                                                         DIEP_DIRECTION);
  789.             if(FAILED(hRes))
  790.             {
  791.                 OutputDebugString("SetParameters(Bounce effect) failed\n");
  792.                 return FALSE;
  793.             }
  794.  
  795.             // play the effect
  796.             hRes = gpdieBounce->lpVtbl->Start(gpdieBounce, 1, 0);
  797.             if(FAILED(hRes))
  798.             {
  799.                 OutputDebugString("Start(Bounce effect) failed\n");
  800.                 return FALSE;
  801.             }
  802.  
  803.         }
  804.  
  805.     } //** end if(play bounce)
  806.  
  807.     // play "fire" effect?
  808.     if(dwEffectFlags & EF_FIRE)
  809.     {
  810.         if(gpdieFire)
  811.         {
  812.             // play the effect
  813.             hRes = gpdieFire->lpVtbl->Start(gpdieFire, 1, 0);
  814.             if(FAILED(hRes))
  815.             {
  816.                 OutputDebugString("Start(Fire effect) failed\n");
  817.                 return FALSE;
  818.             }
  819.         }
  820.     } //** end if(play fire)
  821.  
  822.     // play "explode" effect?
  823.     if(dwEffectFlags & EF_EXPLODE)
  824.     {
  825.         if(gpdieExplode)
  826.         {
  827.             // BUGBUG how many iterations of the effect??
  828.             hRes = gpdieExplode->lpVtbl->Start(gpdieExplode, 1, 0);
  829.             if(FAILED(hRes))
  830.             {
  831.                 OutputDebugString("Start(Explode effect) failed\n");
  832.                 return FALSE;
  833.             }
  834.         }
  835.     } //** end if(play explode)
  836.  
  837.     return TRUE;
  838.  
  839. } //*** end inputPlayEffect()
  840.  
  841.  
  842. //===========================================================================
  843. // inputForceLevelDlgProc
  844. //
  845. // Dialog proceedure for handling the Force Level dialog box.
  846. //
  847. // Parameters:
  848. //
  849. // Returns:
  850. //
  851. //===========================================================================
  852. BOOL CALLBACK inputForceLevelDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  853.                                     LPARAM lParam)
  854. {
  855.     int nSelection = 0;
  856.  
  857.     switch(uMsg)
  858.     {
  859.         case WM_INITDIALOG:
  860.              // select default / current setting
  861.              switch(dwGain)
  862.              {
  863.                 case FF_CHILD:
  864.                      nSelection = IDC_CHILD;
  865.                      break;
  866.  
  867.                 case FF_BODYBUILDER:
  868.                      nSelection = IDC_BODYBUILDER;
  869.                      break;
  870.  
  871.                 case FF_ADULT:
  872.                 default:
  873.                      nSelection = IDC_ADULT;
  874.                      break;
  875.              }
  876.              CheckRadioButton(hWnd, IDC_CHILD, IDC_BODYBUILDER, nSelection);
  877.              return TRUE;
  878.  
  879.         case WM_COMMAND:
  880.              {
  881.                 switch(LOWORD(wParam))
  882.                 {
  883.                     case IDOK:
  884.                          // get user's force level selection
  885.                          // BUGBUG
  886.                          if(IsDlgButtonChecked(hWnd, IDC_CHILD))
  887.                          {
  888.                             OutputDebugString("Child level\n");
  889.                             dwGain = FF_CHILD;
  890.                          }
  891.                          else if(IsDlgButtonChecked(hWnd, IDC_BODYBUILDER))
  892.                          {
  893.                             OutputDebugString("Bodybuilder level\n");
  894.                             dwGain = FF_BODYBUILDER;
  895.                          }
  896.                          else
  897.                          {
  898.                             OutputDebugString("Adult level (Default)\n");
  899.                             dwGain = FF_ADULT;
  900.                          }
  901.                          EndDialog(hWnd, 0);
  902.                          break;
  903.                 }
  904.  
  905.              }
  906.              break;
  907.  
  908.     }
  909.  
  910.     return FALSE;
  911. }
  912.