📄 gamemanager.cpp
字号:
iHeight = m_UpdateRegionRect.Height();
m_UpdateRegionRect.top = MAP_HEIGHT - m_UpdateRegionRect.top - iHeight;
TANKS_APP->m_gDrawDIB.DrawDib (
&m_BackBuffer, // Source image
dc, // Device context
m_UpdateRegionRect.left, // Destination leftmost corner
m_UpdateRegionRect.top, // Destination topmost corner
iWidth, // Destination width
iHeight, // Destination height
m_UpdateRegionRect.left, // Source leftmost corner
m_UpdateRegionRect.top, // Source topmost corner
iWidth, // Source width
iHeight, // Source height
0 // Flags - nothing
);
::ReleaseDC (m_MapHWnd, dc);
::ValidateRect (m_MapHWnd, NULL);
}
/*------------------------------------------------------------------------------
Function: EmptyMsgQ
Purpose: Empties the incoming message queue. Messages arrived either internally
from game objects (e.g. a tank may send add bullet message) or
externally from the host.
Input: dwLoopStartTime: Current loop time (local game time).
Output:
Remarks: Returns TRUE if any message was processed.
------------------------------------------------------------------------------*/
BOOL
CGameManager::EmptyMsgQ(DWORD dwLoopStartTime)
{
CMessage Msg;
while (m_MsgQueue.Dequeue(Msg))
{ // While there are still messages to process:
switch (Msg.GetType())
{
//
// Internal messages (Game objects to game manager)
//
case CMessage::ADD_OBJECT: // Add a general object
AddObject ( Msg.m_UnionData.pGameObj );
break;
case CMessage::ADD_GAME_OVER: // Add game over message
AddObject (new CGameOver);
break;
case CMessage::ADD_SHELL: // Add a shell
AddShell (Msg);
break;
case CMessage::ADD_BULLET: // Add a bullet
AddBullet (Msg);
break;
case CMessage::ADD_MINE: // Add a mine
AddMine (Msg);
break;
//
// Incoming messages (From server, through the comm manager to the game manager)
//
case CMessage::ADD_BONUS:
AddBonus (Msg, dwLoopStartTime);
break;
case CMessage::ADD_TANK:
AddTank (Msg);
break;
case CMessage::REMOVE_TANK:
RemoveTank (Msg);
break;
case CMessage::ADD_BOARD:
AddBoard (Msg);
break;
case CMessage::ADD_BOMBER:
AddBomber (Msg, dwLoopStartTime);
break;
case CMessage::SET_TANK_STATUS:
SetTankStatus (Msg);
break;
case CMessage::SET_TANK_POS:
SetTankPos (Msg);
break;
case CMessage::SET_TANK_ZOMBIE:
SetTankZombie (Msg);
break;
default: // this message isn't for me - bail out:
ASSERT(FALSE);
}
}
return TRUE; // Something always changes
}
/*------------------------------------------------------------------------------
Function: FindVacantRect
Purpose: Returns a postion of a vacant rectangle on the game board.
Input: size: Required size of a vacant rectangle.
pt: Position of vacant rectangle found.
Output: None.
Remarks: This function must succeed (or else it's caught in an endless loop).
------------------------------------------------------------------------------*/
void
CGameManager::FindVacantRect(CSize& size, CPoint &pt)
{
// First check if list is empty - means we are positioning the first tank.
// For that purpose we preserve the (0,0) location on every board.
// Reason: the fist REQUEST_TANK message may arrive before the game manager
// gets the game board.
if (m_GameObjsList.GetObjectsCount() == 0) {
pt.x = 0; pt.y = 0;
return;
}
CRect rect;
do {
rect.left = rand() % (MAP_WIDTH - size.cx + 1);
rect.top = rand() % (MAP_HEIGHT - size.cy + 1);
rect.right = rect.left + size.cx - 1;
rect.bottom = rect.top + size.cy - 1;
} while (!IsRectVacant(rect));
// Got solution
pt.x = rect.left;
pt.y = rect.top;
}
/*------------------------------------------------------------------------------
Function: GetMinDistanceFromTanks
Purpose: Returns the minimal distance from the given point to the tanks in game.
Method is used when placing a new tank on the game board.
Input: pt: The position in inspection.
Output: The minimal squared distance to the tanks in game, or MAX_DWORD if no
tanks is playing.
------------------------------------------------------------------------------*/
DWORD
CGameManager::GetMinDistanceFromTanks (CPoint &pt)
{
if (0 == m_GameObjsList.GetObjectsCount())
return MAX_DWORD; // No objects
DWORD dwMinDist = MAX_DWORD;
m_TanksCS.Lock();
for (int i=0; i<MAX_TANKS; i++)
if (NULL != m_pTanks[i])
{ // Found a tank
CPoint &ptTank = m_pTanks[i]->GetPos();
DWORD dwCurDist = (ptTank.x - pt.x) * (ptTank.x - pt.x) +
(ptTank.y - pt.y) * (ptTank.y - pt.y);
dwMinDist = min (dwMinDist, dwCurDist);
}
m_TanksCS.Unlock();
return dwMinDist;
}
/*------------------------------------------------------------------------------
Function: IsRectVacant
Purpose: Check that the given game board rectangle is clear of game object.
Input: rect: The rectangle in inspection.
Output: Return TRUE if rectangle is vacant.
------------------------------------------------------------------------------*/
BOOL
CGameManager::IsRectVacant(CRect& rect)
{
LIST_POS lp = m_GameObjsList.GetHeadPosition();
// Make sure list is untouched during search:
m_GameObjsList.Freeze();
// First, check if board has that rectangle vacant:
CGameObject* pGameObj = m_GameObjsList.GetNext(lp); // GameBoard
ASSERT(BOARD == pGameObj->GetType());
if (! ((CGameBoard*)pGameObj)->IsRectVacant(rect) ) {
m_GameObjsList.Thaw();
return FALSE;
}
CRect TstRect;
// Now, check with all other objects in LOWER_LEVEL:
for ( pGameObj = m_GameObjsList.GetNext(lp);
pGameObj && HIGHER_LEVEL > pGameObj->GetHeight();
pGameObj = m_GameObjsList.GetNext(lp) ) {
// On the first intersection - stop and exit:
if ( TstRect.IntersectRect(pGameObj->CGameObject::GetUpdateRectangle(), rect) )
{
m_GameObjsList.Thaw();
return FALSE;
}
}
m_GameObjsList.Thaw();
return TRUE;
}
/*------------------------------------------------------------------------------
Function: TryToAddBonus
Purpose: Attempts to add a new bonus object to the game.
Input: dwCurTime: Current game time.
Output: None.
Remarks: A new bonus is added iff the former one has expired. This way we
ensure only one bonus is available at a time.
------------------------------------------------------------------------------*/
void
CGameManager::TryToAddBonus (DWORD dwCurTime)
{
if (dwCurTime < m_dwBonusTimeout)
return; // Not time yet
ASSERT (m_CommManager.IsHost()); // Non-host machines don't create bonuses
// Pick bonus timeout
DWORD ttl = BONUS_MIN_LIFESPAN + BONUS_LIFESPAN_RANGE * (rand () % 5); // sleep between 1~3 min. in 30 sec. jumps.
// Pick bonus type
BonusType bt = BonusType(1 + (rand() % BONUS_SHIELD));
// Pick bonus location
CPoint loc;
FindVacantRect (CSize (BONUS_WIDTH, BONUS_HEIGHT) ,loc);
// Post a new bonus message:
m_CommManager.NotifyNewBonus (bt, ttl, loc);
// Calc time in the future to spawn a new bonus
m_dwBonusTimeout = dwCurTime + ttl + 2000;
}
/*------------------------------------------------------------------------------
Function: AttemptToSendGameChecksum
Purpose: Sends a check sum representing our local player's game status.
The host compares the check sum with it's own game status, and
send updates on mismatches found.
Input: None.
Output: None.
Remarks: The method is called every loop iteration, but execution is completed
only once every CHKSUM_TIME_GAP_BITS ms.
------------------------------------------------------------------------------*/
void
CGameManager::AttemptToSendGameChecksum ()
{ // This function must be called only from within the gamer manager game loop
if ((m_Timer.GetLocalTime () >> CHKSUM_TIME_GAP_BITS) <= m_dwChkSumSignal)
// There's still time
return;
if ((-1 == m_iLocalTankID) && // No local tank. The game is in GameOver mode.
!m_CommManager.IsHost()) // Not a judge
// Game will be over when the user presses any key => send nothing!
return;
if (0 == m_dwChkSumSignal)
{ // First call
m_dwChkSumSignal = m_Timer.GetLocalTime () >> CHKSUM_TIME_GAP_BITS;
return;
}
m_dwChkSumSignal++; // The checksum for this time-slot is starting
// Time to check-sum and send
// Compose checksum message
CMessage::MessageData msg;
// Clear all tanks checks sums
memset (msg.CheckSumParam.TanksChkSums,
0,
sizeof (CheckSumParamTag::TankCheckSumParamTag) * MAX_TANKS);
m_TanksCS.Lock();
// Get local tanks position:
if (m_iLocalTankID >= 0)
{ // Local tank is alive
CPoint pos = m_pTanks[m_iLocalTankID]->GetPos();
msg.CheckSumParam.XPos = WORD(pos.x);
msg.CheckSumParam.YPos = WORD(pos.y);
msg.CheckSumParam.DeadHostReport = FALSE;
}
else
{ // Reporting tank is host and host is dead
msg.CheckSumParam.DeadHostReport = TRUE; // Indicate dead host report
}
// Get other info on all tanks, inc. local:
for (int i=0; i<MAX_TANKS; i++)
if (m_pTanks[i] != NULL)
{ // i'th tank exists
msg.CheckSumParam.TanksChkSums[i].TankExists = TRUE;
msg.CheckSumParam.TanksChkSums[i].FastFire = m_pTanks[i]->GetFastFire();
msg.CheckSumParam.TanksChkSums[i].Bullets = WORD(m_pTanks[i]->GetBulletsCount());
msg.CheckSumParam.TanksChkSums[i].Shells = WORD(m_pTanks[i]->GetShellsCount());
msg.CheckSumParam.TanksChkSums[i].Mines = WORD(m_pTanks[i]->GetMinesCount());
msg.CheckSumParam.TanksChkSums[i].Zombie = m_pTanks[i]->IsZombie();
}
m_TanksCS.Unlock();
msg.CheckSumParam.NumberOfTanks = BYTE(m_iNumTanks);
// Now, the mines:
// Clear all partial sector checksums:
memset (msg.CheckSumParam.MinesSectorsChkSum,
0,
sizeof (BYTE) * (MAX_SECTOR + 1));
// Start scanning the list of objects.
m_MinesCS.Lock();
LIST_POS lp = m_GameObjsList.GetHeadPosition();
CGameObject* pGameObj = m_GameObjsList.GetNext(lp); // GameBoard
ASSERT(BOARD == pGameObj->GetType());
// Continue (after the board) and scan for mines (only lower level):
for ( pGameObj = m_GameObjsList.GetNext(lp);
pGameObj && (HIGHER_LEVEL > pGameObj->GetHeight());
pGameObj = m_GameObjsList.GetNext(lp) )
{
// This is a lower level object
if (pGameObj->GetType() != MINE)
continue;
// This is a mine - nothing can stop us now.
BYTE bSector = pGameObj->GetSector();
ASSERT (bSector <= MAX_SECTOR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -