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 / duel / duel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  14.5 KB  |  626 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:       duel.c
  6.  *  Content:    Multi-player duel
  7.  *
  8.  *
  9.  ***************************************************************************/
  10. #define INITGUID
  11. #include "duel.h"
  12. #include "gameproc.h"
  13. #include "gfx.h"
  14. #include "comm.h"
  15. #include "input.h"
  16. #include "lobby.h"
  17. #include "wizard.h"
  18. #include "util.h"
  19. #include "sfx.h"
  20.  
  21. // {33925241-05F8-11d0-8063-00A0C90AE891}
  22. DEFINE_GUID(DUEL_GUID, 
  23. 0x33925241, 0x5f8, 0x11d0, 0x80, 0x63, 0x0, 0xa0, 0xc9, 0xa, 0xe8, 0x91);
  24.  
  25. /*
  26.  * Externals
  27.  */
  28. extern DWORD            gdwFrameCount;
  29. extern DWORD            gdwFrameTime;
  30. extern int                gnProgramState;
  31. extern SHIP                gOurShip;
  32. extern LPDPLCONNECTION    glpdplConnection;
  33. extern DPID                gOurID;
  34. extern BOOL             gbNoField;
  35.  
  36.  
  37. /*
  38.  * Globals
  39.  */
  40. LPGUID                  glpGuid;                // Duel's GUID
  41. HWND                    ghWndMain;                // Main application window handle
  42. HINSTANCE                ghinst;                    // Application instance handle        
  43. BOOL                    gbShowFrameCount=TRUE;    // Show FPS ?
  44. BOOL                    gbIsActive;                // Is the application active ?
  45. BOOL                    gbUseEmulation;            // DDHEL or DDHAL for Graphics
  46. BOOL                    gbIsHost;                // Are we hosting or joining a game        
  47. DWORD                   gdwKeys;                // User keyboard input
  48. DWORD                   gdwOldKeys;             // Last frame's keyboard input
  49. BOOL                    gbFullscreen=FALSE;        // Window or FullScreen mode ?
  50. RECT                    grcWindow;                // client rectangle of main window
  51. HANDLE                    ghThread;                // handle to wizard thread
  52. TCHAR                    gtszClassName[MAX_CLASSNAME]; // Duel's class name    
  53. BOOL                    gbReliable;             // sends are reliable
  54.  
  55. /*
  56.  * Statics
  57.  */
  58. static BOOL                gbReinitialize;            // used for switching display modes
  59.  
  60. /*
  61.  * WinMain
  62.  */
  63. int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
  64.                         int nCmdShow )
  65. {
  66.     BOOL bHelp=FALSE;
  67.     MSG     msg;
  68.  
  69.     ghinst = hinstance;
  70.  
  71.     CoInitialize( NULL );
  72.  
  73.     // Parse command line
  74.     while( lpCmdLine[0] == '-' )
  75.     {
  76.         lpCmdLine++;
  77.  
  78.         switch (*lpCmdLine++)
  79.         {
  80.         case 'e':
  81.             gbUseEmulation = TRUE;
  82.             break;
  83.         case 'd':
  84.             gbNoField = TRUE;
  85.             break;
  86.         case '?':
  87.         default:
  88.             bHelp= TRUE;
  89.             gbFullscreen= FALSE;  // give help in windowed mode
  90.             break;
  91.         }
  92.  
  93.         while( isspace(*lpCmdLine) )
  94.         {
  95.             lpCmdLine++;
  96.         }
  97.     }
  98.  
  99.     /*
  100.      * Give user help if asked for
  101.      */
  102.  
  103.     if( bHelp )
  104.     {
  105.         TCHAR tszHelpMsg[MAX_HELPMSG];
  106.         TCHAR tszTitle[MAX_WINDOWTITLE];
  107.  
  108.         LoadString(ghinst, IDS_DUEL_HELP, tszHelpMsg, MAX_HELPMSG);
  109.         LoadString(ghinst, IDS_DUEL_TITLE, tszTitle, MAX_WINDOWTITLE);
  110.         MessageBox(ghWndMain, tszHelpMsg, tszTitle, MB_OK );
  111.         return TRUE;
  112.     }
  113.  
  114.  
  115.     if( !InitApplication(hinstance) )
  116.     {
  117.         return FALSE;
  118.     }
  119.  
  120.     // were we launched by a lobby ?
  121.     if (LaunchedByLobby())
  122.     {
  123.         // start game
  124.         PostMessage(ghWndMain, UM_LAUNCH, 0, 0);
  125.         gbIsActive = TRUE;
  126.     }
  127.  
  128.     gdwFrameTime = timeGetTime();
  129.  
  130.     while( TRUE )
  131.     {
  132.         if (gbIsActive)
  133.         {
  134.             // any windows messages ? (returns immediately)
  135.             if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
  136.             {
  137.                 if( !GetMessage( &msg, NULL, 0, 0 ) )
  138.                 {
  139.                     return msg.wParam;
  140.                 }
  141.                 TranslateMessage(&msg);
  142.                 DispatchMessage(&msg);
  143.             }
  144.             else
  145.             {
  146.                 // Poll our receive queue. Polling is used in the sample only for simplicity.
  147.                 // Receiving messages using an event is the recommended way.
  148.                 if (gnProgramState != PS_SPLASH)
  149.                 {
  150.                     ReceiveMessages();
  151.                 }
  152.  
  153.                 // update screen
  154.                 if (!UpdateFrame())
  155.                 {
  156.                     ExitGame();
  157.                 }
  158.             }
  159.         }
  160.         else
  161.         {
  162.             // any windows messages ? (blocks until a message arrives)
  163.             if( !GetMessage( &msg, NULL, 0, 0 ) )
  164.             {
  165.                 return msg.wParam;
  166.             }
  167.             TranslateMessage(&msg);
  168.             DispatchMessage(&msg);
  169.         }
  170.     }
  171.  
  172.     CoUninitialize();
  173.  
  174. } /* WinMain */
  175.  
  176.  
  177. /*
  178.  * MainWndproc
  179.  *
  180.  * Callback for all Windows messages
  181.  */
  182. long WINAPI MainWndproc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
  183. {
  184.     PAINTSTRUCT ps;
  185.     HDC         hdc;
  186.     DWORD dwRetCode;
  187.     DWORD dwTid;
  188.  
  189.     switch( message )
  190.     {
  191.     case WM_SIZE:
  192.     case WM_MOVE:
  193.         // get the client rectangle
  194.         if (gbFullscreen)
  195.         {
  196.             SetRect(&grcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
  197.         }
  198.         else
  199.         {
  200.             GetClientRect(hWnd, &grcWindow);
  201.             ClientToScreen(hWnd, (LPPOINT)&grcWindow);
  202.             ClientToScreen(hWnd, (LPPOINT)&grcWindow+1);
  203.         }
  204.         break;
  205.  
  206.     case WM_ACTIVATE:
  207.         // ignore this message during reinitializing graphics
  208.         if (gbReinitialize) return 0;
  209.  
  210.         // When we are deactivated, although we don't update our screen, we still need to
  211.         // to empty our receive queue periodically as messages will pile up otherwise. 
  212.         // Polling the receive queue continuously even when we are deactivated causes our app
  213.         // to consume all the CPU time. To avoid hogging the CPU, we block on GetMessage() WIN API
  214.         // and setup a timer to wake ourselves up at regular intervals to process our messages.
  215.  
  216.         if (LOWORD(wParam) == WA_INACTIVE)
  217.         {
  218.             // deactivated
  219.             gbIsActive = FALSE;
  220.             if (PS_ACTIVE == gnProgramState)
  221.             {
  222.                 SetTimer(hWnd, RECEIVE_TIMER_ID, RECEIVE_TIMEOUT, NULL);
  223.             }
  224.         }
  225.         else
  226.         {
  227.             // activated
  228.             gbIsActive = TRUE;
  229.             if (PS_ACTIVE == gnProgramState)
  230.             {
  231.                 KillTimer(hWnd, RECEIVE_TIMER_ID);
  232.             }
  233.         }
  234.  
  235.         // set game palette, if activated in game mode
  236.         if (gbIsActive && (gnProgramState != PS_SPLASH))
  237.             SetGamePalette();
  238.  
  239.         ReacquireInputDevices();
  240.  
  241.         return 0;
  242.  
  243.     case WM_CREATE:
  244.         break;
  245.  
  246.     case WM_SYSKEYUP:
  247.         switch( wParam )
  248.         {
  249.         // handle ALT+ENTER (fullscreen/window mode)
  250.         case VK_RETURN:
  251.             // mode switch is allowed only during the game
  252.             if (gnProgramState == PS_ACTIVE)
  253.             {
  254.                 gbReinitialize = TRUE;
  255.                 ReleaseLocalData();  //only sound buffers have to be rels'd anyway.
  256.                 CleanupSfx();
  257.                 CleanupInput();
  258.                 CleanupGraphics();
  259.                 DestroyWindow(ghWndMain);
  260.                 gbFullscreen = !gbFullscreen;
  261.                 InitGraphics();
  262.                 InitInput();
  263.                 InitSfx();
  264.                 InitLocalSoundData();
  265.                 gbReinitialize = FALSE;
  266.             }
  267.             break;
  268.         }
  269.         break;
  270.  
  271.     case WM_KEYDOWN:
  272.         switch( wParam )
  273.         {
  274.         case 'r':
  275.         case 'R':
  276.             // toggle reliable status
  277.             gbReliable = !gbReliable;
  278.             UpdateTitle();
  279.             break;
  280.  
  281.         case VK_F1:
  282.             {
  283.                 TCHAR tszHelpMsg[MAX_HELPMSG];
  284.                 TCHAR tszTitle[MAX_WINDOWTITLE];
  285.  
  286.                 LoadString(ghinst, IDS_DUEL_HELP, tszHelpMsg, MAX_HELPMSG);
  287.                 LoadString(ghinst, IDS_DUEL_TITLE, tszTitle, MAX_WINDOWTITLE);
  288.                 MessageBox(ghWndMain, tszHelpMsg, tszTitle, MB_OK );
  289.             }
  290.             break;
  291.  
  292.         case VK_F5:
  293.             gbShowFrameCount = !gbShowFrameCount;
  294.             if( gbShowFrameCount )
  295.             {
  296.                 gdwFrameCount = 0;
  297.                 gdwFrameTime = timeGetTime();
  298.             }
  299.             break;
  300.  
  301.         case VK_RETURN:
  302.             if( (gnProgramState == PS_SPLASH) && !gbFullscreen)
  303.             {
  304.                 // get connection settings from user
  305.                 ghThread = CreateThread(NULL, 0, (LPVOID)DoWizard, 0, 0, &dwTid);
  306.             }
  307.             break;
  308.  
  309.         case VK_ESCAPE:
  310.         case VK_F12:
  311.             // adios
  312.             ExitGame();
  313.             return 0;
  314.         }
  315.         break;
  316.  
  317.     case WM_ERASEBKGND:
  318.         return 1;
  319.  
  320.     case WM_PAINT:
  321.         hdc = BeginPaint( hWnd, &ps );
  322.         if (gnProgramState == PS_SPLASH)
  323.         {
  324.             // display the splash screen
  325.             bltSplash(NULL);
  326.         }
  327.  
  328.         EndPaint( hWnd, &ps );
  329.         return 1;
  330.  
  331.     case UM_LAUNCH:
  332.         // cleanup the wizard thread
  333.         if (ghThread)
  334.         {
  335.             // wait for thread to exit
  336.             while (!GetExitCodeThread(ghThread, &dwRetCode));
  337.             CloseHandle(ghThread);
  338.         }
  339.         
  340.         // start the game in rest mode
  341.         gnProgramState = PS_REST;
  342.         LaunchGame();
  343.         return 1;
  344.  
  345.     case UM_ABORT:
  346.         // cleanup the wizard thread
  347.         if (ghThread)
  348.         {
  349.             // wait for thread to exit
  350.             while (!GetExitCodeThread(ghThread, &dwRetCode));
  351.             CloseHandle(ghThread);
  352.         }
  353.         ExitGame();
  354.         return 1;
  355.  
  356.     case WM_TIMER:
  357.         ReceiveMessages();
  358.         break;
  359.  
  360.     case WM_DESTROY:
  361.         // if gbReinitialize is TRUE don't quit, we are just switching display modes
  362.         if (!gbReinitialize)
  363.         {
  364.             CleanupApplication();
  365.             PostQuitMessage( 0 );
  366.         }
  367.         return 0;
  368.  
  369.     default:
  370.         break;
  371.     }
  372.     return DefWindowProc(hWnd, message, wParam, lParam);
  373.  
  374. } /* MainWndproc */
  375.  
  376. /*
  377.  * InitApplication
  378.  *
  379.  * Do that initialization stuff...
  380.  */
  381. BOOL InitApplication( HINSTANCE hinst )
  382. {
  383.     WNDCLASS    wc;
  384.     BOOL        rc;
  385.  
  386.     glpGuid = (LPGUID) &DUEL_GUID;
  387.  
  388.     LoadString(ghinst, IDS_DUEL_CLASSNAME, gtszClassName, MAX_CLASSNAME);
  389.  
  390.     wc.style = CS_DBLCLKS;
  391.     wc.lpfnWndProc = MainWndproc;
  392.     wc.cbClsExtra = 0;
  393.     wc.cbWndExtra = 0;
  394.     wc.hInstance = hinst;
  395.     wc.hIcon = LoadIcon( hinst, MAKEINTRESOURCE(DUEL_ICON));
  396.     wc.hCursor = LoadCursor( NULL, IDC_ARROW );
  397.     wc.hbrBackground = GetStockObject( BLACK_BRUSH );
  398.     wc.lpszMenuName =  NULL;
  399.     wc.lpszClassName = gtszClassName;
  400.     rc = RegisterClass( &wc );
  401.     if( !rc )
  402.     {
  403.         return FALSE;
  404.     }
  405.  
  406.     // Initialize all components
  407.     if ((!InitGraphics()) || (!InitInput()) || (!InitSfx()))
  408.     {
  409.         return FALSE;
  410.     }
  411.  
  412.     // start in splash mode
  413.     gnProgramState = PS_SPLASH;
  414.  
  415.     return TRUE;
  416.  
  417. } /* initApplication */
  418.  
  419. /*
  420.  * CleanupApplication
  421.  *
  422.  * Calls clean up on all components
  423.  */
  424. void CleanupApplication( void )
  425. {
  426.     CleanupComm();
  427.     CleanupSfx();
  428.     CleanupGraphics();
  429.     CleanupInput();
  430. }
  431.  
  432. /*
  433.  * LaunchedByLobby
  434.  *
  435.  * Determines if we were launched by a lobby. If so, gets the connection settings
  436.  * and creates our player using the information from the lobby
  437.  */
  438. BOOL LaunchedByLobby(void)
  439. {
  440.     HRESULT hr;
  441.     HWND    hwndStatus;
  442.  
  443.     // create a lobby object
  444.     hr = DPLobbyCreate();
  445.     if (FAILED(hr))
  446.     {
  447.         ShowError(IDS_DPLOBBY_ERROR_C);
  448.         return FALSE;
  449.     }
  450.  
  451.     // get connection settings from the lobby (into glpdplConnection)
  452.     hr = DPLobbyGetConnectionSettings();
  453.     if (FAILED(hr))
  454.     {
  455.         if (DPERR_NOTLOBBIED == hr)
  456.         {
  457.             // we were not lobbied - start up game normally
  458.             hr = DPLobbyRelease();
  459.             if (FAILED(hr))
  460.             {
  461.                 ShowError(IDS_DPLOBBY_ERROR_R);
  462.                 goto FAIL;
  463.             }
  464.             // move on
  465.             return FALSE;
  466.         }
  467.         else
  468.         {
  469.             ShowError(IDS_DPLOBBY_ERROR_GCS);
  470.             goto FAIL;
  471.         }
  472.     }
  473.  
  474.     // are we hosting or joining ?
  475.     if (glpdplConnection->dwFlags & DPLCONNECTION_CREATESESSION)
  476.     {
  477.         gbIsHost = TRUE;
  478.     }
  479.  
  480.     // set our session flags
  481.     glpdplConnection->lpSessionDesc->dwFlags = DPSESSION_MIGRATEHOST | 
  482.                                                  DPSESSION_KEEPALIVE;
  483.  
  484.     // let lobby know our connection flags
  485.     hr = DPLobbySetConnectionSettings();
  486.     if (FAILED(hr))
  487.     {
  488.         ShowError(IDS_DPLOBBY_ERROR_SCS);
  489.         goto FAIL;
  490.     }
  491.  
  492.     if ( !gbIsHost ) 
  493.     {
  494.         // show splash screen and 
  495.         // connection status if we are joining a game
  496.         UpdateWindow(ghWndMain);
  497.         hwndStatus = ShowConnectStatus();
  498.     }
  499.     else
  500.     {
  501.         // we are hosting, don't need connection status
  502.         hwndStatus = NULL;
  503.     }
  504.  
  505.     // connect to the lobby
  506.     hr = DPLobbyConnect();
  507.  
  508.     if ( hwndStatus )
  509.     {
  510.         // get rid of the connectino status window
  511.         DestroyWindow(hwndStatus);
  512.     }
  513.  
  514.     if (FAILED(hr))
  515.     {
  516.         ShowError(IDS_DPLOBBY_ERROR_CONNECT);
  517.         goto FAIL;
  518.     }
  519.  
  520.     // create our player
  521.     hr = DPlayCreatePlayer(
  522.                             &gOurID,
  523. #ifdef UNICODE
  524.                             glpdplConnection->lpPlayerName->lpszShortName,
  525. #else
  526.                             glpdplConnection->lpPlayerName->lpszShortNameA,
  527. #endif
  528.                             NULL,
  529.                             NULL,
  530.                             0
  531.                           );
  532.  
  533.     if (FAILED(hr))
  534.     {
  535.         ShowError(IDS_DPLAY_ERROR_CP);
  536.         goto FAIL;
  537.     }
  538.  
  539.  
  540.     // cleanup
  541.     hr = DPLobbyRelease();
  542.     if (FAILED(hr))
  543.     {
  544.         ShowError(IDS_DPLOBBY_ERROR_R);
  545.         goto FAIL;
  546.     }
  547.  
  548.     // we were lobbied
  549.     return TRUE;
  550.  
  551. FAIL:
  552.     // cleanup and exit
  553.     DPLobbyRelease();
  554.     ExitGame();
  555.     return FALSE;
  556. }
  557.  
  558. /*
  559.  * Displays error to the user
  560.  */
  561. BOOL ShowError( int iStrID )
  562. {
  563.     TCHAR tszMsg[MAX_ERRORMSG];
  564.     TCHAR tszTitle[MAX_WINDOWTITLE];
  565.  
  566.     LoadString(ghinst, iStrID, tszMsg, MAX_ERRORMSG);
  567.     LoadString(ghinst, IDS_DUEL_ERROR_TITLE, tszTitle, MAX_WINDOWTITLE);
  568.     MessageBox( ghWndMain, tszMsg, tszTitle, MB_OK );
  569.     return FALSE;
  570. }
  571.  
  572. /*
  573.  * Displays connection status to the user
  574.  */
  575. HWND ShowConnectStatus(void)
  576. {
  577.     HWND hwnd;
  578.  
  579.     hwnd = CreateDialog( ghinst, MAKEINTRESOURCE(IDD_CONNECT_STATUS), ghWndMain, NULL);
  580.  
  581.     return hwnd;
  582.  
  583. }
  584.  
  585. /*
  586.  * UpdateTitle
  587.  *
  588.  * Updates the window title based on application status
  589.  */
  590. void UpdateTitle(void)
  591. {
  592.     DWORD dwFeatures;
  593.       TCHAR tszTitle[MAX_WINDOWTITLE];
  594.     UINT iStringID;
  595.  
  596.     // calculate title features
  597.     dwFeatures = 0;
  598.     if (gbReliable)
  599.         dwFeatures |= 1;
  600.     if (gbIsHost)
  601.         dwFeatures |= 2;
  602.  
  603.     switch (dwFeatures)
  604.     {
  605.     case 0:
  606.         iStringID = IDS_DUEL_TITLE;
  607.         break;
  608.     case 1:
  609.         iStringID = IDS_DUEL_RELIABLE_TITLE;
  610.         break;
  611.     case 2:
  612.         iStringID = IDS_DUEL_HOST_TITLE;
  613.         break;
  614.     case 3:
  615.         iStringID = IDS_DUEL_HOST_RELIABLE_TITLE;
  616.         break;
  617.     }
  618.  
  619.     // get appropriate window title for these features
  620.     LoadString(ghinst, iStringID, tszTitle, MAX_WINDOWTITLE);
  621.     // change window title
  622.     SetWindowText(ghWndMain, tszTitle);               
  623. }
  624.  
  625.  
  626.