//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose: A moving vehicle that is used as a battering ram
//
// $NoKeywords: $
//=============================================================================

#include "cbase.h"
#include "tf_vehicle_tank.h"
#include "engine/IEngineSound.h"
#include "VGuiScreen.h"
#include "ammodef.h"
#include "in_buttons.h"
#include "vehicle_mortar_shared.h"
#include "movevars_shared.h"
#include "mortar_round.h"
#include "explode.h"
#include "tf_gamerules.h"
#include "shake.h"
#include "basetempentity.h"
#include "weapon_grenade_rocket.h"


#define TANK_MINS			Vector(-30, -50, -10)
#define TANK_MAXS			Vector( 30,  50, 55)
#define TANK_MODEL			"models/objects/vehicle_tank.mdl"
#define TANK_START_SOUND	"vehicles/diesel_start1.wav"
#define TANK_RUNNING_SOUND	"vehicles/diesel_loop2.wav"
#define TANK_TURBO_SOUND	"vehicles/diesel_nitroboost.wav"

// N seconds between tank shots.
#define TANK_FIRE_INTERVAL	2

#define TANK_MAX_RANGE	1800


const char *g_pTurretThinkContextName = "TurretThinkContext";

ConVar vehicle_tank_damage( "vehicle_tank_damage","150", FCVAR_NONE, "Tank Rocket damage" );
ConVar vehicle_tank_range( "vehicle_tank_range","1000", FCVAR_NONE, "Tank Rocket range" );
ConVar vehicle_tank_radius( "vehicle_tank_radius","128", FCVAR_NONE, "Tank rocket explosive radius" );


// How fast the turret rotates to where the driver is facing.
#define TURRET_DEGREES_PER_SEC	30


IMPLEMENT_SERVERCLASS_ST( CVehicleTank, DT_VehicleTank )
	SendPropAngle( SENDINFO( m_flTurretYaw ), 11 ),
	SendPropAngle( SENDINFO( m_flTurretPitch ), 11 ),
END_SEND_TABLE()


LINK_ENTITY_TO_CLASS( vehicle_tank, CVehicleTank );
PRECACHE_REGISTER( vehicle_tank );


// CVars
ConVar	vehicle_tank_health( "vehicle_tank_health","800", FCVAR_NONE, "Tank health" );


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CVehicleTank::CVehicleTank()
{
	m_flClientYaw = 0;
	m_flClientPitch = 0;

	m_flTurretYaw = 0;
	m_flTurretPitch = 0;

	m_flNextFireTime = 0;

	UseClientSideAnimation();
}


CVehicleTank::~CVehicleTank()
{
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVehicleTank::Precache()
{
	engine->PrecacheModel( TANK_MODEL );
	enginesound->PrecacheSound( TANK_START_SOUND );
	enginesound->PrecacheSound( TANK_RUNNING_SOUND );
	enginesound->PrecacheSound( TANK_TURBO_SOUND );
	PrecacheVGuiScreen( "screen_vulnerable_point" );


	BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CVehicleTank::Spawn()
{
	SetVehicleSounds( TANK_START_SOUND, TANK_RUNNING_SOUND, NULL, TANK_TURBO_SOUND );
	
	SetModel( TANK_MODEL );
	
	// This size is used for placement only...
	SetSize( TANK_MINS, TANK_MAXS );
	m_takedamage = DAMAGE_YES;
	m_iHealth = vehicle_tank_health.GetInt();

	SetType( OBJ_VEHICLE_TANK );
	SetMaxPassengerCount( 3 );

	// This can go away when the tank is using a real tank model.
	SetActivity( ACT_DEPLOY );

	SetContextThink( TurretThink, gpGlobals->curtime, g_pTurretThinkContextName );

	BaseClass::Spawn();
}

//-----------------------------------------------------------------------------
// Purpose: Gets info about the control panels
//-----------------------------------------------------------------------------
void CVehicleTank::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
	//pPanelName = "screen_vehicle_siege_tower";
	pPanelName = "screen_vulnerable_point";
}

float MoveAngleTowards( float flAngle, float flTowards, float flRate )
{
	// Translate so flTowards is 0, then interpolate towards 0 or 360.
	flAngle -= flTowards; 

	// Move yaw to the client yaw using the shortest path.
	flAngle = anglemod( flAngle );
	if ( flAngle > 180 )
	{
		flAngle = min( 360, flAngle + flRate );
	}
	else
	{
		flAngle = max( 0, flAngle - flRate );
	}

	// Translate back.
	flAngle += flTowards;
	return flAngle;
}


void CVehicleTank::TurretThink()
{
	float flStartYaw = m_flTurretYaw;
	float flStartPitch = m_flTurretPitch;

	m_flTurretYaw = MoveAngleTowards( m_flTurretYaw, m_flClientYaw, gpGlobals->frametime * TURRET_DEGREES_PER_SEC );
	m_flTurretPitch = MoveAngleTowards( m_flTurretPitch, m_flClientPitch, gpGlobals->frametime * TURRET_DEGREES_PER_SEC );

	if ( m_flTurretYaw != flStartYaw || m_flTurretPitch != flStartPitch )
		NetworkStateChanged();

	SetBoneController( 0, m_flTurretYaw );
	SetBoneController( 1, m_flTurretPitch );

	SetContextThink( TurretThink, gpGlobals->curtime, g_pTurretThinkContextName );
}


//-----------------------------------------------------------------------------
// Here's where we deal with weapons
//-----------------------------------------------------------------------------
void CVehicleTank::OnItemPostFrame( CBaseTFPlayer *pDriver )
{
	// I can't do anything if I'm not active
	if ( !ShouldBeActive() )
		return;

	// Make sure we're allowed to fire here
	if ( !IsReadyToDrive() )
		return;

	if ( GetPassengerRole(pDriver) != VEHICLE_DRIVER )
		return;

	if ( pDriver->m_nButtons & IN_ATTACK )
	{
		// Time to fire?
		if ( gpGlobals->curtime >= m_flNextFireTime )
		{
			Fire();
		}
	}
	else if ( pDriver->m_nButtons & (IN_ATTACK2 | IN_SPEED) )
	{
		//StartBoost();
		BaseClass::OnItemPostFrame( pDriver );
	}
}


void CVehicleTank::Fire()
{
	// NOTE: this code reverses the steps taken in C_VehicleTank::ClientThink to 
	// reconstruct the fire direction.

	// Build local angles.
	QAngle angles;
	angles[YAW] = m_flTurretYaw + 90;
	angles[PITCH] = -m_flTurretPitch;
	angles[ROLL] = 0;

	// Convert to a forward vector.
	Vector vForward, vLocalForward;
	AngleVectors( angles, &vLocalForward );

	// Move the vector to world space.
	matrix3x4_t tankToWorld;
	AngleMatrix( GetAbsAngles(), tankToWorld );
	VectorTransform( vLocalForward, tankToWorld, vForward );


	Vector vFireOrigin;
	QAngle dummy;
	GetAttachment( LookupAttachment( "muzzle" ), vFireOrigin, dummy );
	
	// Create the rocket.
	CWeaponGrenadeRocket *pRocket = CWeaponGrenadeRocket::Create( vFireOrigin, vForward, vehicle_tank_range.GetFloat(), this );
	if ( pRocket )
	{
		pRocket->SetRealOwner( GetDriverPlayer() );
		pRocket->SetDamage( vehicle_tank_damage.GetFloat() );
		pRocket->SetDamageRadius( vehicle_tank_radius.GetFloat() );

		// Apply a screen shake.
		UTIL_ScreenShake( vFireOrigin, 12, 60, 1.5, 512, SHAKE_START, true );

		// BOOM!
		CPASAttenuationFilter pasAttenuation( this, "VehicleTank.FireSound" );
		EmitSound( pasAttenuation, entindex(), "VehicleTank.FireSound" );
	}

	m_flNextFireTime = gpGlobals->curtime + TANK_FIRE_INTERVAL;
}


bool CVehicleTank::ClientCommand( CBaseTFPlayer *pPlayer, const char *pCmd, ICommandArguments *pArg )
{
	ResetDeteriorationTime();
	
	if ( FStrEq( pCmd, "TurretAngles" ) )
	{
		m_flClientYaw = atof( pArg->Argv(1) );
		m_flClientPitch = atof( pArg->Argv(2) );
		return true;
	}

	return BaseClass::ClientCommand( pPlayer, pCmd, pArg );
}


bool CVehicleTank::IsPassengerUsingStandardWeapons( int nRole )
{
	return nRole != VEHICLE_DRIVER;
}


