/*****************************************************************************
*
* GameManager.cpp
*
* Electrical Engineering Faculty - Software Lab
* Spring semester 1998
*
* Tanks game
*
* Module description: The core of the game - implements the main game loop.
* Holds the list of objects participating in the game.
* In every game iteration, all the objects on the list
* are refreshed, and the game display is updated.
* More details on the algorithms in use are described below.
*
* Authors: Eran Yariv - 28484475
* Moshe Zur - 24070856
*
*
* Date: 23/09/98
*
******************************************************************************/
#include "stdafx.h"
#include <afxmt.h>
#include "tanks.h"
#include "DIB.h"
#include "GameManager.h"
#include "ImageManager.h"
#include "MsgQueue.h"
#include "Bonus.h"
#include "GameBoard.h"
#include "TankObj.h"
#include "Shell.h"
#include "Bullet.h"
#include "Mine.h"
#include "Bomber.h"
#include "Message.h"
#include "GameOver.h"
CGameManager::CGameManager (UINT uFreq) :
m_Timer(TANKS_APP->m_gTimer),
m_ImageManager(TANKS_APP->m_gImageManager),
m_MsgQueue(TANKS_APP->m_gIncomingMsgQueue),
m_CommManager(TANKS_APP->m_gCommManager),
m_dwBonusTimeout (0)
{
VERIFY (SetFrequency (uFreq));
}
BOOL CGameManager::SetFrequency (UINT uFreq)
{
if (uFreq < MIN_RENDER_FREQ || uFreq > MAX_RENDER_FREQ)
return FALSE;
m_uFreq = uFreq;
m_uMilliSleep = UINT (double(1000.0) / double(m_uFreq));
return TRUE;
}
UINT CGameManager::ThreadEntry (LPVOID /*lpParam*/)
{
SetPriority (GAME_MANAGER_THREAD_PRIORITY);
m_MapHWnd = TANKS_APP->m_gHwndMap; // Get handle to map (Windows handle)
ASSERT (NULL != m_MapHWnd);
m_dwChkSumSignal = 0; // Don't send check sum right away
m_iNumTanks = 0; // Initially, no tanks.
m_iLocalTankID = -1; // Initially, no local tank
HDC dc = ::GetDC (m_MapHWnd);
m_BackBuffer.CreateEmpty (MAP_WIDTH, MAP_HEIGHT);// Create back (off-screen) buffer
m_BackBuffer.GetPaletteFromResourceBitmap (IDB_BULLET);
TANKS_APP->m_gDrawDIB.SetPalette (*m_BackBuffer.m_pPalette);
TANKS_APP->m_gDrawDIB.Realize (dc, FALSE);
::ReleaseDC (m_MapHWnd, dc);
MultiRectGameTechnique (); // Do the game main loop
DestroyObjects (); // Kill list of objects
return 0;
}
void CGameManager::DestroyObjects ()
{
m_GameObjsList.KillList();
m_TanksCS.Lock();
for (int i=0; i<MAX_TANKS; i++)
m_pTanks[i] = NULL;
m_TanksCS.Unlock();
m_BonusCS.Lock();
m_pBonus = NULL;
m_BonusCS.Unlock();
m_iNumTanks = 0;
}
#pragma warning (disable : 4701)
/* warning C4701: local variable 'CurObjHImg' may be used without having been initialized
God damn it, trust me on this one - I know what I'm doing here....
*/
/*------------------------------------------------------------------------------
Function: MultiRectGameTechnique
Purpose: Main game loop. Every iteration of the loop, game objects recalc
their status, and the display is refreshed. Every Iteration lasts a
constant period of time, to allow a steady frame rate of the display,
using the sleep system call, to stay low on CPU use.
Input: None.
Output: None.
Remarks: The algorithm in use is explained in details in the Programmers's
manual section in the game's help files.
------------------------------------------------------------------------------*/
void
CGameManager::MultiRectGameTechnique ()
{
#ifdef GATHER_RENDERING_STATS
DWORD dwLoopCount=0;
DWORD dwStartTime = GetTickCount();
int lTotalTimeLeft = 0, // For statistics only !
lMaxTimeLeft = 0, // For statistics only !
lMinTimeLeft = LONG(m_uMilliSleep), // For statistics only !
iMaxObjs = 0; // For statistics only !
#endif // GATHER_RENDERING_STATS
BOOL bImageChanged;
typedef struct {
HIMAGE himg;
CPoint ObjectPos;
} UpdateArrayUnit;
UpdateArrayUnit UpdateArray[MAX_POSSIBLE_OBJECTS];
UINT uArrIndex;
LONG lSleepTime; // Time left to sleep (may be negative if loop is too slow)
m_bRefreshAll = TRUE;
// Initially, empty the message queue to add the board object to the game objects list
EmptyMsgQ(dwStartTime);
// First, create the constant board in the back-buffer
LIST_POS lp = m_GameObjsList.GetHeadPosition();
CGameObject* pBoard = m_GameObjsList.GetNext(lp);
ASSERT (pBoard->GetType() == BOARD);
CDIB *pBoardDIB = m_ImageManager.ExposeDIB (pBoard->GetImage());
// Copy the board directly - no transparency
m_BackBuffer.CopyFrom (pBoardDIB);
while (!m_bEndThread)
{ // Loop while game lasts
// Get sample time
DWORD dwCurTime = m_Timer.GetLocalTime();
DWORD dwLoopStart = dwCurTime;
AttemptToSendGameChecksum ();
if (m_CommManager.IsHost())
TryToAddBonus (dwCurTime);
// Empty object list requests queue
bImageChanged = EmptyMsgQ(dwCurTime);
// Init update rectangle
m_UpdateRegionRect.SetRectEmpty();
// Init updates array
uArrIndex = 0;
//
// First pass: loop the objects
//
// Walk over game objects and update rectangle
// Skip the board:
LIST_POS lp = m_GameObjsList.GetHeadPosition();
CGameObject* pGameObj = m_GameObjsList.GetNext(lp);
ASSERT (pGameObj->GetType() == BOARD);
// Loop the other (transparent non-board) objects:
for ( pGameObj = m_GameObjsList.GetNext(lp);
pGameObj;
pGameObj = m_GameObjsList.GetNext(lp) )
{
StateType CurObjState = pGameObj->CalcState(dwCurTime);
HIMAGE CurObjHImg;
// Get new position from current object
CPoint CurObjPos = pGameObj->GetPos();
// Get update rectangle from current object
CRect CurObjUpdateRect = pGameObj -> GetUpdateRectangle();
// Update region to be union of all rectangles
m_UpdateRegionRect |= CurObjUpdateRect;
if (STATE_DEAD == CurObjState)
{ // Object is no longer with us:
BOOL fIsMine = FALSE;
if (pGameObj->GetType() == MINE)
{
fIsMine = TRUE;
}
else if (pGameObj->GetType() == TA