www.pudn.com > sxdl.zip > cTank.cpp
#include "cTank.h"
#include "cBoomBastic.h"
#include "cBomb.h"
#include "cRocket.h"
#include "cBonus.h"
#include "bbUtil.h"
#include "cParticle.h"
#define TANK_SIZE 64.0f
cTank::cTank(CBasicSprite *TankSprites[4], int TankX, int TankY, int TankID, int Player)
{
m_TankSprites[0] = TankSprites[0];
m_TankSprites[1] = TankSprites[1];
m_TankSprites[2] = TankSprites[2];
m_TankSprites[3] = TankSprites[3];
m_TankID = TankID;
m_Player = Player;
m_Speed = 150.0f;
m_MovementDirection = Vector3(0.0f, 0.0f, 0.0f);
m_WheelState = 0;
m_BombPower = 1;
NbBombs = 1;
NbRockets = 2;
m_TimeLastRocketHasBeenShot = -1.0f;
m_TankDirection = RIGHT_DIRECTION;
m_NbPickedUpBonus[0] = m_NbPickedUpBonus[1] = m_NbPickedUpBonus[2] = m_NbPickedUpBonus[3] = 0;
Family = TANK_ID;
Scale = Vector3(TANK_SIZE, TANK_SIZE, 1.0f);
Position = g_BoomBastic.Tilemap->MapToWorld(TankX, TankY);
if (m_Player == 1)
{
//Le tank recoit les evenements clavier du premier joueur
g_BoomBastic.Input.Register(this, CInput::LeftRight_1);
g_BoomBastic.Input.Register(this, CInput::UpDown_1);
g_BoomBastic.Input.Register(this, CInput::FirePrimary_1);
g_BoomBastic.Input.Register(this, CInput::FireSecondary_1);
}
else if (m_Player == 2)
{
//Le tank recoit les evenements clavier du second joueur
g_BoomBastic.Input.Register(this, CInput::LeftRight_2);
g_BoomBastic.Input.Register(this, CInput::UpDown_2);
g_BoomBastic.Input.Register(this, CInput::FirePrimary_2);
g_BoomBastic.Input.Register(this, CInput::FireSecondary_2);
}
WillRenderLike(m_TankSprites[0]);
Activate();
}
void cTank::OnUserInput(CInput::VirtualKeys VirtualKey, float ElapsedTime, float Value)
{
if (m_Player == 1)
{
switch (VirtualKey)
{
case CInput::LeftRight_1:
//Gauche
if (Value > 0.0)
MoveTank(LEFT_DIRECTION);
//Droite
else
MoveTank(RIGHT_DIRECTION);
break;
case CInput::UpDown_1:
//Haut...
if (Value > 0.0)
MoveTank(UP_DIRECTION);
//Bas...
else
MoveTank(DOWN_DIRECTION);
break;
case CInput::FirePrimary_1:
DropBomb();
break;
case CInput::FireSecondary_1:
ShootRocket();
break;
default:
break;
}
}
else if (m_Player == 2)
{
switch (VirtualKey)
{
case CInput::LeftRight_2:
//Gauche
if (Value > 0.0)
MoveTank(LEFT_DIRECTION);
//Droite
else
MoveTank(RIGHT_DIRECTION);
break;
case CInput::UpDown_2:
//Haut...
if (Value > 0.0)
MoveTank(UP_DIRECTION);
//Bas...
else
MoveTank(DOWN_DIRECTION);
break;
case CInput::FirePrimary_2:
DropBomb();
break;
case CInput::FireSecondary_2:
ShootRocket();
break;
default:
break;
}
}
}
bool cTank::OnAnimate(float ElapsedTime, float AbsoluteTime)
{
//Si le tank est sur une tile en feu, on le detruit
int TankX, TankY;
g_BoomBastic.Tilemap->ConvertWorldToMap(Position, &amt;TankX, &amt;TankY);
if(g_BoomBastic.Tilemap->LogicalMap(TankX, TankY) == LMAP_FIRE)
HitPoints = -1;
//Sinon, on le deplace
// m_MovementDirection.vNormalize(); // OpenSxDL
SxDLVec3Normalize ( m_MovementDirection ) ;
//Si le vecteur mouvement est nul, on ne fait rien
// float fNorm = m_MovementDirection.fNorm() // OpenSxDL
float fNorm = SxDLVec3Length ( m_MovementDirection ) ;
if ( fNorm == 0.0f)
return true;
Vector3 OldPosition = Position;
//On met a jour la position et on effectue le test de collision
Position += m_MovementDirection * m_Speed * ElapsedTime;
MapCollisionAndResponse();
//On change l'etat des chenilles (l'etat des chenilles change 20 fois par seconde)
m_WheelState = (int)(AbsoluteTime * 20);
//FinalMove correspond au veritable deplacement (en prenant en compte les collisions)
Vector3 FinalMove = Position - OldPosition;
// float fNorm = m_MovementDirection.fNorm() // OpenSxDL
fNorm = SxDLVec3Length ( FinalMove ) ;
//Si FinalMove n'est pas nul...
if ( fNorm != 0.0f)
{
if (fabs(FinalMove.y) == 0.0f)
{
if (FinalMove.x > 0)
{
m_TankDirection = RIGHT_DIRECTION;
Angles = Vector3 ( 0.0f , 0.0f , Pi / 2.0f ) ;
}
else
{
m_TankDirection = LEFT_DIRECTION;
Angles = Vector3 ( 0.0f , 0.0f , Pi * 1.5f ) ;
}
WillRenderLike(m_TankSprites[ 2 + m_WheelState > 2]);
}
else
{
if (FinalMove.y > 0)
{
m_TankDirection = UP_DIRECTION;
Angles = Vector3 ( 0.0f , 0.0f , Pi ) ;
}
else
{
m_TankDirection = DOWN_DIRECTION;
Angles = Vector3 ( 0.0f , 0.0f , 0.0f ) ;
}
WillRenderLike(m_TankSprites[2 + m_WheelState > 2]);
}
}
m_MovementDirection = Vector3(0.0f, 0.0f, 0.0f);
return true;
}
void cTank::MapCollisionAndResponse()
{
//On cherche la tile dans laquelle se trouve le tank
int CurrentMapPositionX, CurrentMapPositionY;
g_BoomBastic.Tilemap->ConvertWorldToMap(Position, &amt;CurrentMapPositionX, &amt;CurrentMapPositionY);
//On effectue le test de collision sur les 8 tiles qui entourent le tank
int i, j;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
//On divise la taille du tank par 2 lors du test de collision pour faciliter les deplacements
//(Il est plus facile de deplacer un petit objet dans des couloirs qu'un gros)
//En contre partie, le tank risque de chevaucher un peu les tiles bloquantes.
//Si il n'y a pas collision avec la tile courante, on passe a la suivante
if (!g_BoomBastic.Tilemap->SpriteCollidesWithTile(Position, TANK_SIZE / 2.0f, CurrentMapPositionX + i, CurrentMapPositionY + j))
continue;
//Si le tank est en collision avec une des tiles qui l'entourent
//(si i == 0 et j == 0 alors la tile correspond a la tile sur laquelle est le tank; on ne traite pas ce cas)
if (i != 0 || j != 0)
{
Vector3 TileCenter = g_BoomBastic.Tilemap->MapToWorld(CurrentMapPositionX + i, CurrentMapPositionY + j);
Vector3 SpriteToTile = TileCenter - Position;
if (fabs(SpriteToTile.x) > fabs(SpriteToTile.y))
{
if (SpriteToTile.x > 0)
Position.x = g_BoomBastic.Tilemap->MapToWorld(CurrentMapPositionX, CurrentMapPositionY).x + 16;
else
Position.x = g_BoomBastic.Tilemap->MapToWorld(CurrentMapPositionX, CurrentMapPositionY).x - 16;
}
else
{
if (SpriteToTile.y > 0)
Position.y = g_BoomBastic.Tilemap->MapToWorld(CurrentMapPositionX, CurrentMapPositionY).y + 16;
else
Position.y = g_BoomBastic.Tilemap->MapToWorld(CurrentMapPositionX, CurrentMapPositionY).y - 16;
}
}
}
}
//On verifie que le tank n'est pas sorti de la map
float HalfSize = TANK_SIZE / 2.0f;
if (Position.x - HalfSize < g_BoomBastic.Tilemap->GetTopLeftCorner().x)
Position.x = g_BoomBastic.Tilemap->GetTopLeftCorner().x + HalfSize;
else if (Position.x + HalfSize > g_BoomBastic.Tilemap->GetBottomRightCorner().x)
Position.x = g_BoomBastic.Tilemap->GetBottomRightCorner().x - HalfSize;
if (Position.y + HalfSize > g_BoomBastic.Tilemap->GetTopLeftCorner().y)
Position.y = g_BoomBastic.Tilemap->GetTopLeftCorner().y - HalfSize;
else if (Position.y - HalfSize < g_BoomBastic.Tilemap->GetBottomRightCorner().y)
Position.y = g_BoomBastic.Tilemap->GetBottomRightCorner().y + HalfSize;
}
void cTank::OnCollide(CEntity *CollidingEntity)
{
//Si le tank est en collision avec un missile
if (CollidingEntity->Family == ROCKET_ID)
{
cRocket *CollidingRocket = (cRocket *)CollidingEntity;
if (CollidingRocket->OwnerID != m_TankID)
CollidingRocket->Explode();
}
//Si le tank est en collision avec un bonus
else if (CollidingEntity->Family == BONUS_ID)
{
cBonus *CollidingBonus = (cBonus *)CollidingEntity;
switch (CollidingBonus->BonusType)
{
case EXPLO_BONUS:
m_BombPower++;
break;
case BOMB_BONUS:
NbBombs++;
break;
case SPEED_BONUS:
m_Speed += 50.0f;
break;
case ROCKET_BONUS:
NbRockets += 3;
break;
default:
break;
}
//Le tank a ramsse un nouveau bonus de type CollidingBonus->BonusType
m_NbPickedUpBonus[CollidingBonus->BonusType]++;
//On enleve le bonus de la carte
CollidingBonus->PickUp();
}
}
void cTank::MoveTank(int Direction)
{
switch(Direction)
{
case LEFT_DIRECTION:
m_MovementDirection.x = -1;
break;
case RIGHT_DIRECTION:
m_MovementDirection.x = 1;
break;
case UP_DIRECTION:
m_MovementDirection.y = 1;
break;
case DOWN_DIRECTION:
m_MovementDirection.y = -1;
break;
default:
break;
}
}
void cTank::DropBomb()
{
//Si le tank peut encore poser des bombes...
if (NbBombs > 0)
{
int CurrentMapPositionX, CurrentMapPositionY;
g_BoomBastic.Tilemap->ConvertWorldToMap(Position, &amt;CurrentMapPositionX, &amt;CurrentMapPositionY);
//Si il n'y a pas deja une bombe a la position actuelle du tank, on en pose une
if (g_BoomBastic.Tilemap->LogicalMap(CurrentMapPositionX, CurrentMapPositionY) != LMAP_BOMB)
new cBomb(CurrentMapPositionX, CurrentMapPositionY, m_BombPower, m_TankID);
}
}
void cTank::ShootRocket()
{
//On peut ne lancer qu'un missile par seconde, et seulement si le tank en a encore
float AbsoluteTime = g_BoomBastic.Timing.GetAbsolute();
if (AbsoluteTime - m_TimeLastRocketHasBeenShot >= 1.0f &amt;&amt; NbRockets > 0)
{
//On joue un son de canon
g_BoomBastic.Sound.Play(CANNON_SOUND);
new cRocket(Position, m_TankDirection, m_BombPower, m_TankID);
m_TimeLastRocketHasBeenShot = AbsoluteTime;
NbRockets--;
}
}
void cTank::OnDying()
{
if (m_Player == 1)
{
//On desenregistre les evenements clavier du premier joueur
g_BoomBastic.Input.Register(NULL, CInput::LeftRight);
g_BoomBastic.Input.Register(NULL, CInput::UpDown);
g_BoomBastic.Input.Register(NULL, CInput::FirePrimary);
g_BoomBastic.Input.Register(NULL, CInput::FireSecondary);
}
else if (m_Player == 2)
{
//On desenregistre les evenements clavier du second joueur
g_BoomBastic.Input.Register(NULL, CInput::LeftRight_2);
g_BoomBastic.Input.Register(NULL, CInput::UpDown_2);
g_BoomBastic.Input.Register(NULL, CInput::FirePrimary_2);
g_BoomBastic.Input.Register(NULL, CInput::FireSecondary_2);
}
//On disperse tous les bonus recoltes
int i, j, k;
int BonusX, BonusY;
for (i = 0; i < 4; i++)
{
for (j = 0; j < m_NbPickedUpBonus[i]; j++)
{
k = 0;
//On determine aleatoirement les coordonnees d'une tile vide ou poser le bonus (k permet d'eviter de tomber dans une boucle infinie)
do
{
BonusX = g_BoomBastic.Tools.randi(0, MAP_WIDTH - 1);
BonusY = g_BoomBastic.Tools.randi(0, MAP_HEIGHT - 1);
k++;
}
while (g_BoomBastic.Tilemap->LogicalMap(BonusX, BonusY) != LMAP_GROUND &amt;&amt; k <= 30);
//et on y place le bonus
if (k <= 30)
new cBonus(BonusX, BonusY, i);
}
}
//Le tank est particulise!
for (i = 0; i < 30; i++)
{
Vector3 ParticleVelocity = g_BoomBastic.Tools.randv() * 60.0f;
ParticleVelocity.z = 0.0f;
new cParticle(Position, ParticleVelocity, 2.0f, 0.0f, 14.0f, g_BoomBastic.Particles[TANK_PARTICLE]);
}
//On informe le jeu que le tank est mort
g_BoomBastic.IsStillAlive[m_TankID] = false;
}