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

📄 gamemanager.cpp

📁 分布式坦克游戏
💻 CPP
📖 第 1 页 / 共 3 页
字号:
        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 + -