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, &TankX, &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, &CurrentMapPositionX, &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, &CurrentMapPositionX, &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 && 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 && 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; 
}