📄 tankobj.cpp
字号:
}
void
CTankObj::LaunchBomber()
{
ASSERT (m_bLocal);
ASSERT (m_bBomberAvail);
ASSERT (!m_bBomberInSetup);
CBomber *pBomber = new CBomber (this);
// spawn mine and send message to game mananger:
CMessage::MessageData Params;
Params.pGameObj = pBomber;
VERIFY (m_MsgQueue.Enqueue(CMessage::ADD_OBJECT, Params));
m_bBomberInSetup = TRUE;
m_bBomberAvail = FALSE;
m_pDlgWnd->SetAerialSupport (FALSE);
}
void
CTankObj::RegainInput (BOOL bBomberLaunched)
{
m_bUseManouverSet = TRUE;
m_bBomberInSetup = FALSE;
if (!bBomberLaunched)
{ // Bomber canceled - re-enable it
m_bBomberAvail = TRUE;
m_pDlgWnd->SetAerialSupport (TRUE);
}
}
void
CTankObj::SendManouverSet ( CManouverSet &ms )
{
if (!m_bLocal)
return; // Don't send manouver set for remote tanks
if (ms.GetAll() == m_PrevManouverSet.GetAll())
// No change => Don't send
return;
CMessage::MessageData Params;
// Create manouver set notification message:
Params.ManouverSetParam.TankID = BYTE(m_uTankID);
Params.ManouverSetParam.ManouverSet = ms.GetAll();
Params.ManouverSetParam.Direction = BYTE(m_uDirectionIndex);
// Send it
TANKS_APP->m_gOutgoingMsgQueue.Enqueue (CMessage::MANOUVER_SET, Params);
// Update last manouver set sent over the network
m_PrevManouverSet = ms;
}
void
CTankObj::EatBonus (BonusType t, DWORD dwEatTime)
{
switch (t)
{
case BONUS_SHELLS:
m_uShells = min (MAX_STATUS_VALUE, m_uShells + TANK_BONUS_SHELLS);
if (m_bLocal)
m_pDlgWnd->SetShellsCount(m_uShells);
break;
case BONUS_BULLETS:
m_uBullets = min (MAX_STATUS_VALUE, m_uBullets + TANK_BONUS_BULLETS);
if (m_bLocal)
m_pDlgWnd->SetBulletsCount(m_uBullets);
break;
case BONUS_MINES:
m_uMines = min (MAX_STATUS_VALUE, TANK_BONUS_MINES + m_uMines);
if (m_bLocal)
m_pDlgWnd->SetMinesCount(m_uMines);
break;
case BONUS_BOMBER:
if (m_bLocal)
m_pDlgWnd->SetAerialSupport (TRUE);
m_bBomberAvail = TRUE;
break;
case BONUS_FIRE_RATE:
if (!m_dwFireRateBonusLastTick) { // We don't already own the bonus
m_dwBulletFireDelay = RAPID_BULLET_FIRE_DELAY;
m_dwShellFireDelay = RAPID_SHELL_FIRE_DELAY;
}
m_dwFireRateBonusLastTick = dwEatTime + FIRE_RATE_BONUS_DURATION;
if (m_bLocal)
m_pDlgWnd->SetFastFireRate (TRUE);
break;
case BONUS_SHIELD:
m_uShieldLevel = min (100, m_uShieldLevel + TANK_BONUS_SHIELD);
if (m_bLocal)
m_pDlgWnd->SetShieldLevel (m_uShieldLevel);
break;
default:
ASSERT (FALSE); // Unsupported bonus type
break;
}
// Play sound
TANKS_APP->m_gSoundManager.Play(CSoundManager::PICK_BONUS);
}
WORD
CTankObj::PackBits (WORD wVal, WORD wNumBits)
{
// If the value can be fully represented by wNumBits bits, return it:
if (wVal < (1 << wNumBits))
return wVal;
// First, find the position of the most significant bit :
int iPos=15;
WORD wValBackup = wVal;
while (!(wVal & 0x8000))
{
wVal = WORD(wVal << 1);
iPos--;
}
// Now, simply shift right so that wNumBits MSBits fit into the result
return WORD(wValBackup >> (iPos - wNumBits + 1));
}
void
CTankObj::GetStatus(CMessage::MessageData &MsgData)
{
MsgData.TankStatusParam.bID = BYTE(m_uTankID);
MsgData.TankStatusParam.bShieldLevel = (BYTE)m_uShieldLevel;
MsgData.TankStatusParam.dwShells = m_uShells;
MsgData.TankStatusParam.dwBullets = m_uBullets;
MsgData.TankStatusParam.dwMines = m_uMines;
MsgData.TankStatusParam.bFireRateBonusSecsLeft =
BYTE(m_dwFireRateBonusLastTick - GetTickCount());
MsgData.TankStatusParam.bBomber = BYTE(m_bBomberAvail);
MsgData.TankStatusParam.bFastFire = (0 != m_dwFireRateBonusLastTick);
MsgData.TankStatusParam.bZombie = BYTE(m_bZombie);
}
/*------------------------------------------------------------------------------
Function: SetPos
Purpose: Calculates tanks position, and tries to enforce it, next time we calc
the tanks state. This function is used for REMOTE tanks only, when
receiving updates from the tank's "owner" (a player in this game session
that controls this tank).
Input: dwTime: Local game time in which the tanks position was recorded.
XPos, YPos: Tank's position.
Output: None.
Remarks: Since this information arrived from the player that owns this tank,
the time when the info was recored is at the past in our local game
time. That means we can't just set the tanks position to the new one.
Instead we keep a record of the tanks last positions and time in a
table, and look through this table to find the closest available entry.
If no such entry is found - the update is abandoned, other wise, we
take the position offset between our record and the update we received,
and use it to correct the current tank's position.
------------------------------------------------------------------------------*/
void
CTankObj::SetPos (DWORD dwTime, int XPos, int YPos)
{
// Force a new position and direction (will take effect only on next call to CalcState)
m_bForceNewPos = TRUE;
// First we need to search the history table for a position near the time given:
int ind = -1, min = INT_MAX;
for (int i = 0; i < MAX_POS_TABLE; i++) {
PosEntry posEntry = m_PosTable[i];
if (DWORD(-1) == posEntry.m_dwTime)
break; // we covered all entries in use, and reached unused entry - just leave
int delta = abs(posEntry.m_dwTime - dwTime);
if (min > delta) {
min = delta;
ind = i;
}
}
// Get rid of extreme points:
// - Table is still empty (ind was never updated)
// - The best entry found is still not relevant to the time stamp reported:
if ((-1 == ind) ||
((UINT)abs(m_PosTable[ind].m_dwTime - dwTime) > TANKS_APP->GetMaxRTT()))
{
if (-1 == ind)
NET_SYNC_TRACE(("TankObj::SetPos - empty table\n"))
if (-1 != ind)
NET_SYNC_TRACE(("TankObj::SetPos - time delta %d\n", \
m_PosTable[ind].m_dwTime - dwTime))
m_bForceNewPos = FALSE;
return;
}
// Now we calc the offset from our position to tanks real position
int dx = m_PosTable[ind].m_uXPos - XPos;
int dy = m_PosTable[ind].m_uYPos - YPos;
// And finally we apply the offset to tank's current position:
m_iNewForcedXPos = m_Pos.x - dx;
m_iNewForcedYPos = m_Pos.y - dy;
}
void
CTankObj::SetStatus(CMessage::MessageData &MsgData)
{
// Make sure the message arrived at the right tank:
ASSERT(MsgData.TankStatusParam.bID == m_uTankID);
m_uShieldLevel = MsgData.TankStatusParam.bShieldLevel;
m_uShells = MsgData.TankStatusParam.dwShells;
m_uBullets = MsgData.TankStatusParam.dwBullets;
m_uMines = MsgData.TankStatusParam.dwMines;
SetZombie (MsgData.TankStatusParam.bZombie);
m_dwFireRateBonusLastTick = MsgData.TankStatusParam.bFireRateBonusSecsLeft +
GetTickCount();
m_bBomberAvail = MsgData.TankStatusParam.bBomber;
if (MsgData.TankStatusParam.bFastFire)
{ // Fast fire rate is set on:
m_dwFireRateBonusLastTick = FIRE_RATE_BONUS_DURATION + GetTickCount();
}
if (m_bLocal)
{ // Local tank - refresh display accordingly
m_pDlgWnd->SetFastFireRate (MsgData.TankStatusParam.bFastFire);
m_pDlgWnd->SetAerialSupport (m_bBomberAvail);
m_pDlgWnd->SetShieldLevel (m_uShieldLevel);
m_pDlgWnd->SetShellsCount (m_uShells);
m_pDlgWnd->SetBulletsCount (m_uBullets);
m_pDlgWnd->SetMinesCount (m_uMines);
}
}
void
CTankObj::GetStatusAndPos(CMessage::MessageData &MsgData)
{
MsgData.TankParam.ID = m_uTankID;
MsgData.TankParam.XPos = m_Pos.x;
MsgData.TankParam.YPos = m_Pos.y;
MsgData.TankParam.ShieldLevel = m_uShieldLevel;
MsgData.TankParam.Shells = m_uShells;
MsgData.TankParam.Bullets = m_uBullets;
MsgData.TankParam.Mines = m_uMines;
MsgData.TankParam.FireRateBonusSecsLeft = BYTE(m_dwFireRateBonusLastTick - GetTickCount());
MsgData.TankParam.Bomber = m_bBomberAvail;
MsgData.TankParam.FastFire = (0 != m_dwFireRateBonusLastTick);
MsgData.TankParam.Direction = m_uDirectionIndex;
}
void
CTankObj::Kill ()
{
m_uShieldLevel = 0;
m_pCurrentImage = &m_hExplodedTankImage;
if (m_bLocal)
m_pDlgWnd->SetShieldLevel (m_uShieldLevel);
// Play sound
TANKS_APP->m_gSoundManager.Play(CSoundManager::TANK_EXPLODE);
}
CRect &
CTankObj::GetUpdateRectangle()
{
if (m_pCurrentImage != &m_hExplodedTankImage)
// Moving tank
return CMovingGameObject::GetUpdateRectangle();
else
{ // Exploding tank
CSize size;
CPoint offset;
m_GlobalImageManager.GetImageSize(*m_pCurrentImage, size);
m_GlobalImageManager.GetImageOffset(*m_pCurrentImage, offset);
m_UpdateRect = CRect (m_Pos + offset, size);
}
return m_UpdateRect;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -