//----------------------------------------------------------------------------- // File: GameProc.cpp // // Desc: Game processing routines // // Copyright (C) 1995-2001 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include #include "duel.h" #include "gameproc.h" #include "gfx.h" #include "DPUtil.h" #include "diutil.h" #include "dsutil.h" #include "dputil.h" #include "lobby.h" #include "dsound.h" #include "d3dtypes.h" #include "stdio.h" //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- extern DWORD g_dwKeys; extern LPDIRECTDRAWSURFACE g_pddsShip[NUM_SHIP_TYPES]; extern BOOL g_bShowFrameCount; extern LPDIRECTDRAWSURFACE g_pddsNumbers; extern BOOL g_bHostPlayer; extern BOOL g_bIsActive; extern DPSESSIONDESC2* g_pdpsd; extern HWND g_hwndMain; extern HINSTANCE g_hInst; extern BOOL g_bReliable; extern BOOL g_bAsync; extern BOOL g_bUseProtocol; extern LPDIRECTPLAYVOICECLIENT g_pVoiceClient; extern LPDIRECTSOUND3DLISTENER g_pDSListener; FRAG g_Frags[64]; // ship framents BLOCKS g_Blocks; // field layout VOID* g_pvReceiveBuffer = NULL; // buffer to store received messages DWORD g_dwReceiveBufferSize = 0; // size of buffer DPID g_LocalPlayerDPID; // our player id BOOL g_bHaveHostInit; // do we need to do host initializaton int g_nProgramState; // current state of the game DWORD g_dwFrameCount; // used for fps calc DWORD g_dwFramesLast; // .. DWORD g_dwFrameTime; // .. BOOL g_bUpdate; // TRUE if player data needs to be updated BOOL g_bSessionLost; // did we get disconnected ? HOSTMSG g_HostMsg; // message buffer BLOCKHITMSG g_BlockHitMsg; // .. SHIPHITMSG g_ShipHitMsg; // .. ADDBLOCKMSG g_AddBlockMsg; // .. CONTROLMSG g_ControlMsg; // .. SYNCMSG g_SyncMsg; // .. SoundObject* g_pBulletFiringSound = NULL; SoundObject* g_pShipExplodeSound = NULL; SoundObject* g_pShipEngineSound = NULL; SoundObject* g_pShipStartSound = NULL; SoundObject* g_pShipStopSound = NULL; SoundObject* g_pShipBounceSound = NULL; SoundObject* g_pBlockExplodeSound = NULL; // Used to determine new joiner's ship type static int g_ShipTypesUsed[NUM_SHIP_TYPES]; //----------------------------------------------------------------------------- // Name: InitMessageBuffers() // Desc: Sets up buffes used for sending different types of messages //----------------------------------------------------------------------------- VOID InitMessageBuffers() { g_HostMsg.byType = MSG_HOST; g_BlockHitMsg.byType = MSG_BLOCKHIT; g_ShipHitMsg.byType = MSG_SHIPHIT; g_AddBlockMsg.byType = MSG_ADDBLOCK; g_ControlMsg.byType = MSG_CONTROL; g_SyncMsg.byType = MSG_SYNC; } //----------------------------------------------------------------------------- // Name: LaunchGame() // Desc: Sets up the game layout and launches //----------------------------------------------------------------------------- VOID LaunchGame() { HRESULT hr; // initialize message buffers and other module globals InitMessageBuffers(); g_bSessionLost = FALSE; // get current session description (g_pdpsd is initialized in here) hr = DPUtil_GetSessionDesc(); if (FAILED(hr) || (!g_pdpsd)) { ShowError(IDS_DPLAY_ERROR_GSD); ExitGame(); return; } // Is this session using the DirectPlay Protocol? g_bUseProtocol = (g_pdpsd->dwFlags & DPSESSION_DIRECTPLAYPROTOCOL) != 0; // update window title UpdateTitle(); // initialize random number seed srand((int)GetTickCount()); // clear front buffer before changing palette EraseScreen(); FlipScreen(); // Begin collecting input DIUtil_ReacquireInputDevices(); // init the ship type tracking ZeroMemory( g_ShipTypesUsed, sizeof(g_ShipTypesUsed) ); // initialize us hr = InitOurShip(); if( FAILED(hr) ) { ExitGame(); return; } // get the field layout if( g_bHostPlayer ) { // initialize field (done only by host) InitField(); // we have host initialization g_bHaveHostInit = TRUE; // start updating screen g_bIsActive = TRUE; } else { // get it from host, if we are joining g_bHaveHostInit = FALSE; } return; } //----------------------------------------------------------------------------- // Name: ExitGame() // Desc: Game termination code //----------------------------------------------------------------------------- VOID ExitGame() { // shut down app PostMessage( g_hwndMain, WM_CLOSE, 0, 0 ); } //----------------------------------------------------------------------------- // Name: InitOurShip() // Desc: Initializes our ship's initial attributes //----------------------------------------------------------------------------- HRESULT InitPlayerLocalSoundData( SHIP* pShip ) { HRESULT hr; if( g_pVoiceClient ) { if( FAILED( hr = g_pVoiceClient->Create3DSoundBuffer( pShip->dpidID, NULL, 0, 0, &pShip->pDSBVoice ) ) ) return hr; } return S_OK; } //----------------------------------------------------------------------------- // Name: InitOurShip() // Desc: Initializes our ship's initial attributes //----------------------------------------------------------------------------- HRESULT InitOurShip() { SHIP ship; HRESULT hr; // Wipe out everything ZeroMemory( &ship, sizeof(ship) ); // Calculate ship type based on the number of players in the game // we cycle through four ships (Y,R,G,B) for now. // This old algorithm's computation will be overridden by HOST_MSG ship.byType = (BYTE) ((g_pdpsd->dwCurrentPlayers-1) % NUM_SHIP_TYPES); g_ShipTypesUsed[ship.byType] = 1; // our color ship.dPosX = randInt( 0, MAX_SHIP_X ); ship.dPosY = randInt( 0, MAX_SHIP_Y ); ship.cFrame = (char) randInt( 0, MAX_SHIP_FRAME ); ship.bEnable = TRUE; ship.dpidID = g_LocalPlayerDPID; ship.pDSBVoice = NULL; if( FAILED( hr = DPUtil_TestAudioSetup( g_hwndMain ) ) ) return hr; if( g_bHostPlayer ) { if( FAILED( hr = DPUtil_VoiceStart() ) ) return hr; } if( FAILED( hr = DPUtil_VoiceConnect() ) ) return hr; // Set our local data hr = DPUtil_SetPlayerLocalData( g_LocalPlayerDPID, &ship, sizeof(ship) ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_SPLD); return hr; } // No ship fragments for( int i=0; i<64; i++ ) g_Frags[i].valid = FALSE; return S_OK; } //----------------------------------------------------------------------------- // Name: SetPlayerLocalSoundDataCB() // Desc: Initializes and registers a player's local SOUND data ONLY. //----------------------------------------------------------------------------- BOOL WINAPI SetPlayerLocalSoundDataCB( DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, VOID* lpContext ) { SHIP ship; DWORD dwSize = sizeof(ship); HRESULT hr; DPUtil_GetPlayerLocalData(dpId, &ship, &dwSize); // no player data yet if (0 == dwSize) return TRUE; InitPlayerLocalSoundData( &ship ); hr = DPUtil_GetPlayerLocalData( dpId, &ship, &dwSize ); return (DP_OK == hr); }; //----------------------------------------------------------------------------- // Name: InitLocalSoundData() // Desc: Initializes and registers all players' sound data ONLY //----------------------------------------------------------------------------- HRESULT InitLocalSoundData() { return DPUtil_EnumPlayers(&(g_pdpsd->guidInstance), SetPlayerLocalSoundDataCB, NULL, 0); } //----------------------------------------------------------------------------- // Name: ReleaseLocalSoundData() // Desc: Releases a single ship's local sound buffers. //----------------------------------------------------------------------------- HRESULT ReleasePlayerLocalSoundData( SHIP* pShip ) { HRESULT hr; if( pShip && g_pVoiceClient && pShip->pDSBVoice ) { if( FAILED( hr = g_pVoiceClient->Delete3DSoundBuffer( pShip->dpidID, &pShip->pDSBVoice ) ) ) return hr; } return S_OK; } //----------------------------------------------------------------------------- // Name: ReleasePlayerLocalData() // Desc: Retrieves and releases a player's local data from dplay. //----------------------------------------------------------------------------- BOOL WINAPI ReleasePlayerLocalDataCB( DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, VOID* lpContext) { SHIP ship; DWORD dwSize = sizeof(SHIP); HRESULT hr; hr = DPUtil_GetPlayerLocalData( dpId, &ship, &dwSize ); if( FAILED(hr) ) goto FAIL; // no player data yet if (0 == dwSize) return TRUE; ReleasePlayerLocalSoundData(&ship); hr = DPUtil_SetPlayerLocalData(dpId, &ship, dwSize ); if (FAILED(hr)) goto FAIL; // success return TRUE; FAIL: // we failed return FALSE; } //----------------------------------------------------------------------------- // Name: ReleaseLocalData() // Desc: Releases local data of ALL players. //----------------------------------------------------------------------------- VOID ReleaseLocalData() { if( g_nProgramState == PS_ACTIVE ) { if( FAILED( DPUtil_EnumPlayers( &g_pdpsd->guidInstance, ReleasePlayerLocalDataCB, NULL, 0 ) ) ) { ShowError(IDS_DPLAY_ERROR_EP); } } }; //----------------------------------------------------------------------------- // Name: ProcessUserInput() // Desc: Processes any input from the user and updates our ship state //----------------------------------------------------------------------------- VOID ProcessUserInput( SHIP* pShip ) { static dwOldKeys = 0; g_bUpdate = FALSE; // DSOUND: check if the engine was turned off if( !(g_dwKeys & (KEY_DOWN | KEY_UP)) && (dwOldKeys & (KEY_DOWN | KEY_UP)) ) { g_dwKeys |= KEY_ENGINEOFF; g_ControlMsg.byState = (BYTE)g_dwKeys; // let everyone know that we just turned our engine off SendGameMessage( (GENERICMSG*)&g_ControlMsg, DPID_ALLPLAYERS ); } // update our ship state UpdateState( pShip, g_dwKeys ); // remember current keys for next frame dwOldKeys = g_dwKeys; } //----------------------------------------------------------------------------- // Name: UpdateState() // Desc: Updates the current state of our ship, given user input //----------------------------------------------------------------------------- VOID UpdateState( SHIP* pShip, DWORD dwControls ) { static DWORD dwTimePrevUpdate = 0; DWORD dwTimeNow = timeGetTime(); pShip->dwKeys = dwControls; // Ship orientation FLOAT fAngle = (FLOAT)( ( pShip->cFrame / 40.0f ) * 2 * 3.1415926283 ); // Don't accept these keyboard inputs faster than 50 // times per second (every 20 ms) if( dwTimeNow - dwTimePrevUpdate > 20 ) { if( dwControls & KEY_LEFT ) { g_bUpdate = TRUE; pShip->cFrame--; if( pShip->cFrame < 0 ) pShip->cFrame += MAX_SHIP_FRAME; } if( dwControls & KEY_RIGHT ) { g_bUpdate = TRUE; pShip->cFrame++; if( pShip->cFrame >= MAX_SHIP_FRAME ) pShip->cFrame -= MAX_SHIP_FRAME; } if( dwControls & KEY_UP ) { g_bUpdate = TRUE; pShip->dVelX += sin(fAngle) * 10.0 / 1000.0; pShip->dVelY += -cos(fAngle) * 10.0 / 1000.0; } if( dwControls & KEY_DOWN ) { g_bUpdate = TRUE; pShip->dVelX -= sin(fAngle) * 10.0 / 1000.0; pShip->dVelY -= -cos(fAngle) * 10.0 / 1000.0; } dwTimePrevUpdate = dwTimeNow; } if( dwControls & KEY_STOP ) { g_bUpdate = TRUE; pShip->dVelX = 0.0; pShip->dVelY = 0.0; } if( !pShip->bBulletEnable && pShip->bEnable ) { if( dwControls & KEY_FIRE ) { g_bUpdate = TRUE; // launch a new bullet pShip->dBulletPosX = (WORD)( sin(fAngle)*6.0 + 16.0 + pShip->dPosX ); pShip->dBulletPosY = (WORD)(-cos(fAngle)*6.0 + 16.0 + pShip->dPosY ); pShip->dBulletVelX = sin(fAngle)*500.0/1000.0; pShip->dBulletVelY = -cos(fAngle)*500.0/1000.0; pShip->bBulletEnable = TRUE; pShip->dwBulletFrame = 0; } } } //----------------------------------------------------------------------------- // Name: SendSync() // Desc: Sends a sync message with the rendevous position. We are using a // synchronization technique in which every player informs everyone else // where the player is going to be at the end of the next sync interval. // Based on this rendezvous position, everyone tries to move their // corresponding ghosts to the rendezvous position by the end of the // interval. //----------------------------------------------------------------------------- VOID SendSync( SHIP* pShip ) { g_SyncMsg.byShipType = pShip->byType; g_SyncMsg.cFrame = pShip->cFrame; g_SyncMsg.dPosX = pShip->dPosX + pShip->dVelX*1000; g_SyncMsg.dPosY = pShip->dPosY + pShip->dVelY*1000; SendGameMessage( (GENERICMSG*)&g_SyncMsg, DPID_ALLPLAYERS ); } //----------------------------------------------------------------------------- // Name: UpdateDisplayStatus() // Desc: Updates the disable timeout. Enables the ship if disable timeout has // elapsed. //----------------------------------------------------------------------------- VOID UpdateDisplayStatus( SHIP* pShip ) { DWORD dwTickDiff; DWORD dwTickCount; // current time dwTickCount = timeGetTime(); // time elapsed since last update dwTickDiff = dwTickCount - pShip->dwLastTick; // timestamp pShip->dwLastTick = dwTickCount; // update time-out pShip->iCountDown -= dwTickDiff; // time-out ? if( pShip->iCountDown < 0 ) { // get new position and enable our lpShip pShip->dPosX = randInt( 0, MAX_SHIP_X ); pShip->dPosY = randInt( 0, MAX_SHIP_Y ); pShip->cFrame = (char) randInt( 0, MAX_SHIP_FRAME ); pShip->bEnable = TRUE; } } //----------------------------------------------------------------------------- // Name: UpdatePosition() // Desc: Updates our ship's position //----------------------------------------------------------------------------- VOID UpdatePosition( DPID dpId, SHIP* lpShip ) { int x,y; BYTE oldxCell, oldyCell, newxCell, newyCell, mask, col, row; double thisTick, totalTick, xtick, ytick; DWORD dwTickCount; DWORD dwTickDiff; if( !lpShip->bEnable ) return; // how long has it been since we last updated dwTickCount = timeGetTime(); dwTickDiff = dwTickCount - lpShip->dwLastTick; // current timestamp lpShip->dwLastTick = dwTickCount; oldxCell = (int)(lpShip->dPosX+16.0) >> 4; oldyCell = (int)(lpShip->dPosY+16.0) >> 4; // compute new position lpShip->dPosX += lpShip->dVelX * dwTickDiff; lpShip->dPosY += lpShip->dVelY * dwTickDiff; newxCell = (int)(lpShip->dPosX+16.0) >> 4; newyCell = (int)(lpShip->dPosY+16.0) >> 4; if(oldxCell != newxCell) { // we allow ghosts to pass through the blocks if( (dpId == g_LocalPlayerDPID) && IsHit( newxCell, newyCell ) ) { if( lpShip->dVelX > 0.0 ) lpShip->dPosX = (oldxCell << 4) + 15 - 16; else lpShip->dPosX = (oldxCell << 4) - 16; lpShip->dVelX = -lpShip->dVelX*0.9; newxCell = oldxCell; lpShip->bBounced = TRUE; } } if(oldyCell != newyCell) { // we allow ghosts to pass through the blocks if( (dpId == g_LocalPlayerDPID) && IsHit( newxCell, newyCell ) ) { if( lpShip->dVelY > 0.0 ) lpShip->dPosY = (oldyCell << 4) + 15 - 16; else lpShip->dPosY = (oldyCell << 4) - 16; lpShip->dVelY = -lpShip->dVelY*0.9; lpShip->bBounced = TRUE; } } if( lpShip->dPosX > MAX_SHIP_X ) { lpShip->dPosX = MAX_SHIP_X; lpShip->dVelX = -lpShip->dVelX*0.9; lpShip->bBounced = TRUE; } else if ( lpShip->dPosX < 0 ) { lpShip->dPosX =0; lpShip->dVelX = -lpShip->dVelX*0.9; lpShip->bBounced = TRUE; } if( lpShip->dPosY > MAX_SHIP_Y ) { lpShip->dPosY = MAX_SHIP_Y; lpShip->dVelY = -lpShip->dVelY*0.9; lpShip->bBounced = TRUE; } else if ( lpShip->dPosY < 0 ) { lpShip->dPosY =0; lpShip->dVelY = -lpShip->dVelY*0.9; lpShip->bBounced = TRUE; } if ((dpId == g_LocalPlayerDPID) && lpShip->bBounced) { SendSync(lpShip); } if( !lpShip->bBulletEnable ) return; // update the active bullet lpShip->dwBulletFrame += dwTickDiff; if( lpShip->dwBulletFrame >= MAX_BULLET_FRAME ) { lpShip->bFiring = FALSE; lpShip->bBulletEnable = FALSE; return; } if( lpShip->dBulletVelX != 0.0 ) xtick = 8.0/lpShip->dBulletVelX; else xtick = 999999.0; if( lpShip->dBulletVelY != 0.0 ) ytick = 8.0/lpShip->dBulletVelY; else ytick = 999999.0; if( xtick < 0.0 ) xtick = -xtick; if( ytick < 0.0 ) ytick = -ytick; if( xtick < ytick ) thisTick = xtick; else thisTick = ytick; if( thisTick > dwTickDiff ) thisTick = dwTickDiff; for( totalTick = 0.0; totalTick < dwTickDiff; ) { totalTick += thisTick; lpShip->dBulletPosX += lpShip->dBulletVelX * thisTick; lpShip->dBulletPosY += lpShip->dBulletVelY * thisTick; if( lpShip->dBulletPosX > MAX_BULLET_X ) { lpShip->dBulletPosX = MAX_BULLET_X; lpShip->dBulletVelX = -lpShip->dBulletVelX*0.9; } else if ( lpShip->dBulletPosX < 0 ) { lpShip->dBulletPosX =0; lpShip->dBulletVelX = -lpShip->dBulletVelX*0.9; } if( lpShip->dBulletPosY > MAX_BULLET_Y ) { lpShip->dBulletPosY = MAX_BULLET_Y; lpShip->dBulletVelY = -lpShip->dBulletVelY*0.9; } else if ( lpShip->dBulletPosY < 0 ) { lpShip->dBulletPosY =0; lpShip->dBulletVelY = -lpShip->dBulletVelY*0.9; } // check to see if it hit anything x = (int)(lpShip->dBulletPosX + 0.5) + 1; y = (int)(lpShip->dBulletPosY + 0.5) + 1; row = y >> 4; col = x >> 4; mask = 1 << (col & 0x7); col = col >> 3; if( g_Blocks.bits[row][col] & mask ) { // dwScored a block hit g_BlockHitMsg.byRow = row; g_BlockHitMsg.byCol = col; g_BlockHitMsg.byMask = mask; SendGameMessage( (GENERICMSG*)&g_BlockHitMsg, DPID_ALLPLAYERS); g_Blocks.bits[row][col] &= ~mask; lpShip->dwScore += 10; lpShip->bBulletEnable = FALSE; lpShip->bBlockHit = TRUE; lpShip->bFiring = FALSE; } } } //----------------------------------------------------------------------------- // Name: IsHit() // Desc: Tells if there is a block at (x,y) location //----------------------------------------------------------------------------- BOOL IsHit( int x, int y ) { int col, mask; // outside screen boundaries? if( (x < 0) || (y < 0) || (x >= 40) || (y >= 30) ) return TRUE; // look at the block bits mask = 1 << (x & 0x7); col = x >> 3; if( g_Blocks.bits[y][col] & mask ) return TRUE; else return FALSE; } //----------------------------------------------------------------------------- // Name: InitField // Desc: Initializes block positions on the field //----------------------------------------------------------------------------- VOID InitField() { int i, x, y; // clear all g_Blocks for(x=0; x<5; x++) for(y=0; y<30; y++) g_Blocks.bits[y][x] = 0; // set random gBlocks for(i=0; i<400; i++) { x = randInt(0, 40); y = randInt(0, 30); if( !setBlock(x, y) ) i--; } } //----------------------------------------------------------------------------- // Name: AddBlock() // Desc: Adds a block to the field //----------------------------------------------------------------------------- VOID AddBlock() { // Maybe add a block? if( g_bHostPlayer && g_bIsActive && ( randInt( 0, 100 ) > 98 ) ) { int x = randInt( 0, 40); int y = randInt( 0, 30); if( setBlock( x, y) ) { g_AddBlockMsg.byX = (BYTE)x; g_AddBlockMsg.byY = (BYTE)y; SendGameMessage( (GENERICMSG*)&g_AddBlockMsg, DPID_ALLPLAYERS ); } } } //----------------------------------------------------------------------------- // Name: setBlock() // Desc: Turns on a block //----------------------------------------------------------------------------- BOOL setBlock( int x, int y ) { BYTE mask, col; mask = 1 << (x & 0x7); col = x >> 3; // is Block already set? if( g_Blocks.bits[y][col] & mask ) return FALSE; // set the block and return success g_Blocks.bits[y][col] |= mask; return TRUE; } //----------------------------------------------------------------------------- // Name: AddFrag() // Desc: Turns on a fragment //----------------------------------------------------------------------------- VOID AddFrag( SHIP* pShip, int offX, int offY ) { for( int i=0; i<64; i++ ) // Find available fragment { if( !g_Frags[i].valid ) break; } if( i == 64 ) return; g_Frags[i].dPosX = offX + pShip->dPosX; g_Frags[i].dPosY = offY + pShip->dPosY; g_Frags[i].pdds = g_pddsShip[pShip->byType]; g_Frags[i].src.top = 32 * ( (int)pShip->cFrame / 10 ) + offX; g_Frags[i].src.left = 32 * ( (int)pShip->cFrame % 10 ) + offY; g_Frags[i].src.right = g_Frags[i].src.left + 8; g_Frags[i].src.bottom = g_Frags[i].src.top + 8; g_Frags[i].dVelX = ((double)offX - 12.0)/24.0; g_Frags[i].dVelY = ((double)offY - 12.0)/24.0; g_Frags[i].valid = TRUE; } //----------------------------------------------------------------------------- // Name: UpdateFragment() // Desc: Updates the position of a fragment //----------------------------------------------------------------------------- VOID UpdateFragment( int i ) { DWORD dwTickCount; static DWORD dwTickDiff; static DWORD dwLastTick; if( i == 0 ) { dwTickCount = timeGetTime(); dwTickDiff = dwTickCount - dwLastTick; dwLastTick = dwTickCount; // If the FPS is high, keep the fragments moving if( dwTickDiff < 10 ) dwTickDiff = 10; } if( !g_Frags[i].valid ) return; g_Frags[i].dPosX += (int) (g_Frags[i].dVelX * dwTickDiff); g_Frags[i].dPosY += (int) (g_Frags[i].dVelY * dwTickDiff); if( (g_Frags[i].dPosX < 0.0) || (g_Frags[i].dPosX >= 632.0) || (g_Frags[i].dPosY < 0.0) || (g_Frags[i].dPosY >= 472.0) ) { g_Frags[i].valid = FALSE; } } //----------------------------------------------------------------------------- // Name: DestroyShip() // Desc: Adds a bunch of fragments to show that the ship is destroyed //----------------------------------------------------------------------------- VOID DestroyShip( SHIP* pShip ) { // Set flag for explosion sound pShip->bDeath = TRUE; // Add ship fragments AddFrag( pShip, 0, 0 ); AddFrag( pShip, 8, 0 ); AddFrag( pShip, 16, 0 ); AddFrag( pShip, 24, 0 ); AddFrag( pShip, 0, 8 ); AddFrag( pShip, 8, 8 ); AddFrag( pShip, 16, 8 ); AddFrag( pShip, 24, 8 ); AddFrag( pShip, 0, 16 ); AddFrag( pShip, 8, 16 ); AddFrag( pShip, 16, 16 ); AddFrag( pShip, 24, 16 ); AddFrag( pShip, 0, 24 ); AddFrag( pShip, 8, 24 ); AddFrag( pShip, 16, 24 ); AddFrag( pShip, 24, 24 ); // Play explosion sound ProcessSoundFlags( pShip ); } //----------------------------------------------------------------------------- // Name: UpdateFrame() // Desc: Refreshes the screen //----------------------------------------------------------------------------- BOOL UpdateFrame() { static DWORD dwSyncLastTick = 0L; static DWORD dwUpdateLastTick = 0L; static DWORD dwLobbyMsgLastTick = 0L; DWORD dwTickCount; SHIP ship; DWORD dwSize; HRESULT hr; int i; switch( g_nProgramState ) { case PS_ACTIVE: // Use DirectInput to read game-play keys DIUtil_ReadKeys( &g_dwKeys ); // get our local data dwSize = sizeof(ship); hr = DPUtil_GetPlayerLocalData( g_LocalPlayerDPID, &ship, &dwSize ); if( FAILED(hr) ) return FALSE; if( !ship.bEnable ) { // Ppdate disable timeout and display status UpdateDisplayStatus( &ship ); } else { // Process any change in game controls ProcessUserInput( &ship ); dwTickCount = timeGetTime(); // Synchronize if it's time if( g_bIsActive && ((dwTickCount - dwSyncLastTick) > SYNC_INTERVAL) ) { SendSync( &ship ); dwSyncLastTick = dwTickCount; } // If our player changed any keys, let everyone know if( g_bUpdate ) { // Control the number of packets we send if( (dwTickCount - dwUpdateLastTick) > UPDATE_INTERVAL ) { // Let others know g_ControlMsg.byState = (BYTE)g_dwKeys; SendGameMessage( (GENERICMSG*)&g_ControlMsg, DPID_ALLPLAYERS ); dwUpdateLastTick = dwTickCount; } } // If connected to a lobby, occasionally report score as a property if( DoingLobbyMessages() && (dwTickCount - dwLobbyMsgLastTick) > 10000 ) // every 10 sec { DPLDATA_PLAYERSCORE playerScore; ZeroMemory(&playerScore, sizeof(DPLDATA_PLAYERSCORE)); playerScore.dwScoreCount = 1; playerScore.Score[0] = (LONG)ship.dwScore; LobbyMessageSetProperty( &DPLPROPERTY_PlayerScore, &playerScore, sizeof(DPLDATA_PLAYERSCORE)); dwLobbyMsgLastTick = dwTickCount; } } // Save ship data as RenderPlayerCB reads stored data hr = DPUtil_SetPlayerLocalData( g_LocalPlayerDPID, &ship, sizeof(ship) ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_SPLD); return FALSE; } // Update fragments for( i=0; i<64; i++ ) UpdateFragment(i); // Add a block AddBlock(); // Render everything if( !DrawScreen() ) { return FALSE; } break; case PS_REST: if( g_bHaveHostInit ) { SetGamePalette(); g_nProgramState = PS_ACTIVE; } break; } return TRUE; } //----------------------------------------------------------------------------- // Name: ProcessSoundFlags() // Desc: //----------------------------------------------------------------------------- VOID ProcessSoundFlags( SHIP* pShip ) { // Get object positions for panning sounds RECT rc; GetClientRect( g_hwndMain, &rc ); FLOAT fShipXPos = (FLOAT)( ( 2 * pShip->dPosX ) / rc.right ) - 1; FLOAT fShipYpos = (FLOAT)( (-2 * pShip->dPosY ) / rc.bottom ) - 1; FLOAT fBulletXPos = (FLOAT)( ( 2 * pShip->dPosX ) / rc.right ) - 1; FLOAT fBulletYpos = (FLOAT)( (-2 * pShip->dPosY ) / rc.bottom ) - 1; // Set sound state based on user input and ship state if( pShip->bDeath ) // nothing but the boom { DSUtil_StopSound( g_pBulletFiringSound ); DSUtil_StopSound( g_pShipBounceSound ); DSUtil_StopSound( g_pShipStopSound ); DSUtil_StopSound( g_pShipStartSound ); DSUtil_StopSound( g_pShipEngineSound ); DSUtil_PlaySound( g_pShipExplodeSound, 0 ); pShip->bEngineRunning = FALSE; pShip->bMoving = FALSE; pShip->dwKeys = 0; // No input for a dead ship pShip->bDeath = FALSE; // Turn off sound flag. } if( pShip->dwKeys & KEY_STOP ) { if( pShip->bMoving ) { DSUtil_StopSound( g_pShipEngineSound ); DSUtil_PlaySound( g_pShipStopSound, 0 ); pShip->bEngineRunning = FALSE; pShip->bMoving = FALSE; } } if( pShip->dwKeys & KEY_ENGINEOFF ) { DSUtil_StopSound( g_pShipEngineSound ); pShip->bEngineRunning = FALSE; } if( pShip->dwKeys & (KEY_UP | KEY_DOWN) ) { if( !pShip->bEngineRunning ) // Turn on engine { DSUtil_PlaySound( g_pShipEngineSound, DSBPLAY_LOOPING ); if( !pShip->bMoving ) //"fire-up-engine" sound { DSUtil_PlayPannedSound( g_pShipStartSound, fShipXPos ); pShip->bMoving = TRUE; } pShip->bEngineRunning = TRUE; } } if( pShip->pDSBVoice ) { pShip->pDSBVoice->SetPosition( D3DVAL( pShip->dPosX - 320 ) * D3DVAL(6.0f/640.f), -D3DVAL( pShip->dPosY - 240 ) * D3DVAL(6.0f/640.f), D3DVAL(0), DS3D_DEFERRED ); pShip->pDSBVoice->SetVelocity( D3DVAL( pShip->dVelX * 10 ), //exagerater vel -D3DVAL( pShip->dVelY * 10 ), D3DVAL(0), DS3D_DEFERRED ); } if( pShip->dwKeys & KEY_FIRE ) { if( !pShip->bFiring ) { DSUtil_PlayPannedSound( g_pBulletFiringSound, fShipXPos ); pShip->bFiring = TRUE; } } if( pShip->bBlockHit ) { DSUtil_PlayPannedSound( g_pBlockExplodeSound, fBulletXPos ); pShip->bBlockHit = FALSE; } if( pShip->bBounced ) { DSUtil_PlayPannedSound( g_pShipBounceSound, fShipXPos ); pShip->bBounced = FALSE; } g_pDSListener->CommitDeferredSettings(); pShip->dwKeys = 0; } //----------------------------------------------------------------------------- // Name: RenderPlayerCB() // Desc: Renders a ship in its current state. Also checks if we hit the ship // and informs the ship that it has been destroyed. //----------------------------------------------------------------------------- BOOL WINAPI RenderPlayerCB( DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, VOID* lpContext ) { SHIP ship; SHIP ourShip; DWORD dwSize = sizeof(ship); BOOL bHit = FALSE; HRESULT hr; DWORD dwTickCount; // Get ship data hr = DPUtil_GetPlayerLocalData( dpId, &ship, &dwSize ); if( FAILED(hr) ) return FALSE; // No player data yet if( 0 == dwSize ) return TRUE; // Ignore current ship ? if( ship.bIgnore ) { // If this ship was being ignored, update ignore time-out // A time-out is used here to ensure that this ship doesn't get ignored // forever on our screen in case the destroy message was dropped. dwTickCount = timeGetTime(); ship.iCountDown -= dwTickCount - ship.dwLastTick; ship.dwLastTick = dwTickCount; if( ship.iCountDown < 0 ) { ship.bIgnore = FALSE; } // Save ship data hr = DPUtil_SetPlayerLocalData( dpId, &ship, sizeof(ship) ); if( FAILED(hr) ) return FALSE; // We are ignoring this ship, so just bail return TRUE; } // Bail if ship is disabled if( !ship.bEnable) return TRUE; // Update ship's position UpdatePosition( dpId, &ship ); // Get our player data to compare with others dwSize = sizeof(ship); hr = DPUtil_GetPlayerLocalData( g_LocalPlayerDPID, &ourShip, &dwSize ); if( FAILED(hr) ) return FALSE; // Check if our bullet hit the current ship if( (dpId != g_LocalPlayerDPID) && ourShip.bBulletEnable && ship.bEnable ) { if( (ourShip.dBulletPosX > ship.dPosX) && (ourShip.dBulletPosX < (ship.dPosX + 32.0) ) && (ourShip.dBulletPosY > ship.dPosY) && (ourShip.dBulletPosY < (ship.dPosY + 32.0) ) ) { // Hasta-la-vista baby DestroyShip( &ship ); // We nailed it bHit = TRUE; // Turn off ship locally ship.bEnable = FALSE; // Temporarily ignore ship until we get a response ship.bIgnore = TRUE; // Set its ignore time-out ship.iCountDown = HIDE_TIMEOUT; // Time-stamp ship.dwLastTick = timeGetTime(); // Turn our bullet off ship.bBulletEnable = FALSE; // Update our score ourShip.dwScore += 1000; // Save our score hr = DPUtil_SetPlayerLocalData( g_LocalPlayerDPID, &ourShip, sizeof(ourShip) ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_SPLD); return FALSE; } } } // Render the ship if( ship.bBulletEnable ) DrawBullet(&ship); if( ship.bEnable ) DrawShip(&ship); ProcessSoundFlags( &ship ); // Save ship data hr = DPUtil_SetPlayerLocalData( dpId, &ship, sizeof(ship) ); if( FAILED(hr) ) return FALSE; // Inform the player if( bHit ) { g_ShipHitMsg.Id = dpId; SendGameMessage( (GENERICMSG*)&g_ShipHitMsg, dpId ); } return TRUE; } //----------------------------------------------------------------------------- // Name: DrawScreen() // Desc: Renders the current frame //----------------------------------------------------------------------------- BOOL DrawScreen() { HRESULT hr; // Clear screen EraseScreen(); // Render players hr = DPUtil_EnumPlayers( NULL, RenderPlayerCB, NULL, 0 ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_EP); return FALSE; } // Render field for( int y=0; y<30; y++ ) { for( int x=0; x<40; x++ ) { BYTE mask = 1 << (x & 0x7); BYTE col = x >> 3; if( g_Blocks.bits[y][col] & mask ) DrawBlock( x, y ); } } // Render score if (!DrawScore()) return FALSE; // Render fragments DrawFragments(); // Render frame rate if( g_bShowFrameCount ) DisplayFrameRate(); // Show FlipScreen(); return TRUE; } //----------------------------------------------------------------------------- // Name: DisplayFrameRate() // Desc: Renders current frame rate //----------------------------------------------------------------------------- VOID DisplayFrameRate() { static DWORD dwFrames = 0; g_dwFrameCount++; DWORD dwTime = timeGetTime() - g_dwFrameTime; if( dwTime > 1000 ) { dwFrames = (g_dwFrameCount*1000)/dwTime; g_dwFrameTime = timeGetTime(); g_dwFrameCount = 0; } if( dwFrames == 0 ) return; if (dwFrames != g_dwFramesLast) g_dwFramesLast = dwFrames; char strFPS[256]; sprintf( strFPS, "%d", dwFrames ); BltNumber( strFPS, 295, 10); } //----------------------------------------------------------------------------- // Name: DrawScore() // Desc: Renders our current score //----------------------------------------------------------------------------- BOOL DrawScore() { SHIP ship; DWORD dwSize; CHAR strScore[11]; int rem; HRESULT hr; dwSize = sizeof(ship); hr = DPUtil_GetPlayerLocalData( g_LocalPlayerDPID, &ship, &dwSize ); if( FAILED(hr) ) return FALSE; // Calculate dwScore string strScore[0] = (BYTE)ship.dwScore/100000 + '0'; rem = ship.dwScore % 100000; strScore[1] = rem/10000 + '0'; rem = ship.dwScore % 10000; strScore[2] = rem/1000 + '0'; rem = ship.dwScore % 1000; strScore[3] = rem/100 + '0'; rem = ship.dwScore % 100; strScore[4] = rem/10 + '0'; rem = ship.dwScore % 10; strScore[5] = rem + '0'; strScore[6] = '\0'; // Blt score BltNumber( strScore, 8, 8 ); // Save ship data hr = DPUtil_SetPlayerLocalData( g_LocalPlayerDPID, &ship, sizeof(ship) ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_SPLD); return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // Name: DrawBlock() // Desc: Renders a block //----------------------------------------------------------------------------- VOID DrawBlock( int x, int y ) { RECT src; src.top = 0; src.left = 224; src.right = src.left + 16; src.bottom = src.top + 16; BltObject( x << 4, y << 4, g_pddsNumbers, &src, DDBLTFAST_SRCCOLORKEY ); } //----------------------------------------------------------------------------- // Name: DrawShip() // Desc: Renders a ship //----------------------------------------------------------------------------- VOID DrawShip( SHIP* pShip ) { RECT src; src.top = 32 * (pShip->cFrame / 10 ); src.left = 32 * (pShip->cFrame % 10 ); src.right = src.left + 32; src.bottom = src.top + 32; BltObject( (int)pShip->dPosX, (int)pShip->dPosY, g_pddsShip[pShip->byType], &src, DDBLTFAST_SRCCOLORKEY ); } //----------------------------------------------------------------------------- // Name: DrawBullet() // Desc: Renders a bullet //----------------------------------------------------------------------------- VOID DrawBullet( SHIP* pShip ) { RECT src; src.top = BULLET_Y; src.left = BULLET_X + (pShip->byType)*4; src.right = src.left + 3; src.bottom = src.top + 3; BltObject( (int)pShip->dBulletPosX, (int)pShip->dBulletPosY, g_pddsNumbers, &src, DDBLTFAST_SRCCOLORKEY ); } //----------------------------------------------------------------------------- // Name: DrawFragments() // Desc: Renders the fragments //----------------------------------------------------------------------------- VOID DrawFragments() { for( int i=0; i<64; i++) { if( g_Frags[i].valid ) { BltObject( (int)g_Frags[i].dPosX, (int)g_Frags[i].dPosY, g_Frags[i].pdds, &g_Frags[i].src, DDBLTFAST_SRCCOLORKEY ); } } } //----------------------------------------------------------------------------- // Name: ReceiveGameMessages() // Desc: Checks if there are any messages for us and receives them //----------------------------------------------------------------------------- HRESULT ReceiveMessages() { DPID idFrom, idTo; LPVOID pvMsgBuffer; DWORD dwMsgBufferSize; HRESULT hr; // read all messages in queue dwMsgBufferSize = g_dwReceiveBufferSize; pvMsgBuffer = g_pvReceiveBuffer; while( TRUE ) { // See what's out there idFrom = 0; idTo = 0; hr = DPUtil_Receive( &idFrom, &idTo, DPRECEIVE_ALL, pvMsgBuffer, &dwMsgBufferSize ); if( hr == DPERR_BUFFERTOOSMALL ) { if( pvMsgBuffer == NULL ) { pvMsgBuffer = GlobalAllocPtr( GHND, dwMsgBufferSize ); if( pvMsgBuffer == NULL ) return DPERR_NOMEMORY; g_pvReceiveBuffer = pvMsgBuffer; g_dwReceiveBufferSize = dwMsgBufferSize; } else if( dwMsgBufferSize > g_dwReceiveBufferSize ) { pvMsgBuffer = GlobalReAllocPtr( pvMsgBuffer, dwMsgBufferSize, 0 ); if( pvMsgBuffer == NULL ) return DPERR_NOMEMORY; g_pvReceiveBuffer = pvMsgBuffer; g_dwReceiveBufferSize = dwMsgBufferSize; } } else if( SUCCEEDED(hr) && ( (dwMsgBufferSize >= sizeof(GENERICMSG)) || (dwMsgBufferSize >= sizeof(DPMSG_GENERIC) ) ) ) { if( idFrom == DPID_SYSMSG ) { DoSystemMessage( (DPMSG_GENERIC*)pvMsgBuffer, dwMsgBufferSize, idFrom, idTo ); } else { DoApplicationMessage( (GENERICMSG*)pvMsgBuffer, dwMsgBufferSize, idFrom, idTo ); } } else break; } return hr; } //----------------------------------------------------------------------------- // Name: DoSystemMessage() // Desc: Evaluates system messages and performs appropriate actions //----------------------------------------------------------------------------- VOID DoSystemMessage( DPMSG_GENERIC* pMsg, DWORD dwMsgSize, DPID idFrom, DPID idTo ) { switch( pMsg->dwType ) { case DPSYS_CREATEPLAYERORGROUP: { DPMSG_CREATEPLAYERORGROUP* pAddMsg = (DPMSG_CREATEPLAYERORGROUP*)pMsg; int iLeastAmt = -1; int iLeastType = -1; int i; if( g_bHostPlayer) { g_HostMsg.Blocks = g_Blocks; //Copy the used ship types array into the message to be sent for( i = 0; i < NUM_SHIP_TYPES; i++ ) g_HostMsg.usedShipTypes[i] = g_ShipTypesUsed[i]; SendGameMessage( (GENERICMSG*)&g_HostMsg, pAddMsg->dpId ); } // Loop through the used ship types array and find the one that's been used the least for( i = 0; i < NUM_SHIP_TYPES; i++ ) { if( (iLeastAmt < 0) || (g_ShipTypesUsed[i] < iLeastAmt) ) { iLeastAmt = g_ShipTypesUsed[i]; iLeastType = i; } } g_ShipTypesUsed[iLeastType]++; // update our copy of the used ship types break; } case DPSYS_DESTROYPLAYERORGROUP: { SHIP* pShip; DPMSG_DESTROYPLAYERORGROUP* pDestroyMsg = (DPMSG_DESTROYPLAYERORGROUP*)pMsg; if( (sizeof(SHIP) != pDestroyMsg->dwLocalDataSize) || (NULL == pDestroyMsg->lpLocalData)) break; pShip = (SHIP*)pDestroyMsg->lpLocalData; g_ShipTypesUsed[pShip->byType]--; // decrement the leaving players' ship type usage counter ReleasePlayerLocalSoundData(pShip); break; } case DPSYS_HOST: { g_bHostPlayer = TRUE; UpdateTitle(); break; } case DPSYS_SESSIONLOST: // inform user that session was lost TRACE(_T("Session lost\n")); if( !g_bSessionLost ) { g_bSessionLost = TRUE; ShowError(IDS_DPLAY_ERROR_SL); } break; case DPSYS_SENDCOMPLETE: // Async send status { DPMSG_SENDCOMPLETE* pComplete = (DPMSG_SENDCOMPLETE*)pMsg; if( FAILED(pComplete->hr) ) { TCHAR* strErr; switch( pComplete->hr ) { case DPERR_CANCELLED: strErr = _T("Cancelled"); break; case DPERR_ABORTED: strErr = _T("Aborted"); break; case DPERR_TIMEOUT: strErr = _T("Timed out"); break; case DPERR_GENERIC: strErr = _T("Generic"); break; default: strErr = _T("Unknown"); } TRACE(_T("Async msg %u to %u transmission failed. err = %s %#08X\n"), pComplete->dwMsgID, pComplete->idTo, strErr, pComplete->hr ); } break; } } } //----------------------------------------------------------------------------- // Name: DoApplicationMessage() // Desc: Evaluates an application message and performs appropriate actions //----------------------------------------------------------------------------- VOID DoApplicationMessage( GENERICMSG* pMsg, DWORD dwMsgSize, DPID idFrom, DPID idTo ) { HRESULT hr; switch( pMsg->byType ) { case MSG_HOST: { HOSTMSG* pHostMsg = (HOSTMSG*)pMsg; SHIP ship; DWORD dwSize = sizeof (ship); int leastUsed = -1; if( !g_bHostPlayer ) { // receive the field layout g_Blocks = pHostMsg->Blocks; // The Host keeps the official record of ship colors in use. // Sync up our data and recompute our color hr = DPUtil_GetPlayerLocalData( g_LocalPlayerDPID, &ship, &dwSize ); // Insure that host is not a Beta version of Duel that does not // send the additional ShipTypesUsed. if( SUCCEEDED(hr) && dwMsgSize >= sizeof(HOSTMSG) ) { // Loop through the used ship types array we were given, // copying it and finding the least used type for ourself. for( int i = 0; i < NUM_SHIP_TYPES; i++ ) { g_ShipTypesUsed[i] = pHostMsg->usedShipTypes[i]; if( (leastUsed < 0) || (g_ShipTypesUsed[i] < leastUsed) ) { leastUsed = g_ShipTypesUsed[i]; ship.byType = (char) i; } } g_ShipTypesUsed[ship.byType]++; // increment it because we used one hr = DPUtil_SetPlayerLocalData( g_LocalPlayerDPID, &ship, dwSize ); } // Have host initializtion at this point g_bHaveHostInit = TRUE; // Start updating screen g_bIsActive = TRUE; } break; } case MSG_BLOCKHIT: { BLOCKHITMSG* pBlockHitMsg = (BLOCKHITMSG*)pMsg; g_Blocks.bits[pBlockHitMsg->byRow][pBlockHitMsg->byCol] &= ~pBlockHitMsg->byMask; break; } case MSG_ADDBLOCK: { ADDBLOCKMSG* pAddBlockMsg = (ADDBLOCKMSG*)pMsg; setBlock( pAddBlockMsg->byX, pAddBlockMsg->byY ); break; } case MSG_SHIPHIT: { SHIPHITMSG* pShipHitMsg = (SHIPHITMSG*)pMsg; SHIP ship; DWORD dwSize; dwSize = sizeof(SHIP); // Get player local data hr = DPUtil_GetPlayerLocalData( pShipHitMsg->Id, &ship, &dwSize ); if( FAILED(hr) ) return; // No player data yet if( 0 == dwSize ) return; if( !ship.bIgnore ) { // Explode the ship on our screen DestroyShip( &ship ); // Turn it off ship.bEnable = FALSE; ship.bBulletEnable = FALSE; // If it is us if( pShipHitMsg->Id == g_LocalPlayerDPID ) { // Set our hide time-out ship.iCountDown = HIDE_TIMEOUT; ship.dwLastTick = timeGetTime(); // Let the world know that we are dead g_ShipHitMsg.Id = g_LocalPlayerDPID; SendGameMessage( (GENERICMSG*)&g_ShipHitMsg, DPID_ALLPLAYERS ); } } // Ppdate player local data DPUtil_SetPlayerLocalData( pShipHitMsg->Id, &ship, sizeof(ship) ); break; } case MSG_CONTROL: { CONTROLMSG* pControlMsg = (CONTROLMSG*)pMsg; SHIP ship; DWORD dwSize; dwSize = sizeof(SHIP); // Get player local data hr = DPUtil_GetPlayerLocalData( idFrom, &ship, &dwSize ); if( FAILED(hr) ) return; // No player data yet if( 0 == dwSize ) return; // Update its State UpdateState( &ship, (DWORD)pControlMsg->byState ); // Save it back DPUtil_SetPlayerLocalData( idFrom, &ship, dwSize ); break; } case MSG_SYNC: { SYNCMSG* pSyncMsg = (SYNCMSG*)pMsg; SHIP ship; DWORD dwSize; dwSize = sizeof(SHIP); // Get player local data hr = DPUtil_GetPlayerLocalData( idFrom, &ship, &dwSize ); if( FAILED(hr) ) return; // We are seeing this player for the first time // so do the initialization if( 0 == dwSize ) { ZeroMemory( &ship, sizeof(ship) ); ship.byType = pSyncMsg->byShipType; ship.dPosX = pSyncMsg->dPosX; ship.dPosY = pSyncMsg->dPosY; ship.cFrame = pSyncMsg->cFrame; ship.dwLastTick = timeGetTime(); ship.bEnable = TRUE; ship.dpidID = idFrom; // initialize sound buffers InitPlayerLocalSoundData(&ship); } if( ship.bEnable ) { // Head towards rendezvous location (accelerate/decelerate as necessary) ship.dVelX = (pSyncMsg->dPosX - ship.dPosX)/1000; ship.dVelY = (pSyncMsg->dPosY - ship.dPosY)/1000; ship.cFrame = pSyncMsg->cFrame; } else if( !ship.bIgnore ) { // Ship is alive, but we just don't know it. // So, display it at the rendezvous location. ship.dPosX = pSyncMsg->dPosX; ship.dPosY = pSyncMsg->dPosY; ship.cFrame = pSyncMsg->cFrame; ship.dwLastTick = timeGetTime(); ship.bEnable = TRUE; ship.dpidID = idFrom; } // Save it back DPUtil_SetPlayerLocalData( idFrom, &ship, sizeof(ship) ); break; } default: { TRACE( TEXT("Unknown message type %d\n"), pMsg->byType ); break; } } } //----------------------------------------------------------------------------- // Name: SendGameMessage() // Desc: Sends a message to specified player(s) //----------------------------------------------------------------------------- VOID SendGameMessage( GENERICMSG* pMsg, DPID idTo ) { int nBytes; DWORD dwFlags = 0; // No sends when we are not in the session if( g_bSessionLost ) return; switch( pMsg->byType ) { case MSG_HOST: nBytes = sizeof( HOSTMSG ); dwFlags = DPSEND_GUARANTEED; break; case MSG_BLOCKHIT: nBytes = sizeof( BLOCKHITMSG ); break; case MSG_SHIPHIT: nBytes = sizeof( SHIPHITMSG ); break; case MSG_ADDBLOCK: nBytes = sizeof( ADDBLOCKMSG ); break; case MSG_CONTROL: nBytes = sizeof( CONTROLMSG ); break; case MSG_SYNC: nBytes = sizeof( SYNCMSG ); break; default: return; } if( g_bAsync ) dwFlags |= DPSEND_ASYNC; if( g_bReliable ) dwFlags |= DPSEND_GUARANTEED; // Send the message to the relevant player(s) DPUtil_Send( g_LocalPlayerDPID, idTo, dwFlags, (VOID*)pMsg, nBytes ); } //----------------------------------------------------------------------------- // Name: CleanupComm() // Desc: Cleans up communication stuff //----------------------------------------------------------------------------- VOID CleanupComm() { HRESULT hr; // Free up all the local sound buffers ReleaseLocalData(); // Free the receive buffer if( g_pvReceiveBuffer ) { GlobalFreePtr(g_pvReceiveBuffer); g_pvReceiveBuffer = NULL; } // Delete our player if( g_LocalPlayerDPID ) { hr = DPUtil_DestroyPlayer( g_LocalPlayerDPID ); if( FAILED(hr) ) { ShowError(IDS_DPLAY_ERROR_DP); } g_LocalPlayerDPID = 0; } // Cleanup DPlay objects hr = DPUtil_FreeDirectPlay(); hr = DPUtil_Release(); } //----------------------------------------------------------------------------- // Name: InitializeGameSounds() // Desc: //----------------------------------------------------------------------------- HRESULT InitializeGameSounds() { if( FAILED( DSUtil_InitDirectSound( g_hwndMain ) ) ) return E_FAIL; g_pShipEngineSound = DSUtil_CreateSound( TEXT("SENGINE"), 1 ); g_pBulletFiringSound = DSUtil_CreateSound( TEXT("BFIRE"), NUM_SHIP_TYPES ); g_pShipExplodeSound = DSUtil_CreateSound( TEXT("SBOOM"), NUM_SHIP_TYPES ); g_pShipStartSound = DSUtil_CreateSound( TEXT("SSTART"), NUM_SHIP_TYPES ); g_pShipStopSound = DSUtil_CreateSound( TEXT("SSTOP"), NUM_SHIP_TYPES ); g_pShipBounceSound = DSUtil_CreateSound( TEXT("SBOUNCE"), NUM_SHIP_TYPES ); g_pBlockExplodeSound = DSUtil_CreateSound( TEXT("LBOOM"), NUM_SHIP_TYPES ); return S_OK; } //----------------------------------------------------------------------------- // Name: CleanupGameSounds() // Desc: //----------------------------------------------------------------------------- VOID CleanupGameSounds() { DSUtil_DestroySound( g_pBulletFiringSound ); DSUtil_DestroySound( g_pShipExplodeSound ); DSUtil_DestroySound( g_pShipEngineSound ); DSUtil_DestroySound( g_pShipStartSound ); DSUtil_DestroySound( g_pShipStopSound ); DSUtil_DestroySound( g_pShipBounceSound ); DSUtil_DestroySound( g_pBlockExplodeSound ); g_pBulletFiringSound = NULL; g_pShipExplodeSound = NULL; g_pShipEngineSound = NULL; g_pShipStartSound = NULL; g_pShipStopSound = NULL; g_pShipBounceSound = NULL; g_pBlockExplodeSound = NULL; DSUtil_FreeDirectSound(); }