📄 tankobj.cpp
字号:
/*****************************************************************************
*
* TankObj.cpp
*
* Electrical Engineering Faculty - Software Lab
* Spring semester 1998
*
* Tanks game
*
* Module description: Implements the characteristics of the tank object.
*
* Authors: Eran Yariv - 28484475
* Moshe Zur - 24070856
*
*
* Date: 23/09/98
*
******************************************************************************/
#include "stdafx.h"
#include <math.h>
#include "tanks.h"
#include "TankObj.h"
#include "GameManager.h"
#include "Bullet.h"
#include "Shell.h"
#include "Mine.h"
#include "Message.h"
#include "Bomber.h"
// Regular delay between shells and bullets:
const UINT BULLET_FIRE_DELAY = UINT(1000.0 / double(BULLET_FIRE_RATE));
const UINT SHELL_FIRE_DELAY = UINT(1000.0 / double(SHELL_FIRE_RATE));
// Extra short delay, when bonus is effective:
const UINT RAPID_BULLET_FIRE_DELAY = UINT(1000.0 / double(BULLET_FIRE_RATE)) / FIRE_RATE_BONUS;
const UINT RAPID_SHELL_FIRE_DELAY = UINT(1000.0 / double(SHELL_FIRE_RATE)) / FIRE_RATE_BONUS;
const DirOffsetType CTankObj::m_SpawnOffsets [24] =
{ // Bullet Shell Mine
{ 0, 20, -6, 17, 37, 16 }, // 0 ( <-- )
{ 1, 25, -4, 22, 37, 8 }, // 15
{ 2, 29, -3, 28, 35, 3 }, // 30
{ 6, 33, 2, 32, 30, -3 }, // 45
{ 9, 36, 5, 36, 26, -5 }, // 60
{ 15, 38, 11, 38, 21, -7 }, // 75
{ 20, 38, 17, 38, 16, -9 }, // 90 ( up )
{ 25, 38, 23, 38, 9, -6 }, // 105
{ 30, 36, 28, 36, 4, -4 }, // 120
{ 32, 32, 32, 32, -2, 1 }, // 135
{ 36, 29, 36, 27, -5, 2 }, // 150
{ 37, 24, 37, 22, -7, 10 }, // 165
{ 37, 18, 37, 16, -9, 15 }, // 180 ( --> )
{ 37, 13, 37, 10, -7, 20 }, // 195
{ 36, 9, 36, 5, -4, 26 }, // 210
{ 32, 5, 32, 1, -2, 30 }, // 225
{ 28, 2, 26, -2, 3, 33 }, // 240
{ 24, 0, 22, -5, 9, 36 }, // 255
{ 17, -1, 15, -5, 15, 38 }, // 270 ( down )
{ 13, -1, 10, -6, 20, 38 }, // 285
{ 8, 2, 5, -2, 26, 36 }, // 300
{ 4, 5, 0, 1, 32, 31 }, // 315
{ 2, 9, -3, 6, 34, 26 }, // 330
{ 0, 14, -5, 11, 37, 21 } // 345
};
const PosEntry UNUSED_POS_ENTRY;
CTankObj::CTankObj ( UINT uID, UINT uXPos, UINT uYPos, UINT uDirIndex, BOOL bLocal, UINT uShieldLevel, UINT uShells,
UINT uBullets, UINT uMines, BYTE bFireRateBonusSecsLeft) :
CMovingGameObject(uXPos, uYPos, TANK_WIDTH, TANK_HEIGHT, uDirIndex, double(TANK_MAX_VELOCITY)),
m_uTankID(uID),
m_uShieldLevel(uShieldLevel),
m_uShells(uShells),
m_uBullets(uBullets),
m_uMines(uMines),
m_pDlgWnd ((CTanksDlg*)(TANKS_APP->m_pMainWnd)),
m_ManouverSet(TANKS_APP->m_gManouverSets[uID & 0x3]),
m_ImageManager(TANKS_APP->m_gImageManager),
m_ObjList(TANKS_APP->m_gGameManager.ExposeObjects()),
m_MsgQueue(TANKS_APP->m_gIncomingMsgQueue),
m_CommManager(TANKS_APP->m_gCommManager),
m_Timer(TANKS_APP->m_gTimer),
m_dwLastBulletFiredTick(0),
m_dwLastShellFiredTick(0),
m_dwLastMineDropTick(0),
m_dwBulletFireDelay(BULLET_FIRE_DELAY),
m_dwShellFireDelay(SHELL_FIRE_DELAY),
m_bLocal (bLocal),
m_dwLastRotationTime (0),
m_bUseManouverSet (TRUE),
m_bBomberInSetup (FALSE),
m_bBomberAvail (FALSE),
m_bForceNewPos (FALSE),
m_bZombie (FALSE),
m_PosTable (UNUSED_POS_ENTRY)
{
ASSERT (uID < MAX_TANKS);
m_hTankImage = m_ImageManager.GetImage ((CImageManager::ImageType)uID);
m_hExplodedTankImage = m_ImageManager.GetImage (CImageManager::IMG_TANK_EXPLODE);
m_hZombieOverlay = m_ImageManager.GetImage (CImageManager::IMG_TANK_ZOMBIE);
// Test code **************:
m_ImageManager.RotateImage (m_hTankImage, m_uDirectionIndex);
// End of test code **************
m_pCurrentImage = &m_hTankImage;
m_dwFireRateBonusLastTick = DWORD(bFireRateBonusSecsLeft) * 1000 + GetTickCount();
if (m_bLocal) {
m_pDlgWnd->SetShieldLevel (m_uShieldLevel);
m_pDlgWnd->SetShellsCount (m_uShells);
m_pDlgWnd->SetBulletsCount (m_uBullets);
m_pDlgWnd->SetMinesCount (m_uMines);
m_pDlgWnd->SetAerialSupport (FALSE);
m_pDlgWnd->SetFastFireRate (m_dwFireRateBonusLastTick != 0);
}
}
CReaction
CTankObj::React(CGameObject *pTo)
{
ASSERT(pTo);
CReaction res;
switch (pTo->GetType())
{
case TANK: // detect collision, and block the other object movement:
case SHELL:
case BULLET:
case BOMB:
if (CollidesWith(pTo))
{
res = CReaction(0, HIT_TANK, BONUS_NONE, m_uTankID);
}
break;
}
return res;
}
#pragma warning (disable : 4701)
/* warning C4701: local variable 'CurObjHImg', 'himgLastImage', 'uLastDir'
may be used without having been initialized
God damn it, trust me on this one - I know what I'm doing here....
*/
/*------------------------------------------------------------------------------
Function: CalcState
Purpose: The main Tank object function. Called every game loop, the function
collects the tank's maneuver, calculates the new position, and gathers
the other game objects reaction to the new position. Upon return the
tanks status (shield, ammo, bonuses) are updated.
Input: The local game time of the current game loop.
Output: Tank's new state.
Remarks: There's a difference between local and remote tanks behavior:
The local tanks are allowed to set their state to dead, they need to
update the game display (ammo etc.) and to dispatch the maneuver that
was in use. Remote tanks don't need to do the above.
------------------------------------------------------------------------------*/
StateType
CTankObj::CalcState (DWORD dwCurTime)
{
m_bImageChanged = FALSE; // Assume no change since last CalcState
if (m_pCurrentImage == &m_hExplodedTankImage)
{ // if state==exploded - advance explosion or terminate
if (m_ImageManager.ImageEnded(*m_pCurrentImage)) // That's it - we are done :-(
{ // Explosion is over
if (m_bLocal) // It's the local tank that dies
{
// Go to game over animation mode
m_pDlgWnd->GameOver ();
}
return STATE_DEAD;
}
return STATE_ALIVE; // Still exploding
}
if (m_bZombie && !m_bForceNewPos)
return STATE_ALIVE; // Zombie tank reacts to nothing !!
// From here on starts the normal tank behaviour:
CManouverSet ms; // Detect manouver set (for network messages)
BOOL bTryToRotate; // Did the user dare to rotate ?
HIMAGE himgLastImage; // Current image, for undo purposes
UINT uLastDir; // Current rotation index, for undo purposes
UINT ManouverSet;
if (m_bUseManouverSet)
{ // Can we use the manouver set to react to the user's whim
//
// Save movement params (for Undo option)
//
SaveMovement ();
//
// handle manouver set:
//
ManouverSet = m_ManouverSet.GetAll();
// Each time we call GetAll on the MS we need to reset its values:
TANKS_APP->m_gKbdManager.RefreshManouverSet();
//
// Calc new direction:
//
bTryToRotate = FALSE;
himgLastImage = m_hTankImage;
uLastDir = m_uDirectionIndex;
//
// In cases of remote tanks, we occasionally get an absolute
// direction from the net.
//
if (! m_bLocal && TANKS_APP->m_guRemoteTanksDirs[m_uTankID] != INVALID_DIRECTION)
{ // We just got an absolute direction from a remote tank
m_uDirectionIndex = TANKS_APP->m_guRemoteTanksDirs[m_uTankID];
TANKS_APP->m_guRemoteTanksDirs[m_uTankID] = INVALID_DIRECTION;
bTryToRotate = TRUE;
}
else
{
// Normal game play - Either a local tank, or a remote w/o absolute direction:
if (ManouverSet & CManouverSet::TURN_RIGHT_MASK)
{ // Rotate right
ms.SetBit (CManouverSet::TURN_RIGHT);
if (dwCurTime - m_dwLastRotationTime >= TANK_ROTATION_DELAY)
{ // enough time has passed sine last rotation
m_uDirectionIndex = (m_uDirectionIndex + 1) % MAX_DIRECTIONS;
bTryToRotate = TRUE;
ASSERT (m_uDirectionIndex < MAX_DIRECTIONS);
}
}
else if (ManouverSet & CManouverSet::TURN_LEFT_MASK)
{ // Rotate left
ms.SetBit (CManouverSet::TURN_LEFT);
if (dwCurTime - m_dwLastRotationTime >= TANK_ROTATION_DELAY)
{ // enough time has passed sine last rotation
m_uDirectionIndex = (m_uDirectionIndex == 0) ?
(MAX_DIRECTIONS - 1) : (m_uDirectionIndex - 1);
bTryToRotate = TRUE;
ASSERT (m_uDirectionIndex < MAX_DIRECTIONS);
}
}
} // end of local tank or remote w/o absolute direction
if (bTryToRotate)
{ // There's an attmept to rotate - Rotate the image
m_ImageManager.RotateImage (m_hTankImage, m_uDirectionIndex);
// Check if we're out of the map now
if (GetMapPosition(m_Pos) != (X_FULL_IN_MAP | Y_FULL_IN_MAP))
{ // Out of map situation - undo rotation
m_uDirectionIndex = uLastDir;
m_hTankImage = himgLastImage;
ms.UnsetBit (CManouverSet::TURN_RIGHT); // Cancel roations
ms.UnsetBit (CManouverSet::TURN_LEFT);
}
else
{ // Rotation o.k. - apply changes
m_dwLastRotationTime = dwCurTime;
m_bImageChanged = TRUE;
}
} // End of rotation attempt
//
// Calc new position:
//
BOOL bTryToMove = FALSE;
if (m_bForceNewPos)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -