⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tankobj.cpp

📁 此程序为分布式坦克游戏
💻 CPP
📖 第 1 页 / 共 3 页
字号:
}

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 + -