DUEL.C

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: duel.c
* Content: Multi-player duel
*
*
***************************************************************************/
#define INITGUID
#include "duel.h"
#include "gameproc.h"
#include "gfx.h"
#include "comm.h"
#include "input.h"
#include "lobby.h"
#include "wizard.h"
#include "util.h"
#include "sfx.h"

// {33925241-05F8-11d0-8063-00A0C90AE891}
DEFINE_GUID(DUEL_GUID,
0x33925241, 0x5f8, 0x11d0, 0x80, 0x63, 0x0, 0xa0, 0xc9, 0xa, 0xe8, 0x91);

/*
* Externals
*/
extern DWORDgdwFrameCount;
extern DWORDgdwFrameTime;
extern intgnProgramState;
extern SHIPgOurShip;
extern LPDPLCONNECTIONglpdplConnection;
extern DPIDgOurID;
extern BOOL gbNoField;


/*
* Globals
*/
LPGUID glpGuid;// Duel's GUID
HWND ghWndMain;// Main application window handle
HINSTANCEghinst;// Application instance handle
BOOL gbShowFrameCount=TRUE;// Show FPS ?
BOOL gbIsActive;// Is the application active ?
BOOL gbUseEmulation;// DDHEL or DDHAL for Graphics
BOOLgbIsHost;// Are we hosting or joining a game
DWORD gdwKeys;// User keyboard input
DWORD gdwOldKeys; // Last frame's keyboard input
BOOLgbFullscreen=FALSE;// Window or FullScreen mode ?
RECTgrcWindow;// client rectangle of main window
HANDLEghThread;// handle to wizard thread
TCHARgtszClassName[MAX_CLASSNAME]; // Duel's class name
BOOL gbReliable; // sends are reliable

/*
* Statics
*/
static BOOLgbReinitialize;// used for switching display modes

