Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
534 lines
17 KiB
C++
534 lines
17 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: Duel.cpp
|
|
//
|
|
// Desc: Multi-player game
|
|
//
|
|
// Copyright (C) 1995-2001 Microsoft Corporation. All Rights Reserved.
|
|
//-----------------------------------------------------------------------------
|
|
#include "duel.h"
|
|
#include "gameproc.h"
|
|
#include "gfx.h"
|
|
#include "DPUtil.h"
|
|
#include "diutil.h"
|
|
#include "dsutil.h"
|
|
#include "lobby.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Globals
|
|
//-----------------------------------------------------------------------------
|
|
// This GUID allows DirectPlay to find other instances of the same game on
|
|
// the network. So it must be unique for every game, and the same for
|
|
// every instance of that game. // {88789F50-0BDB-43b4-AF06-F951EC05D6CE}
|
|
GUID g_AppGUID = { 0x88789f50, 0xbdb, 0x43b4, { 0xaf, 0x6, 0xf9, 0x51, 0xec, 0x5, 0xd6, 0xce } };
|
|
|
|
extern DWORD g_dwFrameCount;
|
|
extern DWORD g_dwFrameTime;
|
|
extern int g_nProgramState;
|
|
extern SHIP g_OurShip;
|
|
extern DPID g_LocalPlayerDPID;
|
|
|
|
static BOOL g_bReinitialize; // Used for switching display modes
|
|
|
|
TCHAR g_strAppName[256] = "Duel"; // The name of the sample
|
|
HANDLE g_hDPMessageEvent = NULL; // Not used in this sample, needed for DPConnect.cpp
|
|
TCHAR g_strLocalPlayerName[MAX_PLAYER_NAME]; // Local player name
|
|
TCHAR g_strSessionName[MAX_SESSION_NAME]; // Default session name
|
|
TCHAR g_strPreferredProvider[MAX_SESSION_NAME]; // Default preferred provider
|
|
|
|
LPDPLCONNECTION g_pDPLConnection = NULL;
|
|
LPDIRECTPLAYLOBBY3 g_pDPLobby = NULL;
|
|
|
|
HWND g_hwndMain; // Main application window handle
|
|
HKEY g_hDuelKey = NULL; // Duel registry key handle
|
|
HINSTANCE g_hInst; // Application instance handle
|
|
BOOL g_bShowFrameCount=TRUE; // Show FPS ?
|
|
BOOL g_bIsActive; // Is the application active ?
|
|
BOOL g_bHostPlayer; // Are we hosting or joining a game
|
|
DWORD g_dwKeys; // User keyboard input
|
|
DWORD g_dwOldKeys; // Last frame's keyboard input
|
|
BOOL g_bFullscreen=FALSE; // Window or FullScreen mode ?
|
|
RECT g_rcWindow; // client rectangle of main window
|
|
BOOL g_bReliable; // sends are reliable
|
|
BOOL g_bAsync; // asynchronous sends
|
|
BOOL g_bAsyncSupported; // asynchronous sends supported
|
|
BOOL g_bUseProtocol; // DirectPlay Protocol messaging
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function prototypes
|
|
//-----------------------------------------------------------------------------
|
|
extern int DPConnect_StartDirectPlayConnect( HINSTANCE hInst, BOOL bBackTrack = FALSE );
|
|
extern HRESULT DPConnect_CheckForLobbyLaunch( BOOL* pbLaunchedByLobby );
|
|
|
|
LRESULT CALLBACK MainWndproc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
|
|
HRESULT InitApplication( HINSTANCE hInst );
|
|
HRESULT ReadRegKey( HKEY hKey, TCHAR* strName, TCHAR* strValue, DWORD dwLength, TCHAR* strDefault );
|
|
HRESULT WriteRegKey( HKEY hKey, TCHAR* strName, TCHAR* strValue );
|
|
VOID CleanupApplication();
|
|
BOOL WasLaunchedByLobby();
|
|
BOOL FinishLobbyLaunch();
|
|
VOID DoHelp();
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: WinMain()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
|
|
{
|
|
MSG msg;
|
|
BOOL bLaunchedByLobby;
|
|
HRESULT hr;
|
|
|
|
g_hInst = hInstance;
|
|
|
|
// Read information from registry
|
|
RegCreateKeyEx( HKEY_CURRENT_USER, DUEL_KEY, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
|
|
&g_hDuelKey, NULL );
|
|
|
|
ReadRegKey( g_hDuelKey, "Player Name",
|
|
g_strLocalPlayerName, MAX_PLAYER_NAME, "" );
|
|
ReadRegKey( g_hDuelKey, "Session Name",
|
|
g_strSessionName, MAX_SESSION_NAME, "" );
|
|
ReadRegKey( g_hDuelKey, "Preferred Provider",
|
|
g_strPreferredProvider, MAX_SESSION_NAME, "" );
|
|
|
|
CoInitialize( NULL );
|
|
|
|
if( FAILED( InitApplication( hInstance ) ) )
|
|
return 0;
|
|
|
|
// See if we were launched from a lobby server
|
|
hr = DPConnect_CheckForLobbyLaunch( &bLaunchedByLobby );
|
|
if( FAILED(hr) )
|
|
return 1;
|
|
|
|
if( bLaunchedByLobby )
|
|
{
|
|
// Start game
|
|
PostMessage( g_hwndMain, UM_LAUNCH, 0, 0 );
|
|
g_bIsActive = TRUE;
|
|
}
|
|
|
|
g_dwFrameTime = timeGetTime();
|
|
|
|
while( TRUE )
|
|
{
|
|
if( g_bIsActive )
|
|
{
|
|
// Any windows messages ? (returns immediately)
|
|
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
|
|
{
|
|
if( !GetMessage( &msg, NULL, 0, 0 ) )
|
|
break;
|
|
|
|
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( g_nProgramState != PS_SPLASH )
|
|
{
|
|
ReceiveMessages();
|
|
LobbyMessageReceive(LMR_PROPERTIES);
|
|
}
|
|
|
|
// update screen
|
|
if( !UpdateFrame() )
|
|
ExitGame(); // posts QUIT msg
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Any windows messages ? (blocks until a message arrives)
|
|
if( !GetMessage( &msg, NULL, 0, 0 ) )
|
|
break;
|
|
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
// Write information to the registry
|
|
WriteRegKey( g_hDuelKey, "Player Name", g_strLocalPlayerName );
|
|
WriteRegKey( g_hDuelKey, "Session Name", g_strSessionName );
|
|
WriteRegKey( g_hDuelKey, "Preferred Provider", g_strPreferredProvider );
|
|
|
|
return (int)msg.wParam;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: MainWndproc()
|
|
// Desc: Callback for all Windows messages
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT CALLBACK MainWndproc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
switch( msg )
|
|
{
|
|
case WM_SIZE:
|
|
case WM_MOVE:
|
|
// Get the client rectangle
|
|
if( g_bFullscreen )
|
|
{
|
|
SetRect( &g_rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN),
|
|
GetSystemMetrics(SM_CYSCREEN) );
|
|
}
|
|
else
|
|
{
|
|
GetClientRect( hWnd, &g_rcWindow );
|
|
ClientToScreen( hWnd, (POINT*)&g_rcWindow );
|
|
ClientToScreen( hWnd, (POINT*)&g_rcWindow+1 );
|
|
}
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
// Ignore this message during reinitializing graphics
|
|
if( g_bReinitialize )
|
|
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 )
|
|
{
|
|
// Aeactivated
|
|
g_bIsActive = FALSE;
|
|
if( PS_ACTIVE == g_nProgramState )
|
|
SetTimer( hWnd, RECEIVE_TIMER_ID, RECEIVE_TIMEOUT, NULL );
|
|
}
|
|
else
|
|
{
|
|
// Activated
|
|
g_bIsActive = TRUE;
|
|
if( PS_ACTIVE == g_nProgramState )
|
|
KillTimer( hWnd, RECEIVE_TIMER_ID );
|
|
}
|
|
|
|
// set game palette, if activated in game mode
|
|
if( g_bIsActive && (g_nProgramState != PS_SPLASH) )
|
|
SetGamePalette();
|
|
|
|
DIUtil_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( g_nProgramState == PS_ACTIVE )
|
|
{
|
|
g_bReinitialize = TRUE;
|
|
ReleaseLocalData(); //only sound buffers have to be rels'd anyway.
|
|
CleanupGameSounds();
|
|
DIUtil_CleanupInput();
|
|
CleanupGraphics();
|
|
DestroyWindow( g_hwndMain );
|
|
g_bFullscreen = !g_bFullscreen;
|
|
InitGraphics();
|
|
DIUtil_InitInput( g_hwndMain );
|
|
InitializeGameSounds();
|
|
InitLocalSoundData();
|
|
g_bReinitialize = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
switch( wParam )
|
|
{
|
|
case 'a':
|
|
case 'A':
|
|
// Toggle Async sends on/off
|
|
if( g_bAsyncSupported )
|
|
{
|
|
g_bAsync = !g_bAsync;
|
|
UpdateTitle(); // caption bar status
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
// Toggle reliable sends
|
|
g_bReliable = !g_bReliable;
|
|
UpdateTitle();
|
|
break;
|
|
|
|
case VK_F1:
|
|
// Display help
|
|
DoHelp();
|
|
break;
|
|
|
|
case VK_F5:
|
|
// Toggle frame rate display
|
|
g_bShowFrameCount = !g_bShowFrameCount;
|
|
if( g_bShowFrameCount )
|
|
{
|
|
g_dwFrameCount = 0;
|
|
g_dwFrameTime = timeGetTime();
|
|
}
|
|
break;
|
|
|
|
case VK_RETURN:
|
|
// Launch game setup wizard
|
|
if( (g_nProgramState == PS_SPLASH) && !g_bFullscreen )
|
|
{
|
|
int nExitCode;
|
|
nExitCode = DPConnect_StartDirectPlayConnect( g_hInst, FALSE );
|
|
|
|
// Figure out what happened, and post a reflecting message
|
|
if( nExitCode == EXITCODE_FORWARD )
|
|
PostMessage(g_hwndMain, UM_LAUNCH, 0, 0);
|
|
|
|
if( nExitCode == EXITCODE_QUIT )
|
|
PostMessage(g_hwndMain, UM_ABORT, 0, 0);
|
|
|
|
if( nExitCode == EXITCODE_LOBBYCONNECT )
|
|
PostMessage( g_hwndMain, UM_LAUNCH, 0, 0 );
|
|
|
|
if( nExitCode == EXITCODE_ERROR )
|
|
{
|
|
MessageBox( g_hwndMain, TEXT("Mutliplayer connect failed. "
|
|
"The sample will now quit."),
|
|
TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
|
|
PostMessage(g_hwndMain, UM_ABORT, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_ESCAPE:
|
|
case VK_F12:
|
|
// Exit the game
|
|
ExitGame();
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint( hWnd, &ps );
|
|
if( g_nProgramState == PS_SPLASH )
|
|
{
|
|
// Display the splash screen
|
|
BltSplashScreen( NULL );
|
|
}
|
|
|
|
EndPaint( hWnd, &ps );
|
|
return 1;
|
|
|
|
case UM_LAUNCH:
|
|
case UM_ABORT:
|
|
// if we were launched by the lobby and not (failed to finish a lobby launch)
|
|
// where wParam is bLobbyLaunched
|
|
if( msg == UM_LAUNCH )
|
|
{
|
|
// Init lobby msg support for reporting score
|
|
// Note that we don't release the lobby object
|
|
LobbyMessageInit();
|
|
|
|
// Start the game in rest mode
|
|
g_nProgramState = PS_REST;
|
|
LaunchGame();
|
|
return 1;
|
|
}
|
|
// Else aborting
|
|
ExitGame();
|
|
return 1;
|
|
|
|
case WM_TIMER:
|
|
ReceiveMessages();
|
|
LobbyMessageReceive( LMR_PROPERTIES );
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
// If g_bReinitialize is TRUE don't quit, we are just switching
|
|
// display modes
|
|
if( !g_bReinitialize )
|
|
{
|
|
CleanupApplication();
|
|
PostQuitMessage( 0 );
|
|
}
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc( hWnd, msg, wParam, lParam );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: InitApplication()
|
|
// Desc: Do that initialization stuff...
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT InitApplication( HINSTANCE hInst )
|
|
{
|
|
WNDCLASS wndClass = { CS_DBLCLKS, MainWndproc, 0, 0, hInst,
|
|
LoadIcon( hInst, MAKEINTRESOURCE(IDI_MAIN)),
|
|
LoadCursor(NULL, IDC_ARROW),
|
|
(HBRUSH)GetStockObject(BLACK_BRUSH),
|
|
NULL, TEXT("DuelClass") };
|
|
RegisterClass( &wndClass );
|
|
|
|
// Initialize all components
|
|
if( FAILED( InitGraphics() ) )
|
|
return E_FAIL;
|
|
|
|
if( FAILED( DIUtil_InitInput( g_hwndMain ) ) )
|
|
return E_FAIL;
|
|
|
|
if( FAILED( InitializeGameSounds() ) )
|
|
{
|
|
// Can play game without sound. Do not exit
|
|
}
|
|
|
|
// Start in splash mode
|
|
g_nProgramState = PS_SPLASH;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CleanupApplication()
|
|
// Desc: Calls clean up on all components
|
|
//-----------------------------------------------------------------------------
|
|
VOID CleanupApplication()
|
|
{
|
|
CleanupComm();
|
|
CleanupGameSounds();
|
|
CleanupGraphics();
|
|
DIUtil_CleanupInput();
|
|
DPLobbyRelease(); // in case we were doing lobby messages
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ShowError()
|
|
// Desc: Displays error to the user
|
|
//-----------------------------------------------------------------------------
|
|
VOID ShowError( int iStrID )
|
|
{
|
|
TCHAR strMsg[MAX_ERRORMSG];
|
|
LoadString( g_hInst, iStrID, strMsg, MAX_ERRORMSG );
|
|
MessageBox( g_hwndMain, strMsg, TEXT("Duel Message"), MB_OK );
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: UpdateTitle()
|
|
// Desc: Updates the window title based on application status
|
|
//-----------------------------------------------------------------------------
|
|
VOID UpdateTitle()
|
|
{
|
|
// Build the window title
|
|
TCHAR strTitle[MAX_WINDOWTITLE] = TEXT("Duel");
|
|
|
|
// State options in window title
|
|
if( g_bHostPlayer | g_bUseProtocol | g_bReliable | g_bAsync )
|
|
{
|
|
strcat( strTitle, " - |" );
|
|
if( g_bHostPlayer )
|
|
_tcscat( strTitle, TEXT(" Host |") );
|
|
if( g_bUseProtocol )
|
|
_tcscat( strTitle, TEXT(" Protocol |") );
|
|
if( g_bReliable )
|
|
_tcscat( strTitle, TEXT(" Reliable |") );
|
|
if( g_bAsync )
|
|
_tcscat( strTitle, TEXT(" Async |") );
|
|
}
|
|
|
|
// Change window title
|
|
SetWindowText( g_hwndMain, strTitle );
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DoHelp()
|
|
// Desc: Display a Help summary in a message box.
|
|
//-----------------------------------------------------------------------------
|
|
VOID DoHelp()
|
|
{
|
|
TCHAR strHelpMsg[MAX_HELPMSG];
|
|
LoadString( g_hInst, IDS_DUEL_HELP, strHelpMsg, MAX_HELPMSG );
|
|
MessageBox( g_hwndMain, strHelpMsg, TEXT("DUEL"), MB_OK );
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ReadRegKey()
|
|
// Desc: Read a registry key
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT ReadRegKey( HKEY hKey, TCHAR* strName, TCHAR* strValue,
|
|
DWORD dwLength, TCHAR* strDefault )
|
|
{
|
|
DWORD dwType;
|
|
LONG bResult;
|
|
|
|
bResult = RegQueryValueEx( hKey, strName, 0, &dwType,
|
|
(LPBYTE) strValue, &dwLength );
|
|
if ( bResult != ERROR_SUCCESS )
|
|
strcpy( strValue, strDefault );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: WriteRegKey()
|
|
// Desc: Writes a registry key
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT WriteRegKey( HKEY hKey, TCHAR* strName, TCHAR* strValue )
|
|
{
|
|
LONG bResult;
|
|
|
|
bResult = RegSetValueEx( hKey, strName, 0, REG_SZ,
|
|
(LPBYTE) strValue, strlen(strValue) + 1 );
|
|
if ( bResult != ERROR_SUCCESS )
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|