source-engine/game/server/tf2/tf_obj_resupply.cpp

384 lines
11 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, 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();
PrecacheModel( RESUPPLY_WALL_MODEL );
PrecacheModel( RESUPPLY_WALL_MODEL_ALIEN );
PrecacheModel( RESUPPLY_GROUND_MODEL );
PrecacheModel( RESUPPLY_GROUND_MODEL_HUMAN );
PrecacheVGuiScreen( "screen_obj_resupply" );
PrecacheScriptSound( "ObjectResupply.InsufficientFunds" );
PrecacheScriptSound( "BaseCombatCharacter.AmmoPickup" );
}
//-----------------------------------------------------------------------------
// 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;
}