/*
* WinMain
*/
int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow )
{
BOOL bHelp=FALSE;
MSG msg;

ghinst = hinstance;

CoInitialize( NULL );

// Parse command line
while( lpCmdLine[0] == '-' )
{
lpCmdLine++;

switch (*lpCmdLine++)
{
case 'e':
gbUseEmulation = TRUE;
break;
case 'd':
gbNoField = TRUE;
break;
case '?':
default:
bHelp= TRUE;
gbFullscreen= FALSE; // give help in windowed mode
break;
}

while( isspace(*lpCmdLine) )
{
lpCmdLine++;
}
}

/*
* Give user help if asked for
*/

if( bHelp )
{
TCHAR tszHelpMsg[MAX_HELPMSG];
TCHAR tszTitle[MAX_WINDOWTITLE];

LoadString(ghinst, IDS_DUEL_HELP, tszHelpMsg, MAX_HELPMSG);
LoadString(ghinst, IDS_DUEL_TITLE, tszTitle, MAX_WINDOWTITLE);
MessageBox(ghWndMain, tszHelpMsg, tszTitle, MB_OK );
return TRUE;
}


if( !InitApplication(hinstance) )
{
return FALSE;
}

// were we launched by a lobby ?
if (LaunchedByLobby())
{
// start game
PostMessage(ghWndMain, UM_LAUNCH, 0, 0);
gbIsActive = TRUE;
}

gdwFrameTime = timeGetTime();

while( TRUE )
{
if (gbIsActive)
{
// any windows messages ? (returns immediately)
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( !GetMessage( &msg, NULL, 0, 0 ) )
{
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Poll our receive queue. Polling is used in the sample only for simplicity.
// Receiving messages using an event is the recommended way.
if (gnProgramState != PS_SPLASH)
{
ReceiveMessages();
}

// update screen
if (!UpdateFrame())
{
ExitGame();
}
}
}
else
{
// any windows messages ? (blocks until a message arrives)
if( !GetMessage( &msg, NULL, 0, 0 ) )
{
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

CoUninitialize();

} /* WinMain */


/*
* MainWndproc
*
* Callback for all Windows messages
*/
long WINAPI MainWndproc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;
DWORD dwRetCode;
DWORD dwTid;

switch( message )
{
case WM_SIZE:
case WM_MOVE:
// get the client rectangle
if (gbFullscreen)
{
SetRect(&grcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
}
else
{
GetClientRect(hWnd, &grcWindow);
ClientToScreen(hWnd, (LPPOINT)&grcWindow);
ClientToScreen(hWnd, (LPPOINT)&grcWindow+1);
}
break;

case WM_ACTIVATE:
// ignore this message during reinitializing graphics
if (gbReinitialize) return 0;

// When we are deactivated, although we don't update our screen, we still need to
// to empty our receive queue periodically as messages will pile up otherwise.
// Polling the receive queue continuously even when we are deactivated causes our app
// to consume all the CPU time. To avoid hogging the CPU, we block on GetMessage() WIN API
// and setup a timer to wake ourselves up at regular intervals to process our messages.

if (LOWORD(wParam) == WA_INACTIVE)
{
// deactivated
gbIsActive = FALSE;
if (PS_ACTIVE == gnProgramState)
{
SetTimer(hWnd, RECEIVE_TIMER_ID, RECEIVE_TIMEOUT, NULL);
}
}
else
{
// activated
gbIsActive = TRUE;
if (PS_ACTIVE == gnProgramState)
{
KillTimer(hWnd, RECEIVE_TIMER_ID);
}
}

// set game palette, if activated in game mode
if (gbIsActive && (gnProgramState != PS_SPLASH))
SetGamePalette();

ReacquireInputDevices();

return 0;

case WM_CREATE:
break;

case WM_SYSKEYUP:
switch( wParam )
{
// handle ALT+ENTER (fullscreen/window mode)
case VK_RETURN:
// mode switch is allowed only during the game
if (gnProgramState == PS_ACTIVE)
{
gbReinitialize = TRUE;
ReleaseLocalData(); //only sound buffers have to be rels'd anyway.
CleanupSfx();
CleanupInput();
CleanupGraphics();
DestroyWindow(ghWndMain);
gbFullscreen = !gbFullscreen;
InitGraphics();
InitInput();
InitSfx();
InitLocalSoundData();
gbReinitialize = FALSE;
}
break;
}
break;

case WM_KEYDOWN:
switch( wParam )
{
case 'r':
case 'R':
// toggle reliable status
gbReliable = !gbReliable;
UpdateTitle();
break;

case VK_F1:
{
TCHAR tszHelpMsg[MAX_HELPMSG];
TCHAR tszTitle[MAX_WINDOWTITLE];

LoadString(ghinst, IDS_DUEL_HELP, tszHelpMsg, MAX_HELPMSG);
LoadString(ghinst, IDS_DUEL_TITLE, tszTitle, MAX_WINDOWTITLE);
MessageBox(ghWndMain, tszHelpMsg, tszTitle, MB_OK );
}
break;

case VK_F5:
gbShowFrameCount = !gbShowFrameCount;
if( gbShowFrameCount )
{
gdwFrameCount = 0;
gdwFrameTime = timeGetTime();
}
break;

case VK_RETURN:
if( (gnProgramState == PS_SPLASH) && !gbFullscreen)
{
// get connection settings from user
ghThread = CreateThread(NULL, 0, (LPVOID)DoWizard, 0, 0, &dwTid);
}
break;

case VK_ESCAPE:
case VK_F12:
// adios
ExitGame();
return 0;
}
break;

case WM_ERASEBKGND:
return 1;

case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
if (gnProgramState == PS_SPLASH)
{
// display the splash screen
bltSplash(NULL);
}

EndPaint( hWnd, &ps );
return 1;

case UM_LAUNCH:
// cleanup the wizard thread
if (ghThread)
{
// wait for thread to exit
while (!GetExitCodeThread(ghThread, &dwRetCode));
CloseHandle(ghThread);
}

// start the game in rest mode
gnProgramState = PS_REST;
LaunchGame();
return 1;

case UM_ABORT:
// cleanup the wizard thread
if (ghThread)
{
// wait for thread to exit
while (!GetExitCodeThread(ghThread, &dwRetCode));
CloseHandle(ghThread);
}
ExitGame();
return 1;

case WM_TIMER:
ReceiveMessages();
break;

case WM_DESTROY:
// if gbReinitialize is TRUE don't quit, we are just switching display modes
if (!gbReinitialize)
{
CleanupApplication();
PostQuitMessage( 0 );
}
return 0;

default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);

} /* MainWndproc */

/*
* InitApplication
*
* Do that initialization stuff...
*/
BOOL InitApplication( HINSTANCE hinst )
{
WNDCLASS wc;
BOOL rc;

glpGuid = (LPGUID) &DUEL_GUID;

LoadString(ghinst, IDS_DUEL_CLASSNAME, gtszClassName, MAX_CLASSNAME);

wc.style = CS_DBLCLKS;
wc.lpfnWndProc = MainWndproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinst;
wc.hIcon = LoadIcon( hinst, MAKEINTRESOURCE(DUEL_ICON));
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = GetStockObject( BLACK_BRUSH );
wc.lpszMenuName = NULL;
wc.lpszClassName = gtszClassName;
rc = RegisterClass( &wc );
if( !rc )
{
return FALSE;
}

// Initialize all components
if ((!InitGraphics()) || (!InitInput()) || (!InitSfx()))
{
return FALSE;
}

// start in splash mode
gnProgramState = PS_SPLASH;

return TRUE;

} /* initApplication */

/*
* CleanupApplication
*
* Calls clean up on all components
*/
void CleanupApplication( void )
{
CleanupComm();
CleanupSfx();
CleanupGraphics();
CleanupInput();
}

/*
* LaunchedByLobby
*
* Determines if we were launched by a lobby. If so, gets the connection settings
* and creates our player using the information from the lobby
*/
BOOL LaunchedByLobby(void)
{
HRESULT hr;
HWND hwndStatus;

// create a lobby object
hr = DPLobbyCreate();
if (FAILED(hr))
{
ShowError(IDS_DPLOBBY_ERROR_C);
return FALSE;
}

// get connection settings from the lobby (into glpdplConnection)
hr = DPLobbyGetConnectionSettings();
if (FAILED(hr))
{
if (DPERR_NOTLOBBIED == hr)
{
// we were not lobbied - start up game normally
hr = DPLobbyRelease();
if (FAILED(hr))
{
ShowError(IDS_DPLOBBY_ERROR_R);
goto FAIL;
}
// move on
return FALSE;
}
else
{
ShowError(IDS_DPLOBBY_ERROR_GCS);
goto FAIL;
}
}

// are we hosting or joining ?
if (glpdplConnection->dwFlags & DPLCONNECTION_CREATESESSION)
{
gbIsHost = TRUE;
}

// set our session flags
glpdplConnection->lpSessionDesc->dwFlags = DPSESSION_MIGRATEHOST |
DPSESSION_KEEPALIVE;

// let lobby know our connection flags
hr = DPLobbySetConnectionSettings();
if (FAILED(hr))
{
ShowError(IDS_DPLOBBY_ERROR_SCS);
goto FAIL;
}

if ( !gbIsHost )
{
// show splash screen and
// connection status if we are joining a game
UpdateWindow(ghWndMain);
hwndStatus = ShowConnectStatus();
}
else
{
// we are hosting, don't need connection status
hwndStatus = NULL;
}

// connect to the lobby
hr = DPLobbyConnect();

if ( hwndStatus )
{
// get rid of the connectino status window
DestroyWindow(hwndStatus);
}

if (FAILED(hr))
{
ShowError(IDS_DPLOBBY_ERROR_CONNECT);
goto FAIL;
}

// create our player
hr = DPlayCreatePlayer(
&gOurID,
#ifdef UNICODE
glpdplConnection->lpPlayerName->lpszShortName,
#else
glpdplConnection->lpPlayerName->lpszShortNameA,
#endif
NULL,
NULL,
0
);

if (FAILED(hr))
{
ShowError(IDS_DPLAY_ERROR_CP);
goto FAIL;
}


// cleanup
hr = DPLobbyRelease();
if (FAILED(hr))
{
ShowError(IDS_DPLOBBY_ERROR_R);
goto FAIL;
}

// we were lobbied
return TRUE;

FAIL:
// cleanup and exit
DPLobbyRelease();
ExitGame();
return FALSE;
}

/*
* Displays error to the user
*/
BOOL ShowError( int iStrID )
{
TCHAR tszMsg[MAX_ERRORMSG];
TCHAR tszTitle[MAX_WINDOWTITLE];

LoadString(ghinst, iStrID, tszMsg, MAX_ERRORMSG);
LoadString(ghinst, IDS_DUEL_ERROR_TITLE, tszTitle, MAX_WINDOWTITLE);
MessageBox( ghWndMain, tszMsg, tszTitle, MB_OK );
return FALSE;
}

/*
* Displays connection status to the user
*/
HWND ShowConnectStatus(void)
{
HWND hwnd;

hwnd = CreateDialog( ghinst, MAKEINTRESOURCE(IDD_CONNECT_STATUS), ghWndMain, NULL);

return hwnd;

}

/*
* UpdateTitle
*
* Updates the window title based on application status
*/
void UpdateTitle(void)
{
DWORD dwFeatures;
TCHAR tszTitle[MAX_WINDOWTITLE];
UINT iStringID;

// calculate title features
dwFeatures = 0;
if (gbReliable)
dwFeatures |= 1;
if (gbIsHost)
dwFeatures |= 2;

switch (dwFeatures)
{
case 0:
iStringID = IDS_DUEL_TITLE;
break;
case 1:
iStringID = IDS_DUEL_RELIABLE_TITLE;
break;
case 2:
iStringID = IDS_DUEL_HOST_TITLE;
break;
case 3:
iStringID = IDS_DUEL_HOST_RELIABLE_TITLE;
break;
}

// get appropriate window title for these features
LoadString(ghinst, iStringID, tszTitle, MAX_WINDOWTITLE);
// change window title
SetWindowText(ghWndMain, tszTitle);
}