//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose: Medic's resupply beacon
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"

#include "tf_obj_resupply.h"
#include "engine/IEngineSound.h"
#include "tf_player.h"
#include "tf_team.h"
#include "VGuiScreen.h"
#include "world.h"

#define RESUPPLY_HEAL_AMT				100
#define RESUPPLY_AMMO_AMT				0.25f

// Wall mounted version
#define RESUPPLY_WALL_MODEL				"models/objects/obj_resupply.mdl"
#define RESUPPLY_WALL_MODEL_ALIEN		"models/objects/alien_obj_resupply.mdl"
#define RESUPPLY_WALL_MINS				Vector(-10, -10, -40)
#define RESUPPLY_WALL_MAXS				Vector( 10,  10, 40)

// Ground placed version
#define RESUPPLY_GROUND_MODEL			"models/objects/obj_resupply_ground.mdl"
#define RESUPPLY_GROUND_MODEL_HUMAN		"models/objects/human_obj_resupply_ground.mdl"
#define RESUPPLY_GROUND_MINS			Vector(-20, -20, 0)
#define RESUPPLY_GROUND_MAXS			Vector( 20,  20, 55)

IMPLEMENT_SERVERCLASS_ST( CObjectResupply, DT_ObjectResupply )
END_SEND_TABLE()

LINK_ENTITY_TO_CLASS(obj_resupply, CObjectResupply);
PRECACHE_REGISTER(obj_resupply);

ConVar	obj_resupply_health( "obj_resupply_health","100", FCVAR_NONE, "Resupply Station health" );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CObjectResupply::CObjectResupply()
{
	m_iHealth = obj_resupply_health.GetInt();
	UseClientSideAnimation();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CObjectResupply::Spawn()
{
	SetModel( RESUPPLY_WALL_MODEL );
	SetSolid( SOLID_BBOX );

	UTIL_SetSize(this, RESUPPLY_WALL_MINS, RESUPPLY_WALL_MAXS);
	m_takedamage = DAMAGE_YES;

	SetType( OBJ_RESUPPLY );
	m_fObjectFlags |= OF_DONT_PREVENT_BUILD_NEAR_OBJ;

	BaseClass::Spawn();
}


//-----------------------------------------------------------------------------
// Spawn the vgui control screens on the object
//-----------------------------------------------------------------------------
void CObjectResupply::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
	pPanelName = "screen_obj_resupply";
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CObjectResupply::Precache()
{
	BaseClass::Precache();
	engine->PrecacheModel( RESUPPLY_WALL_MODEL );
	engine->PrecacheModel( RESUPPLY_WALL_MODEL_ALIEN );
	engine->PrecacheModel( RESUPPLY_GROUND_MODEL );
	engine->PrecacheModel( RESUPPLY_GROUND_MODEL_HUMAN );
	PrecacheVGuiScreen( "screen_obj_resupply" );
}


//-----------------------------------------------------------------------------
// Resupply Health 
//-----------------------------------------------------------------------------
bool CObjectResupply::ResupplyHealth( CBaseTFPlayer *pPlayer, float flFraction )
{
	// Calculate the amount to heal
	float flAmountToHeal = flFraction * RESUPPLY_HEAL_AMT;
	if (flAmountToHeal > (pPlayer->m_iMaxHealth - pPlayer->m_iHealth))
	{
		flAmountToHeal = (pPlayer->m_iMaxHealth - pPlayer->m_iHealth);
	}

	if ( flAmountToHeal > 0 )
	{
		pPlayer->TakeHealth( flAmountToHeal, 0 );
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Handle commands sent from vgui panels on the client 
//-----------------------------------------------------------------------------
bool CObjectResupply::ClientCommand( CBaseTFPlayer *pPlayer, const char *pCmd, ICommandArguments *pArg )
{
	// NOTE: Must match ResupplyBuyType_t
	static float s_Costs[] =
	{
		RESUPPLY_AMMO_COST,
		RESUPPLY_HEALTH_COST,
		RESUPPLY_GRENADES_COST,
		RESUPPLY_ALL_COST
	};

	COMPILE_TIME_ASSERT( RESUPPLY_BUY_TYPE_COUNT == 4 );

	if ( FStrEq( pCmd, "buy" ) )
	{
		if ( pArg->Argc() < 2 )
			return true;

		// I can't do anything if I'm not active
		if ( !ShouldBeActive() ) 
			return true;

		// Do we have enough resources to activate it?
		if (pPlayer->GetBankResources() <= 0)
		{
			// Play a sound indicating it didn't work...
			CSingleUserRecipientFilter filter( pPlayer );
			EmitSound( filter, pPlayer->entindex(), "ObjectResupply.InsufficientFunds" );
			return true;
		}

		bool bUsedResupply = false;
		ResupplyBuyType_t type = (ResupplyBuyType_t)atoi( pArg->Argv(1) );
		if (type >= RESUPPLY_BUY_TYPE_COUNT)
			return true;

		// Get the potential cost.
		float flCost = s_Costs[type];
//		flCost += pPlayer->ClassCostAdjustment( type );

		float flFraction = pPlayer->GetBankResources() / flCost;
		if (flFraction > 1.0f)
			flFraction = 1.0f;

		switch( type )
		{
		case RESUPPLY_BUY_HEALTH:
			// Calculate the amount to heal
			if (ResupplyHealth(pPlayer, flFraction))
			{
				bUsedResupply = true;
			}
			break;

		case RESUPPLY_BUY_AMMO:
			// Refill the player's ammo too
			if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_AMMO_FROM_STATION ))
			{
				bUsedResupply = true;
			}
			break;

		case RESUPPLY_BUY_GRENADES:
			// Refill the player's ammo too
			if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_GRENADES_FROM_STATION ))
			{
				bUsedResupply = true;
			}
			break;

		case RESUPPLY_BUY_ALL:
			// Calculate the amount to heal
			if (ResupplyHealth(pPlayer, flFraction))
			{
				bUsedResupply = true;
			}

			// Refill the player's ammo too
			if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_ALL_FROM_STATION ))
			{
				bUsedResupply = true;
			}
			break;
		}

		if (bUsedResupply)
		{
			// Play an ammo pickup just to this player
			CSingleUserRecipientFilter filter( pPlayer );
			pPlayer->EmitSound( filter, pPlayer->entindex(), "BaseCombatCharacter.AmmoPickup" );

			pPlayer->RemoveBankResources( flFraction * flCost );
		}

		return true;
	}

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


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CObjectResupply::DestroyObject( void )
{
	if ( GetTeam() )
	{
		((CTFTeam*)GetTeam())->RemoveResupply( this );
	}
	BaseClass::DestroyObject();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pTeam - 
//-----------------------------------------------------------------------------
void CObjectResupply::ChangeTeam( int iTeamNum )
{
	CTFTeam *pExisting = (CTFTeam*)GetTeam();
	CTFTeam *pTeam = (CTFTeam*)GetGlobalTeam( iTeamNum );

	// Already on this team
	if ( GetTeamNumber() == iTeamNum )
		return;

	if ( pExisting )
	{
		// Remove it from current team ( if it's in one ) and give it to new team
		pExisting->RemoveResupply( this );
	}
		
	// Change to new team
	BaseClass::ChangeTeam( iTeamNum );
	
	// Add this object to the team's list
	if (pTeam)
	{
		pTeam->AddResupply( this );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Resupply always wants to use the wall mount for attachment points
//-----------------------------------------------------------------------------
void CObjectResupply::SetupAttachedVersion( void )
{
	BaseClass::SetupAttachedVersion();

	if ( GetTeamNumber() == TEAM_ALIENS )
	{
		SetModel( RESUPPLY_WALL_MODEL_ALIEN );
	}
	else
	{
		SetModel( RESUPPLY_WALL_MODEL );
	}

	UTIL_SetSize(this, RESUPPLY_WALL_MINS, RESUPPLY_WALL_MAXS);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CObjectResupply::CalculatePlacement( CBaseTFPlayer *pPlayer )
{
	trace_t tr;
	Vector vecAiming;
	// Get an aim vector. Don't use GetAimVector() because we don't want autoaiming.
	Vector vecSrc = pPlayer->Weapon_ShootPosition( );
	pPlayer->EyeVectors( &vecAiming );
	Vector vecTarget;
	VectorMA( vecSrc, 90, vecAiming, vecTarget );
	m_vecBuildOrigin = vecTarget;

	// Angle it towards me
	Vector vecForward = pPlayer->WorldSpaceCenter() - m_vecBuildOrigin;
	SetLocalAngles( QAngle( 0, UTIL_VecToYaw( vecForward ), 0 ) );

	// Is there something to attach to? 
	// Use my bounding box, not the build box, so I fit to the wall
	UTIL_TraceLine( vecSrc, vecTarget, MASK_SOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr);
	//UTIL_TraceHull( vecSrc, vecTarget, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, pPlayer, TFCOLLISION_GROUP_OBJECT, &tr );
	m_vecBuildOrigin = tr.endpos;
	bool bTryToPlaceGroundVersion = false;
	if ( tr.allsolid || (tr.fraction == 1.0) )
	{
		bTryToPlaceGroundVersion = true;
	}
	else 
	{
		// Make sure we're planting on the world
		CBaseEntity *pEntity = tr.m_pEnt;
		if ( pEntity != GetWorldEntity() )
		{
			bTryToPlaceGroundVersion = true;
		}
	}

	// Make sure the wall we've touched is vertical
	if ( !bTryToPlaceGroundVersion && fabs(tr.plane.normal.z) > 0.3 )
	{
		bTryToPlaceGroundVersion = true;
	}

	// Aborting?
	if ( bTryToPlaceGroundVersion )
	{
		// We couldn't find a wall, so try and place a ground version instead
		if ( GetTeamNumber() == TEAM_HUMANS )
		{
			SetModel( RESUPPLY_GROUND_MODEL_HUMAN );
		}
		else
		{
			SetModel( RESUPPLY_GROUND_MODEL );
		}
		UTIL_SetSize(this, RESUPPLY_GROUND_MINS, RESUPPLY_GROUND_MAXS);
		m_vecBuildMins = WorldAlignMins() - Vector( 4,4,0 );
		m_vecBuildMaxs = WorldAlignMaxs() + Vector( 4,4,0 );
		return BaseClass::CalculatePlacement( pPlayer );
	}

	SetupAttachedVersion();
	m_vecBuildMins = WorldAlignMins() - Vector( 4,4,0 );
	m_vecBuildMaxs = WorldAlignMaxs() + Vector( 4,4,0 );

	// Set the angles
	vecForward = tr.plane.normal;
	SetLocalAngles( QAngle( 0, UTIL_VecToYaw( vecForward ), 0 ) );

	// Trace back from the corners
	Vector vecMins, vecMaxs, vecModelMins, vecModelMaxs;
	const model_t *pModel = GetModel();
	modelinfo->GetModelBounds( pModel, vecModelMins, vecModelMaxs );

	// Check the four build points
	Vector vecPointCheck = (vecForward * 32);
	Vector vecUp = Vector(0,0,1);
	Vector vecRight;
	CrossProduct( vecUp, vecForward, vecRight );
	float flWidth = fabs(vecModelMaxs.y - vecModelMins.y) * 0.5;
	float flHeight = fabs(vecModelMaxs.z - vecModelMins.z) * 0.5;

	bool bResult = true;
	if ( bResult )
	{
		bResult = CheckBuildPoint( m_vecBuildOrigin + (vecRight * flWidth) + (vecUp * flHeight), vecPointCheck );
	}
	if ( bResult )
	{
		bResult = CheckBuildPoint( m_vecBuildOrigin + (vecRight * flWidth) - (vecUp * flHeight), vecPointCheck );
	}
	if ( bResult )
	{
		bResult = CheckBuildPoint( m_vecBuildOrigin - (vecRight * flWidth) + (vecUp * flHeight), vecPointCheck );
	}
	if ( bResult )
	{
		bResult = CheckBuildPoint( m_vecBuildOrigin - (vecRight * flWidth) - (vecUp * flHeight), vecPointCheck );
	}

	AttemptToFindPower();

	return bResult;
}
